Java tutorial
// ======================================================================== // $Id: ServletHttpResponse.java,v 1.65 2006/04/04 22:28:05 gregwilkins Exp $ // Copyright 2000-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package net.lightbody.bmp.proxy.jetty.jetty.servlet; import net.lightbody.bmp.proxy.jetty.http.HttpContext; import net.lightbody.bmp.proxy.jetty.http.HttpFields; import net.lightbody.bmp.proxy.jetty.http.HttpOutputStream; import net.lightbody.bmp.proxy.jetty.http.HttpResponse; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import net.lightbody.bmp.proxy.jetty.util.*; import org.apache.commons.logging.Log; import javax.servlet.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter; import java.util.Locale; /* ------------------------------------------------------------ */ /** Servlet Response Wrapper. * This class wraps a Jetty HTTP response as a 2.2 Servlet * response. * * Note that this wrapper is not synchronized and if a response is to * be operated on by multiple threads, then higher level * synchronizations may be required. * * @version $Id: ServletHttpResponse.java,v 1.65 2006/04/04 22:28:05 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class ServletHttpResponse implements HttpServletResponse { private static Log log = LogFactory.getLog(ServletHttpResponse.class); public static final int DISABLED = -1, NO_OUT = 0, OUTPUTSTREAM_OUT = 1, WRITER_OUT = 2; private static ServletWriter __nullServletWriter; private static ServletOut __nullServletOut; static { try { __nullServletWriter = new ServletWriter(IO.getNullStream()); __nullServletOut = new ServletOut(IO.getNullStream()); } catch (Exception e) { log.fatal(e); System.exit(1); } } /* ------------------------------------------------------------ */ private HttpResponse _httpResponse; private ServletHttpRequest _servletHttpRequest; private int _outputState = NO_OUT; private ServletOut _out = null; private ServletWriter _writer = null; private HttpSession _session = null; private boolean _noSession = false; private Locale _locale = null; private boolean _charEncodingSetInContentType = false; /* ------------------------------------------------------------ */ public ServletHttpResponse(ServletHttpRequest request, HttpResponse response) { _servletHttpRequest = request; _servletHttpRequest.setServletHttpResponse(this); _httpResponse = response; } /* ------------------------------------------------------------ */ void recycle() { _outputState = NO_OUT; _out = null; _writer = null; _session = null; _noSession = false; _locale = null; _charEncodingSetInContentType = false; } /* ------------------------------------------------------------ */ int getOutputState() { return _outputState; } /* ------------------------------------------------------------ */ void setOutputState(int s) throws IOException { if (s < 0) { _outputState = DISABLED; if (_writer != null) _writer.disable(); _writer = null; if (_out != null) _out.disable(); _out = null; } else _outputState = s; } /* ------------------------------------------------------------ */ HttpResponse getHttpResponse() { return _httpResponse; } /* ------------------------------------------------------------ */ void commit() throws IOException { if (_writer != null && _writer.isWritten()) _writer.flush(); else _httpResponse.commit(); } /* ------------------------------------------------------------ */ void complete() throws IOException { _httpResponse.completing(); commit(); setOutputState(DISABLED); } /* ------------------------------------------------------------ */ public boolean isCommitted() { return _httpResponse.isCommitted(); } /* ------------------------------------------------------------ */ boolean isDirty() { return _httpResponse.isDirty(); } /* ------------------------------------------------------------ */ public void setBufferSize(int size) { HttpOutputStream out = (HttpOutputStream) _httpResponse.getOutputStream(); if (out.isWritten() || _writer != null && _writer.isWritten()) throw new IllegalStateException("Output written"); out.setBufferSize(size); } /* ------------------------------------------------------------ */ public int getBufferSize() { return ((HttpOutputStream) _httpResponse.getOutputStream()).getBufferSize(); } /* ------------------------------------------------------------ */ public void flushBuffer() throws IOException { if (((HttpOutputStream) _httpResponse.getOutputStream()).isClosed()) return; if (_writer != null) _writer.flush(); if (_out != null) _out.flush(); if (_writer == null && _out == null) _httpResponse.getOutputStream().flush(); if (!_httpResponse.isCommitted()) _httpResponse.commit(); } /* ------------------------------------------------------------ */ public void resetBuffer() { if (isCommitted()) throw new IllegalStateException("Committed"); ((HttpOutputStream) _httpResponse.getOutputStream()).resetBuffer(); if (_writer != null) _writer.reset(); } /* ------------------------------------------------------------ */ public void reset() { resetBuffer(); _httpResponse.reset(); } /* ------------------------------------------------------------ */ /** * Sets the locale of the response, setting the headers (including the * Content-Type's charset) as appropriate. This method should be called * before a call to {@link #getWriter}. By default, the response locale * is the default locale for the server. * * @see #getLocale * @param locale the Locale of the response */ public void setLocale(Locale locale) { if (locale == null || isCommitted()) return; _locale = locale; setHeader(HttpFields.__ContentLanguage, locale.toString().replace('_', '-')); if (this._outputState == 0) { /* get current MIME type from Content-Type header */ String type = _httpResponse.getField(HttpFields.__ContentType); if (type == null) { // servlet did not set Content-Type yet // so lets assume default one type = "application/octet-stream"; } HttpContext httpContext = _servletHttpRequest.getServletHandler().getHttpContext(); if (httpContext instanceof ServletHttpContext) { String charset = ((ServletHttpContext) httpContext).getLocaleEncoding(locale); if (charset != null && charset.length() > 0) { int semi = type.indexOf(';'); if (semi < 0) type += "; charset=" + charset; else if (!_charEncodingSetInContentType) type = type.substring(0, semi) + "; charset=" + charset; setHeader(HttpFields.__ContentType, type); } } } } /* ------------------------------------------------------------ */ public Locale getLocale() { if (_locale == null) return Locale.getDefault(); return _locale; } /* ------------------------------------------------------------ */ public void addCookie(Cookie cookie) { _httpResponse.addSetCookie(cookie); } /* ------------------------------------------------------------ */ public boolean containsHeader(String name) { return _httpResponse.containsField(name); } /* ------------------------------------------------------------ */ public String encodeURL(String url) { // should not encode if cookies in evidence if (_servletHttpRequest == null || _servletHttpRequest.isRequestedSessionIdFromCookie() && _servletHttpRequest.getServletHandler().isUsingCookies()) return url; // get session; if (_session == null && !_noSession) { _session = _servletHttpRequest.getSession(false); _noSession = (_session == null); } // no session or no url if (_session == null || url == null) return url; // invalid session String id = _session.getId(); if (id == null) return url; // Check host and port are for this server // TODO not implemented // Already encoded int prefix = url.indexOf(SessionManager.__SessionUrlPrefix); if (prefix != -1) { int suffix = url.indexOf("?", prefix); if (suffix < 0) suffix = url.indexOf("#", prefix); if (suffix <= prefix) return url.substring(0, prefix + SessionManager.__SessionUrlPrefix.length()) + id; return url.substring(0, prefix + SessionManager.__SessionUrlPrefix.length()) + id + url.substring(suffix); } // edit the session int suffix = url.indexOf('?'); if (suffix < 0) suffix = url.indexOf('#'); if (suffix < 0) return url + SessionManager.__SessionUrlPrefix + id; return url.substring(0, suffix) + SessionManager.__SessionUrlPrefix + id + url.substring(suffix); } /* ------------------------------------------------------------ */ public String encodeRedirectURL(String url) { return encodeURL(url); } /* ------------------------------------------------------------ */ /** * @deprecated As of version 2.1, use encodeURL(String url) instead */ public String encodeUrl(String url) { return encodeURL(url); } /* ------------------------------------------------------------ */ /** * @deprecated As of version 2.1, use * encodeRedirectURL(String url) instead */ public String encodeRedirectUrl(String url) { return encodeRedirectURL(url); } /* ------------------------------------------------------------ */ public void sendError(int status, String message) throws IOException { // Find error page. String error_page = _servletHttpRequest.getServletHandler().getErrorPage(status, _servletHttpRequest); resetBuffer(); // Handle error page? if (error_page == null) { // handle normally _httpResponse.sendError(status, message); } else { _httpResponse.setStatus(status, message); if (message == null) { message = (String) HttpResponse.__statusMsg.get(TypeUtil.newInteger(status)); if (message == null) message = "" + status; } // handle error page ServletHolder holder = _servletHttpRequest.getServletHolder(); if (holder != null) _servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_SERVLET_NAME, holder.getName()); _servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_REQUEST_URI, _servletHttpRequest.getRequestURI()); _servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_STATUS_CODE, new Integer(status)); _servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_MESSAGE, message); RequestDispatcher dispatcher = _servletHttpRequest.getServletHandler().getServletContext() .getRequestDispatcher(error_page); try { ((Dispatcher) dispatcher).error(_servletHttpRequest, this); } catch (ServletException e) { log.warn(LogSupport.EXCEPTION, e); _httpResponse.sendError(status, message); } } complete(); } /* ------------------------------------------------------------ */ public void sendError(int status) throws IOException { sendError(status, null); } /* ------------------------------------------------------------ */ public void sendRedirect(String url) throws IOException { if (url == null) throw new IllegalArgumentException(); if (!URI.hasScheme(url)) { StringBuffer buf = _servletHttpRequest.getHttpRequest().getRootURL(); if (url.startsWith("/")) buf.append(URI.canonicalPath(url)); else { String path = _servletHttpRequest.getRequestURI(); String parent = (path.endsWith("/")) ? path : URI.parentPath(path); url = URI.canonicalPath(URI.addPaths(parent, url)); if (!url.startsWith("/")) buf.append('/'); buf.append(url); } url = buf.toString(); } resetBuffer(); _httpResponse.setField(HttpFields.__Location, url); _httpResponse.setStatus(HttpResponse.__302_Moved_Temporarily); complete(); } /* ------------------------------------------------------------ */ public void setDateHeader(String name, long value) { try { _httpResponse.setDateField(name, value); } catch (IllegalStateException e) { LogSupport.ignore(log, e); } } /* ------------------------------------------------------------ */ public void setHeader(String name, String value) { try { _httpResponse.setField(name, value); } catch (IllegalStateException e) { LogSupport.ignore(log, e); } } /* ------------------------------------------------------------ */ public void setIntHeader(String name, int value) { try { _httpResponse.setIntField(name, value); } catch (IllegalStateException e) { LogSupport.ignore(log, e); } } /* ------------------------------------------------------------ */ public void addDateHeader(String name, long value) { try { _httpResponse.addDateField(name, value); } catch (IllegalStateException e) { LogSupport.ignore(log, e); } } /* ------------------------------------------------------------ */ public void addHeader(String name, String value) { try { _httpResponse.addField(name, value); } catch (IllegalStateException e) { LogSupport.ignore(log, e); } } /* ------------------------------------------------------------ */ public void addIntHeader(String name, int value) { try { _httpResponse.addIntField(name, value); } catch (IllegalStateException e) { LogSupport.ignore(log, e); } } /* ------------------------------------------------------------ */ public void setStatus(int status) { _httpResponse.setStatus(status); } /* ------------------------------------------------------------ */ /** * @deprecated As of version 2.1 of the Servlet spec. * To set a status code * use <code>setStatus(int)</code>, to send an error with a description * use <code>sendError(int, String)</code>. * * Sets the status code and message for this response. * * @param status the status code * @param message the status message */ public void setStatus(int status, String message) { setStatus(status); _httpResponse.setReason(message); } /* ------------------------------------------------------------ */ public ServletOutputStream getOutputStream() { if (_outputState == DISABLED) return __nullServletOut; if (_outputState != NO_OUT && _outputState != OUTPUTSTREAM_OUT) throw new IllegalStateException(); if (_writer != null) { _writer.flush(); _writer.disable(); _writer = null; } if (_out == null) _out = new ServletOut(_servletHttpRequest.getHttpRequest().getOutputStream()); _outputState = OUTPUTSTREAM_OUT; return _out; } /* ------------------------------------------------------------ */ public PrintWriter getWriter() throws java.io.IOException { if (_outputState == DISABLED) return __nullServletWriter; if (_outputState != NO_OUT && _outputState != WRITER_OUT) throw new IllegalStateException(); // If we are switching modes, flush output to try avoid overlaps. if (_out != null) _out.flush(); /* if there is no writer yet */ if (_writer == null) { /* get encoding from Content-Type header */ String encoding = _httpResponse.getCharacterEncoding(); if (encoding == null) { if (_servletHttpRequest != null) { /* implementation of educated defaults */ String mimeType = _httpResponse.getMimeType(); encoding = _servletHttpRequest.getServletHandler().getHttpContext() .getEncodingByMimeType(mimeType); } if (encoding == null) encoding = StringUtil.__ISO_8859_1; _httpResponse.setCharacterEncoding(encoding, true); } /* construct Writer using correct encoding */ _writer = new ServletWriter(_httpResponse.getOutputStream(), encoding); } _outputState = WRITER_OUT; return _writer; } /* ------------------------------------------------------------ */ public void setContentLength(int len) { // Protect from setting after committed as default handling // of a servlet HEAD request ALWAYS sets content length, even // if the getHandling committed the response! if (!isCommitted()) setIntHeader(HttpFields.__ContentLength, len); } /* ------------------------------------------------------------ */ public String getContentType() { return _httpResponse.getContentType(); } /* ------------------------------------------------------------ */ public void setContentType(String contentType) { if (isCommitted() || contentType == null) return; int semi = contentType.indexOf(';'); if (semi > 0) { int charset0 = contentType.indexOf("charset=", semi); if (charset0 > 0) { if (_outputState == WRITER_OUT) { // need to strip charset= from params int charset1 = contentType.indexOf(' ', charset0); if ((charset0 == semi + 1 && charset1 < 0) || (charset0 == semi + 2 && charset1 < 0 && contentType.charAt(semi + 1) == ' ')) _httpResponse.setContentType(contentType.substring(0, semi)); else if (charset1 < 0) _httpResponse.setContentType(contentType.substring(0, charset0).trim()); else _httpResponse.setContentType( contentType.substring(0, charset0) + contentType.substring(charset1)); } else { _charEncodingSetInContentType = true; _httpResponse.setContentType(contentType); } } else _httpResponse.setContentType(contentType); } else _httpResponse.setContentType(contentType); if (_locale != null) setLocale(_locale); } /* ------------------------------------------------------------ */ public void setCharacterEncoding(String encoding) { if (this._outputState == 0 && !isCommitted()) { _charEncodingSetInContentType = true; _httpResponse.setCharacterEncoding(encoding, true); } } /* ------------------------------------------------------------ */ public String getCharacterEncoding() { String encoding = _httpResponse.getCharacterEncoding(); return (encoding == null) ? StringUtil.__ISO_8859_1 : encoding; } /* ------------------------------------------------------------ */ public String toString() { return _httpResponse.toString(); } /* ------------------------------------------------------------ */ /** Unwrap a ServletResponse. * * @see javax.servlet.ServletResponseWrapper * @see javax.servlet.http.HttpServletResponseWrapper * @param response * @return The core ServletHttpResponse which must be the * underlying response object */ public static ServletHttpResponse unwrap(ServletResponse response) { while (!(response instanceof ServletHttpResponse)) { if (response instanceof ServletResponseWrapper) { ServletResponseWrapper wrapper = (ServletResponseWrapper) response; response = wrapper.getResponse(); } else throw new IllegalArgumentException("Does not wrap ServletHttpResponse"); } return (ServletHttpResponse) response; } }