0001: /*
0002: License $Id: JoServletResponse.java,v 1.20 2005/05/26 16:36:04 hendriks73 Exp $
0003:
0004: Copyright (c) 2001-2005 tagtraum industries.
0005:
0006: LGPL
0007: ====
0008:
0009: jo! is free software; you can redistribute it and/or
0010: modify it under the terms of the GNU Lesser General Public
0011: License as published by the Free Software Foundation; either
0012: version 2.1 of the License, or (at your option) any later version.
0013:
0014: jo! is distributed in the hope that it will be useful,
0015: but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0017: Lesser General Public License for more details.
0018:
0019: You should have received a copy of the GNU Lesser General Public
0020: License along with this library; if not, write to the Free Software
0021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0022:
0023: For LGPL see <http://www.fsf.org/copyleft/lesser.txt>
0024:
0025:
0026: Sun license
0027: ===========
0028:
0029: This release contains software by Sun Microsystems. Therefore
0030: the following conditions have to be met, too. They apply to the
0031: files
0032:
0033: - lib/mail.jar
0034: - lib/activation.jar
0035: - lib/jsse.jar
0036: - lib/jcert.jar
0037: - lib/jaxp.jar
0038: - lib/crimson.jar
0039: - lib/servlet.jar
0040: - lib/jnet.jar
0041: - lib/jaas.jar
0042: - lib/jaasmod.jar
0043:
0044: contained in this release.
0045:
0046: a. Licensee may not modify the Java Platform
0047: Interface (JPI, identified as classes contained within the javax
0048: package or any subpackages of the javax package), by creating additional
0049: classes within the JPI or otherwise causing the addition to or modification
0050: of the classes in the JPI. In the event that Licensee creates any
0051: Java-related API and distribute such API to others for applet or
0052: application development, you must promptly publish broadly, an accurate
0053: specification for such API for free use by all developers of Java-based
0054: software.
0055:
0056: b. Software is confidential copyrighted information of Sun and
0057: title to all copies is retained by Sun and/or its licensors. Licensee
0058: shall not modify, decompile, disassemble, decrypt, extract, or otherwise
0059: reverse engineer Software. Software may not be leased, assigned, or
0060: sublicensed, in whole or in part. Software is not designed or intended
0061: for use in on-line control of aircraft, air traffic, aircraft navigation
0062: or aircraft communications; or in the design, construction, operation or
0063: maintenance of any nuclear facility. Licensee warrants that it will not
0064: use or redistribute the Software for such purposes.
0065:
0066: c. Software is provided "AS IS," without a warranty
0067: of any kind. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES,
0068: INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
0069: PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
0070:
0071: d. This License is effective until terminated. Licensee may
0072: terminate this License at any time by destroying all copies of Software.
0073: This License will terminate immediately without notice from Sun if Licensee
0074: fails to comply with any provision of this License. Upon such termination,
0075: Licensee must destroy all copies of Software.
0076:
0077: e. Software, including technical data, is subject to U.S.
0078: export control laws, including the U.S. Export Administration Act and its
0079: associated regulations, and may be subject to export or import regulations
0080: in other countries. Licensee agrees to comply strictly with all such
0081: regulations and acknowledges that it has the responsibility to obtain
0082: licenses to export, re-export, or import Software. Software may not be
0083: downloaded, or otherwise exported or re-exported (i) into, or to a national
0084: or resident of, Cuba, Iraq, Iran, North Korea, Libya, Sudan, Syria or any
0085: country to which the U.S. has embargoed goods; or (ii) to anyone on the
0086: U.S. Treasury Department's list of Specially Designated Nations or the U.S.
0087: Commerce Department's Table of Denial Orders.
0088:
0089:
0090: Feedback
0091: ========
0092:
0093: We encourage your feedback and suggestions and want to use your feedback to
0094: improve the Software. Send all such feedback to:
0095: <feedback@tagtraum.com>
0096:
0097: For more information on tagtraum industries and jo!
0098: please see <http://www.tagtraum.com/>.
0099:
0100:
0101: */
0102: package com.tagtraum.jo;
0103:
0104: import com.tagtraum.framework.http.*;
0105: import com.tagtraum.framework.log.Log;
0106: import com.tagtraum.framework.util.UnSyncStringBuffer;
0107:
0108: import javax.servlet.RequestDispatcher;
0109: import javax.servlet.ServletException;
0110: import javax.servlet.ServletOutputStream;
0111: import javax.servlet.http.Cookie;
0112: import java.io.*;
0113: import java.net.MalformedURLException;
0114: import java.net.Socket;
0115: import java.net.URL;
0116: import java.util.Enumeration;
0117: import java.util.Locale;
0118: import java.util.ResourceBundle;
0119:
0120: /**
0121: * Represents the response.
0122: *
0123: * @version 1.1beta1 $Id: JoServletResponse.java,v 1.20 2005/05/26 16:36:04 hendriks73 Exp $
0124: * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
0125: * @see I_JoServletResponse
0126: * @see I_JoServletRequest
0127: * @see JoServletRequest
0128: * @see JoServletHandler
0129: */
0130: public class JoServletResponse implements I_JoServletResponse, C_Http,
0131: C_Jo {
0132:
0133: /**
0134: * Source-Version
0135: */
0136: public static String vcid = "$Id: JoServletResponse.java,v 1.20 2005/05/26 16:36:04 hendriks73 Exp $";
0137: private static ResourceBundle localStrings = ResourceBundle
0138: .getBundle("com.tagtraum.jo.localStrings");
0139: private static final int ONE_KB = 1024;
0140: public static final int FOUR_KB = 4096;
0141: public static final int EIGHT_KB = 8192;
0142: private static final String CHUNKED_TRANSFERENCODING = "chunked";
0143: private static final String CLOSE_HTTP_CONNECTION = "close";
0144: private static final String KEEP_ALIVE_HTTP_CONNECTION = "Keep-Alive";
0145: public static final String DEFAULT_CHARENCODING = "ISO-8859-1";
0146:
0147: private HttpHeader header;
0148: private ResponseLine responseLine;
0149: /** Indicates whether headers have been written yet. */
0150: private boolean headersWritten;
0151: /** Inidcates whether the outputstrean has been used internally. */
0152: private boolean gotInternalOutputStream;
0153: private Locale locale;
0154: private StringBuffer headerStringBuffer;
0155: private I_JoHost host;
0156: private I_JoServletContextPeer peer;
0157: private I_JoServletRequest request;
0158: private Socket socket;
0159: private PrintWriter writer;
0160: private JoOutputStream servletOutputStream;
0161: private OutputStream socketOutputStream;
0162: /** Indicates whether {@link #getOutputStream()} has been called. */
0163: private boolean gotOutputStream;
0164: /** Indicates whether {@link #getWriter()} has been called. */
0165: private boolean gotWriter;
0166: private int bufferSize;
0167: private boolean forceClose;
0168:
0169: public JoServletResponse() {
0170: this .responseLine = new ResponseLine();
0171: this .header = new HttpHeader();
0172: this .servletOutputStream = new JoOutputStream();
0173: // initiate the header buffer to 1024bytes
0174: this .headerStringBuffer = new StringBuffer(ONE_KB);
0175: }
0176:
0177: /**
0178: * Initializes this response.
0179: *
0180: * @see JoServletHandler
0181: */
0182: public void init() {
0183: // 'null' or reset instance variables
0184: this .locale = Locale.getDefault(); // see Servlet 2.3, 14.2.18, setLocale()
0185: this .gotOutputStream = false;
0186: this .gotInternalOutputStream = false;
0187: this .gotWriter = false;
0188: this .writer = null;
0189: this .request = null;
0190: this .bufferSize = 0;
0191: this .host = null;
0192: this .peer = null;
0193: this .headersWritten = false;
0194: this .servletOutputStream.resetBytesWritten();
0195: this .responseLine.setStatusCode(SC_OK, "OK");
0196: this .responseLine.setProtocol(C_HTTP_1_1);
0197: this .header.init();
0198: // Header Defaulting
0199: setDefaultHeaders();
0200: }
0201:
0202: /**
0203: * Sets some default headers like Content-Type: text/plain, the Date
0204: * and a default locale.
0205: *
0206: * @see #init
0207: */
0208: private void setDefaultHeaders() {
0209: // Spec, Section 5.2: "Servlet containers must not set a default content type when the
0210: // servlet programmer does not set the type."
0211: // necessary? (rik)
0212: // header.setHeader(C_HTTP_MIME_Version, "1.0");
0213: // don't use 'setDateHeader()' for speed. (rik)
0214: this .header.setHeader(C_HTTP_Date, HttpDate.lazyCurrentDate());
0215: // do we need to set the locale? (rik)
0216: if (forceClose)
0217: header.setHeader(C_HTTP_Connection, CLOSE_HTTP_CONNECTION);
0218: }
0219:
0220: public void setForceClose(boolean forceClose) {
0221: this .forceClose = forceClose;
0222: }
0223:
0224: /**
0225: * Sets the associated request.
0226: *
0227: * @param request associated request
0228: */
0229: public void setRequest(I_JoServletRequest request) {
0230: this .request = request;
0231: }
0232:
0233: /**
0234: * Sets the socket for this response.
0235: *
0236: * @param socket this response's Socket.
0237: */
0238: public void setSocket(Socket socket) throws IOException {
0239: this .socket = socket;
0240: this .socketOutputStream = this .socket.getOutputStream();
0241: setForceClose(false);
0242: }
0243:
0244: /**
0245: * Sets the peer that belongs to this response.
0246: *
0247: * @param peer associated peer
0248: */
0249: public void setServletContextPeer(I_JoServletContextPeer peer) {
0250: this .peer = peer;
0251: this .host = peer.getHost();
0252: setHeader(C_HTTP_Server, host.getServerInfo());
0253: }
0254:
0255: /**
0256: * Sets the buffer size for this response. The buffer is not necessarily set
0257: * to the size you are trying to set, but <i>at least</i> to the size you are
0258: * trying to set. This happens in order to reuse buffers.
0259: *
0260: * @param size size in bytes
0261: * @exception IllegalArgumentException if you try to set a negative size
0262: * @exception IllegalStateException if {@link #isCommitted()} returns true
0263: *
0264: * @see #reset();
0265: * @see #isCommitted();
0266: * @see #getBufferSize();
0267: */
0268: public void setBufferSize(int size) {
0269: if (size < 0)
0270: throw new IllegalArgumentException(localStrings
0271: .getString("buffer_size_less_than_zero")
0272: + size);
0273: if (isCommitted())
0274: throw new IllegalStateException(localStrings
0275: .getString("data_was_written"));
0276: if (size == 0)
0277: this .bufferSize = 0;
0278: else if (size <= FOUR_KB)
0279: this .bufferSize = FOUR_KB;
0280: else if (size <= EIGHT_KB)
0281: this .bufferSize = EIGHT_KB;
0282: else
0283: this .bufferSize = size;
0284: }
0285:
0286: /**
0287: * Returns the actual buffer size.
0288: *
0289: * @return buffer size in bytes
0290: * @see #setBufferSize(int)
0291: */
0292: public int getBufferSize() {
0293: if (gotOutputStream || gotWriter)
0294: return servletOutputStream.getBufferSize();
0295: return bufferSize;
0296: }
0297:
0298: /**
0299: * Indicates whether the the response has been committed yet, i.e.
0300: * whether content has been written to the client yet.
0301: *
0302: * @return true or false
0303: * @see #flushBuffer()
0304: */
0305: public boolean isCommitted() {
0306: return headersWritten;
0307: }
0308:
0309: /**
0310: * Commits this response.
0311: * @see #isCommitted()
0312: */
0313: public void flushBuffer() throws IOException {
0314: if (gotInternalOutputStream)
0315: servletOutputStream.flushBuffer();
0316: }
0317:
0318: /**
0319: * Resets the response, including the headers.
0320: *
0321: * @see #isCommitted()
0322: */
0323: public void reset() {
0324: resetBuffer();
0325: this .header.init();
0326: setDefaultHeaders();
0327: }
0328:
0329: /**
0330: * Resets the response, without resetting any headers.
0331: *
0332: * @see #isCommitted()
0333: */
0334: public void resetBuffer() {
0335: if (gotInternalOutputStream)
0336: servletOutputStream.reset();
0337: }
0338:
0339: /**
0340: * Returns an internal <code>ServletOutputStream</code>.
0341: *
0342: * @return an internal ServletOutputStream
0343: */
0344: public ServletOutputStream getInternalOutputStream()
0345: throws IOException {
0346: if (gotWriter) {
0347: // flush writer to keep things in order.
0348: writer.flush();
0349: }
0350: if (!gotInternalOutputStream) {
0351: setProtocolSpecificHeaders();
0352: if (CHUNKED_TRANSFERENCODING.equals(header
0353: .getHeader(C_HTTP_TransferEncoding))) {
0354: servletOutputStream.init(new ChunkedOutputStream(
0355: socketOutputStream), this );
0356: } else {
0357: servletOutputStream.init(socketOutputStream, this );
0358: }
0359: gotInternalOutputStream = true;
0360: }
0361: return servletOutputStream;
0362: }
0363:
0364: /**
0365: * Returns a <code>ServletOutputStream</code>.
0366: *
0367: * @exception IllegalStateException if we got a writer before
0368: * @return the ServletOutputStream
0369: */
0370: public ServletOutputStream getOutputStream() throws IOException {
0371: if (gotWriter)
0372: throw new IllegalStateException(localStrings
0373: .getString("illegal_getoutputstream_call"));
0374: ServletOutputStream internalOutputStream = getInternalOutputStream();
0375: if (!gotOutputStream) {
0376: servletOutputStream.setBufferSize(bufferSize);
0377: gotOutputStream = true;
0378: }
0379: return internalOutputStream;
0380: }
0381:
0382: /**
0383: * Returns a <code>PrintWriter</code>. Before you call this method you
0384: * chould call {@link #setContentType(String)} as that makes sure
0385: * that you use the correct character encoding.
0386: *
0387: * @exception IllegalStateException if {@link #getOutputStream()} was called before
0388: * @return a PrintWriter
0389: */
0390: public PrintWriter getWriter() throws IOException {
0391: if (gotOutputStream)
0392: throw new IllegalStateException(localStrings
0393: .getString("illegal_getwriter_call"));
0394: if (!gotWriter) {
0395: this .writer = new JoWriter(getInternalOutputStream(),
0396: getCharacterEncoding());
0397: servletOutputStream.setBufferSize(bufferSize);
0398: gotWriter = true;
0399: }
0400: return this .writer;
0401: }
0402:
0403: /**
0404: * Sets some protocol specific headers like TransferEncoding
0405: * and keep alive.
0406: */
0407: private void setProtocolSpecificHeaders() {
0408: String protocol = request.getProtocol();
0409: // HTTP/0.9
0410: if (protocol.equals(C_HTTP_0_9)) {
0411: header.setHeader(C_HTTP_Connection, CLOSE_HTTP_CONNECTION);
0412: // HTTP/1.0
0413: } else if (protocol.equals(C_HTTP_1_0)) {
0414: if (KEEP_ALIVE_HTTP_CONNECTION.equals(request
0415: .getHeader(C_HTTP_Connection))) {
0416: if (header.getHeader(C_HTTP_ContentLength) == null
0417: && responseLine.getStatusCode() != StatusCodes.SC_NOT_MODIFIED
0418: && responseLine.getStatusCode() != StatusCodes.SC_NO_CONTENT
0419: && responseLine.getStatusCode() >= StatusCodes.SC_OK) {
0420: header.setHeader(C_HTTP_Connection,
0421: CLOSE_HTTP_CONNECTION);
0422: }
0423: } else {
0424: header.setHeader(C_HTTP_Connection,
0425: CLOSE_HTTP_CONNECTION);
0426: }
0427: // HTTP/1.1
0428: } else if (protocol.equals(C_HTTP_1_1)) {
0429: if (!CLOSE_HTTP_CONNECTION.equals(request
0430: .getHeader(C_HTTP_Connection))
0431: && header.getHeader(C_HTTP_ContentLength) == null
0432: && responseLine.getStatusCode() != StatusCodes.SC_NOT_MODIFIED
0433: && responseLine.getStatusCode() != StatusCodes.SC_NO_CONTENT
0434: && responseLine.getStatusCode() >= StatusCodes.SC_OK) {
0435: if (header.getHeader(C_HTTP_TransferEncoding) == null) {
0436: header.setHeader(C_HTTP_TransferEncoding,
0437: CHUNKED_TRANSFERENCODING);
0438: header.setHeader(C_HTTP_Connection, null);
0439: } else {
0440: header.setHeader(C_HTTP_Connection,
0441: CLOSE_HTTP_CONNECTION);
0442: }
0443: }
0444: }
0445: }
0446:
0447: /**
0448: * Writes the headers if they have not been written yet.
0449: */
0450: public void writeHeaders() throws IOException {
0451: if (!headersWritten) {
0452: headersWritten = true;
0453: // if HTTP/0.9, don't write headers.
0454: if (!request.getProtocol().equals(C_HTTP_0_9)) {
0455: BufferedOutputStream responseBufferedOut = new BufferedOutputStream(
0456: socketOutputStream);
0457: responseLine.write(responseBufferedOut);
0458: responseBufferedOut.write(HttpHeader.CRLF_BYTEARRAY);
0459: header.write(responseBufferedOut);
0460: responseBufferedOut.write(HttpHeader.CRLF_BYTEARRAY);
0461: responseBufferedOut.flush();
0462: String logName = null;
0463: if (host != null)
0464: logName = host.getName();
0465: if (Log.isLog(Log.FORTYTWO, logName)) {
0466: ByteArrayOutputStream logOut = new ByteArrayOutputStream();
0467: responseLine.write(logOut);
0468: logOut.write(HttpHeader.CRLF_BYTEARRAY);
0469: header.write(logOut);
0470: logOut.write(HttpHeader.CRLF_BYTEARRAY);
0471: headerStringBuffer.insert(0, "Response-Header:\n");
0472: Log.getLog(logName).log(
0473: new String(logOut.toByteArray(), "ASCII"),
0474: Log.FORTYTWO);
0475: }
0476: }
0477: servletOutputStream.resetBytesWritten();
0478: }
0479: }
0480:
0481: /**
0482: * Sets the length of the body.
0483: *
0484: * @param len the length
0485: */
0486: public void setContentLength(int len) {
0487: header.setHeader(C_HTTP_ContentLength, Integer.toString(len));
0488: }
0489:
0490: /**
0491: * Sets the Content-Type.
0492: *
0493: * @param aType the MIME-Typ
0494: */
0495: public void setContentType(String aType) {
0496: header.setHeader(C_HTTP_ContentType, aType);
0497: }
0498:
0499: /**
0500: * Returns the current HTTP status.
0501: *
0502: * @return current status
0503: */
0504: public int getStatus() {
0505: return responseLine.getStatusCode();
0506: }
0507:
0508: /**
0509: * Sets the HTTP statuscode.
0510: *
0511: * @param aCode the statuscode
0512: * @param aMessage a message
0513: * @see #setStatus(int)
0514: * @deprecated
0515: */
0516: public void setStatus(int aCode, String aMessage) {
0517: try {
0518: sendError(aCode, aMessage);
0519: } catch (IOException ioe) {
0520: // ignore
0521: }
0522: }
0523:
0524: /**
0525: * Sets the statuscode.
0526: *
0527: * @param aCode the statuscode
0528: * @see #setStatus(int, String)
0529: */
0530: public void setStatus(int aCode) {
0531: responseLine.setStatusCode(aCode);
0532: }
0533:
0534: /**
0535: * Sends an error message to the client.
0536: *
0537: * @param exception Throwable
0538: */
0539: public void sendError(Exception exception, String servletName)
0540: throws IOException {
0541: if (isCommitted())
0542: throw new IllegalStateException(
0543: localStrings
0544: .getString("response_already_committed_senderror_failed")
0545: + exception.toString());
0546: String savedLocation = header.getHeader(C_HTTP_Location);
0547: String encoding = header.getHeader(C_HTTP_TransferEncoding);
0548: reset();
0549: header.setHeader(C_HTTP_Location, savedLocation);
0550: header.setHeader(C_HTTP_TransferEncoding, encoding);
0551:
0552: // Customized Error Pages
0553: try {
0554: String uri = null;
0555: if (peer != null) {
0556: uri = peer.getErrorPage(exception.getClass().getName());
0557: if (uri == null
0558: && exception instanceof ServletException
0559: && ((ServletException) exception)
0560: .getRootCause() != null) {
0561: uri = peer
0562: .getErrorPage(((ServletException) exception)
0563: .getRootCause().getClass()
0564: .getName());
0565: }
0566: }
0567: if (uri != null) {
0568: // request.setAttribute(C_Request_Attribute_ErrorStatusCode, new Integer(aCode));
0569: request.setAttribute(C_Request_Attribute_ExceptionType,
0570: exception.getClass().getName());
0571: request.setAttribute(C_Request_Attribute_ErrorMessage,
0572: exception.getMessage());
0573: request.setAttribute(C_Request_Attribute_Exception,
0574: exception);
0575: request.setAttribute(
0576: C_Request_Attribute_Error_RequestURI, request
0577: .getRequestURI());
0578: request.setAttribute(
0579: C_Request_Attribute_Error_ServletName,
0580: servletName);
0581: peer.getServletContext().getRequestDispatcher(uri)
0582: .forward(request, this );
0583: return;
0584: } else {
0585: sendError(StatusCodes.SC_INTERNAL_SERVER_ERROR,
0586: exception.toString());
0587: }
0588: } catch (Exception e) {
0589: sendError(StatusCodes.SC_INTERNAL_SERVER_ERROR, e
0590: .toString());
0591: }
0592: }
0593:
0594: /**
0595: * Sends an error message to the client.
0596: *
0597: * @param aCode the Statuscode
0598: * @param aMessage message
0599: */
0600: public void sendError(int aCode, String aMessage)
0601: throws IOException {
0602: if (isCommitted())
0603: throw new IllegalStateException(
0604: localStrings
0605: .getString("response_already_committed_senderror_failed")
0606: + aMessage);
0607: String savedLocation = header.getHeader(C_HTTP_Location);
0608: String encoding = header.getHeader(C_HTTP_TransferEncoding);
0609: reset();
0610: header.setHeader(C_HTTP_Location, savedLocation);
0611: header.setHeader(C_HTTP_TransferEncoding, encoding);
0612:
0613: // Customized Error Pages
0614: try {
0615: String uri = null;
0616: if (peer != null)
0617: uri = peer.getErrorPage(new Integer(aCode));
0618: if (uri != null) {
0619: request.setAttribute(
0620: C_Request_Attribute_ErrorStatusCode,
0621: new Integer(aCode));
0622: // request.setAttribute(C_Request_Attribute_ExceptionType, "");
0623: request.setAttribute(C_Request_Attribute_ErrorMessage,
0624: aMessage);
0625: request.setAttribute(
0626: C_Request_Attribute_Error_RequestURI, request
0627: .getRequestURI());
0628: RequestDispatcher rd = peer.getServletContext()
0629: .getRequestDispatcher(uri);
0630: if (rd == null)
0631: throw new ServletException(localStrings
0632: .getString("errorpage_not_found")
0633: + "" + uri);
0634: setStatus(aCode);
0635: // if not modified, send neither contenttype nor length
0636: if (aCode != SC_NOT_MODIFIED) {
0637: header.setHeader(C_HTTP_ContentType, "text/html");
0638: } else {
0639: header.setHeader(C_HTTP_ContentType, null);
0640: }
0641: rd.forward(request, this );
0642: return;
0643: }
0644: } catch (ServletException se) {
0645: aCode = StatusCodes.SC_INTERNAL_SERVER_ERROR;
0646: aMessage = se.toString() + "<p>"
0647: + localStrings.getString("original_message")
0648: + "<p>" + aMessage;
0649: }
0650:
0651: // Normal Error Page
0652: UnSyncStringBuffer theMessage = new UnSyncStringBuffer();
0653: theMessage.append("<body>");
0654: theMessage.append(aMessage);
0655: theMessage.append("</body>");
0656: byte[] b = theMessage.toString().getBytes();
0657: setStatus(aCode);
0658: // if not modified, send neither contenttype nor length
0659: if (aCode != SC_NOT_MODIFIED) {
0660: header.setHeader(C_HTTP_ContentType, "text/html");
0661: } else {
0662: header.setHeader(C_HTTP_ContentType, null);
0663: }
0664: // make sure that all headers are set correctly.
0665: getInternalOutputStream();
0666: writeHeaders();
0667: // if not modified, don't send body
0668: if (aCode != SC_NOT_MODIFIED)
0669: getInternalOutputStream().write(b);
0670: }
0671:
0672: /**
0673: * Sends an errormessage to the client.
0674: *
0675: * @param code the statuscode
0676: * @see #sendError(int, String)
0677: */
0678: public void sendError(int code) throws IOException {
0679: sendError(code, StatusCodes.get(code));
0680: }
0681:
0682: /**
0683: * Returns how many bytes were sent.
0684: */
0685: public int getSentBodyBytes() {
0686: return servletOutputStream.getBytesWritten();
0687: }
0688:
0689: /**
0690: * Sends a <code>SC_MOVED_TEMPORARILY</code> to the client.
0691: *
0692: * @param aLocation <b>absolut</b> URL
0693: */
0694: public void sendRedirect(String aLocation) throws IOException {
0695: if (isCommitted())
0696: throw new IllegalStateException(
0697: localStrings
0698: .getString("response_already_committed_sendredirect_failed")
0699: + aLocation);
0700: // don't clean headers here, to keep cookies. ???
0701: if (gotOutputStream || gotWriter)
0702: servletOutputStream.reset();
0703: // absolute URLs must contain a colon...
0704: if (aLocation.indexOf(':') == -1) {
0705: try {
0706: aLocation = toAbsoluteURL(aLocation);
0707: } catch (IllegalArgumentException iae) {
0708: throw new IllegalArgumentException(localStrings
0709: .getString("failed_to_resolve_location")
0710: + aLocation);
0711: }
0712: }
0713: setHeader(C_HTTP_Location, aLocation);
0714: setStatus(SC_MOVED_TEMPORARILY);
0715: writeHeaders();
0716: }
0717:
0718: /**
0719: * Sets the locale.
0720: *
0721: * @param locale
0722: * @see #getLocale()
0723: */
0724: public void setLocale(Locale locale) {
0725: if (locale != null) {
0726: this .locale = locale;
0727: String language = this .locale.getLanguage();
0728: if (language.length() > 0)
0729: header.setHeader(C_HTTP_ContentLanguage, language);
0730: String encoding = peer.getEncoding(this .locale);
0731: if (encoding != null)
0732: setCharacterEncoding(encoding);
0733: }
0734: }
0735:
0736: /**
0737: * Returns the locale.
0738: *
0739: * @return locale
0740: * @see #setLocale(Locale)
0741: */
0742: public Locale getLocale() {
0743: return locale;
0744: }
0745:
0746: /**
0747: * Checks whether a certain header is set.
0748: *
0749: * @param headerKey name of the header
0750: * @return true or false
0751: */
0752: public boolean containsHeader(String headerKey) {
0753: return header.getHeader(headerKey) != null;
0754: }
0755:
0756: /**
0757: * Encodes a redirect URL fo URL rewriting
0758: *
0759: * @param aUrl URL
0760: * @return encoded URL
0761: */
0762: public String encodeRedirectURL(String aUrl) {
0763: String absoluteURL = toAbsoluteURL(aUrl);
0764: if (needsEncoding(absoluteURL)) {
0765: absoluteURL = encode(absoluteURL);
0766: }
0767: return absoluteURL;
0768: }
0769:
0770: /**
0771: * Encodes a redirect URL fo URL rewriting
0772: *
0773: * @param aUrl URL
0774: * @return encoded URL
0775: * @deprecated
0776: */
0777: public String encodeRedirectUrl(String aUrl) {
0778: return encodeRedirectURL(aUrl);
0779: }
0780:
0781: /**
0782: * Encodes a URL fo URL rewriting
0783: *
0784: * @param url URL
0785: * @return encoded URL
0786: */
0787: public String encodeURL(String url) {
0788: String absoluteURL = toAbsoluteURL(url);
0789: if (needsEncoding(absoluteURL)) {
0790: url = encode(url);
0791: }
0792: return url;
0793: }
0794:
0795: /**
0796: * Encodes a URL fo URL rewriting
0797: *
0798: * @param aUrl URL
0799: * @return encoded URL
0800: * @deprecated
0801: */
0802: public String encodeUrl(String aUrl) {
0803: return encodeURL(aUrl);
0804: }
0805:
0806: private String encode(String url) {
0807: URI uri = new URI(url);
0808: // overwrite ';'-parameters?
0809: uri.setParameter(C_DefaultSessionIDName + "="
0810: + request.getSession(false).getId());
0811: url = uri.toString();
0812: return url;
0813: }
0814:
0815: private String toAbsoluteURL(String relativeURL) {
0816: if (relativeURL == null)
0817: return relativeURL;
0818: URL url;
0819: try {
0820: url = new URL(relativeURL);
0821: } catch (MalformedURLException mue1) {
0822: String requestURL = request.getRequestURL().toString();
0823: try {
0824: url = new URL(new URL(requestURL), relativeURL);
0825: } catch (MalformedURLException mue2) {
0826: throw new IllegalArgumentException(relativeURL);
0827: }
0828: }
0829: return url.toExternalForm();
0830: }
0831:
0832: private boolean needsEncoding(String uri) {
0833: if (uri.startsWith("#"))
0834: return false;
0835: if (request.getSession(false) == null)
0836: return false;
0837: URL url;
0838: try {
0839: url = new URL(uri);
0840: if (!request.getServerName().equals(url.getHost()))
0841: return false;
0842: } catch (MalformedURLException mue) {
0843: return false;
0844: }
0845: return true;
0846: }
0847:
0848: /**
0849: * Helpermethod that eases the setting of the char encoding.
0850: */
0851: private void setCharacterEncoding(String anEncoding) {
0852: String type = getHeader(C_HTTP_ContentType);
0853: if (type == null)
0854: return;
0855: int i = type.indexOf(';');
0856: if (i != -1)
0857: type = type.substring(0, i);
0858: if (anEncoding != null)
0859: type += "; charset=" + getRealEncoding(anEncoding);
0860: setHeader(C_HTTP_ContentType, type);
0861: }
0862:
0863: private static String getRealEncoding(String anEncoding) {
0864: if (anEncoding.startsWith("8859_") && anEncoding.length() > 5) {
0865: anEncoding = "ISO-8859-" + anEncoding.substring(5);
0866: } else if (anEncoding.startsWith("ISO8859_")
0867: && anEncoding.length() > 5) {
0868: anEncoding = "ISO-8859-" + anEncoding.substring(8);
0869: }
0870: return anEncoding;
0871: }
0872:
0873: /**
0874: * Return the encoding of the current content type.
0875: *
0876: * @return the Charset
0877: */
0878: public String getCharacterEncoding() {
0879: String encoding = header.getHeader(C_HTTP_ContentType);
0880: if (encoding == null)
0881: return DEFAULT_CHARENCODING;
0882: int i = encoding.indexOf('=');
0883: if (i == -1)
0884: return DEFAULT_CHARENCODING;
0885: if (encoding.length() > i + 1)
0886: return encoding.substring(i + 1);
0887: return DEFAULT_CHARENCODING;
0888: }
0889:
0890: /**
0891: * Returns an enumeration of the headernames.
0892: *
0893: * @return enumeration
0894: */
0895: public Enumeration getHeaderNames() {
0896: return header.getHeaderNames();
0897: }
0898:
0899: /**
0900: * Returns a headervalue as string, or nullif it has not been set.
0901: *
0902: * @param aKey header name
0903: * @return header value
0904: */
0905: public String getHeader(String aKey) {
0906: return header.getHeader(aKey);
0907: }
0908:
0909: /**
0910: * Sets a header.
0911: *
0912: * @param aKey key
0913: * @param aValue value
0914: */
0915: public void setHeader(String aKey, String aValue) {
0916: header.setHeader(aKey, aValue);
0917: }
0918:
0919: /**
0920: * Adds a header.
0921: *
0922: * @param aKey key
0923: * @param aValue value
0924: */
0925: public void addHeader(String aKey, String aValue) {
0926: header.addHeader(aKey, aValue);
0927: }
0928:
0929: /**
0930: * Returns a header as <code>int</code>.
0931: *
0932: * @param aName header name
0933: * @return value as <code>int</code>, -1 if the header is not set
0934: */
0935: public int getIntHeader(String aName) {
0936: return header.getIntHeader(aName);
0937: }
0938:
0939: /**
0940: * Returns a header as date (<code>long</code>).
0941: *
0942: * @param aName header name
0943: * @return value as <code>long</code>, -1 if the header is not set
0944: */
0945: public long getDateHeader(String aName) {
0946: return header.getDateHeader(aName);
0947: }
0948:
0949: /**
0950: * Sets an <code>int</code> as header.
0951: *
0952: * @param aName header name
0953: * @param aValue header value
0954: */
0955: public void setIntHeader(String aName, int aValue) {
0956: header.setIntHeader(aName, aValue);
0957: }
0958:
0959: /**
0960: * Adds an <code>int</code> as header.
0961: *
0962: * @param aName header name
0963: * @param aValue header value
0964: */
0965: public void addIntHeader(String aName, int aValue) {
0966: header.addIntHeader(aName, aValue);
0967: }
0968:
0969: /**
0970: * Sets a Date as header.
0971: *
0972: * @param aName header name
0973: * @param aDate date as long
0974: */
0975: public void setDateHeader(String aName, long aDate) {
0976: header.setDateHeader(aName, aDate);
0977: }
0978:
0979: /**
0980: * Adds a Date as header.
0981: *
0982: * @param aName header name
0983: * @param aDate date as long
0984: */
0985: public void addDateHeader(String aName, long aDate) {
0986: header.addDateHeader(aName, aDate);
0987: }
0988:
0989: /**
0990: * Sets a Cooies as header as specified by
0991: * <a href="http://home.netscape.com/newsref/std/cookie_spec.html">
0992: * http://home.netscape.com/newsref/std/cookie_spec.html</a>
0993: *
0994: * @param cookie Cookie
0995: */
0996: public void addCookie(Cookie cookie) {
0997: header.addCookie(cookie);
0998: }
0999:
1000: /**
1001: * Closes the writer or {@link JoOutputStream}.
1002: */
1003: public void close() throws IOException {
1004: try {
1005: getInternalOutputStream();
1006: writeHeaders();
1007: if (writer != null)
1008: writer.flush();
1009: } finally {
1010: servletOutputStream.internalClose();
1011: }
1012: }
1013:
1014: }
|