Annotation of java/classes/org/w3c/jigsaw/servlet/JigsawHttpServletResponse.java, revision 1.39
1.1 abaird 1: // JigsawHttpServletReponse.java
1.39 ! ylafon 2: // $Id: JigsawHttpServletResponse.java,v 1.38 2000/07/25 08:58:08 bmahe Exp $
1.1 abaird 3: // (c) COPYRIGHT MIT and INRIA, 1996.
4: // Please first read the full copyright statement in file COPYRIGHT.html
5:
1.10 bmahe 6: package org.w3c.jigsaw.servlet;
1.1 abaird 7:
1.39 ! ylafon 8: import java.io.ByteArrayOutputStream;
! 9: import java.io.DataOutputStream;
! 10: import java.io.IOException;
! 11: import java.io.OutputStream;
! 12: import java.io.OutputStreamWriter;
! 13: import java.io.PrintWriter;
! 14: import java.io.UnsupportedEncodingException;
! 15:
! 16: import javax.servlet.ServletOutputStream;
! 17:
! 18: import javax.servlet.http.Cookie;
! 19: import javax.servlet.http.HttpServletResponse;
! 20: import javax.servlet.http.HttpSession;
! 21:
! 22: import java.net.MalformedURLException;
! 23: import java.net.URL;
! 24:
1.33 bmahe 25: import java.util.Locale;
1.1 abaird 26:
1.39 ! ylafon 27: import org.w3c.www.mime.MimeType;
! 28: import org.w3c.www.mime.MimeTypeFormatException;
! 29: import org.w3c.www.mime.Utils;
! 30:
! 31: import org.w3c.www.http.BasicValue;
! 32: import org.w3c.www.http.HttpAcceptCharsetList;
! 33: import org.w3c.www.http.HttpAcceptEncodingList;
! 34: import org.w3c.www.http.HttpAcceptLanguageList;
! 35: import org.w3c.www.http.HttpAcceptList;
! 36: import org.w3c.www.http.HttpCookie;
! 37: import org.w3c.www.http.HttpCookieList;
! 38: import org.w3c.www.http.HttpEntityMessage;
! 39: import org.w3c.www.http.HttpEntityTagList;
! 40: import org.w3c.www.http.HttpExt;
! 41: import org.w3c.www.http.HttpExtList;
! 42: import org.w3c.www.http.HttpFactory;
! 43: import org.w3c.www.http.HttpMessage;
! 44: import org.w3c.www.http.HttpParamList;
! 45: import org.w3c.www.http.HttpRangeList;
! 46: import org.w3c.www.http.HttpReplyMessage;
! 47: import org.w3c.www.http.HttpRequestMessage;
! 48: import org.w3c.www.http.HttpSetCookie;
! 49: import org.w3c.www.http.HttpSetCookieList;
! 50: import org.w3c.www.http.HttpString;
! 51: import org.w3c.www.http.HttpTokenList;
! 52: import org.w3c.www.http.HttpWarningList;
! 53:
! 54: import org.w3c.jigsaw.http.Reply;
! 55: import org.w3c.jigsaw.http.Request;
! 56:
1.33 bmahe 57: import org.w3c.www.http.HeaderValue;
1.2 abaird 58:
59: /**
1.30 bmahe 60: * @author Alexandre Rafalovitch <alex@access.com.au>
61: * @author Anselm Baird-Smith <abaird@w3.org>
62: * @author Benoît Mahé (bmahe@w3.org)
63: * @author Roland Mainz (Roland.Mainz@informatik.med.uni-giessen.de)
1.2 abaird 64: */
1.1 abaird 65:
1.29 bmahe 66: public class JigsawHttpServletResponse implements HttpServletResponse {
67:
1.33 bmahe 68: public final static String CHARSET_PARAMETER = "charset";
69:
70: public final static int DEFAULT_BUFFER_SIZE = 8 * 1024; // 8KB buffer
71: public final static int MIN_BUFFER_SIZE = 4 * 1024; // 4KB buffer
72:
1.31 ylafon 73: private final static int STATE_INITIAL = 0;
74: private final static int STATE_HEADERS_DONE = 1;
75: private final static int STATE_ALL_DONE = 2;
1.34 bmahe 76:
1.11 bmahe 77: private final static int STREAM_STATE_INITIAL = 0;
1.31 ylafon 78: private final static int STREAM_WRITER_USED = 1;
79: private final static int OUTPUT_STREAM_USED = 2;
1.11 bmahe 80:
81: private int stream_state = STREAM_STATE_INITIAL;
1.20 bmahe 82:
83: private JigsawServletOutputStream output = null;
84: private PrintWriter writer = null;
1.39 ! ylafon 85:
1.30 bmahe 86: private MimeTypeFormatException setContentTypeException = null;
87:
88: // servlet has set a fixed content length or not,
89: // and cut (see flushStream) any data which are too much here...
90: private final static int CALC_CONTENT_LENGTH = -1;
91: private int fixedContentLength = CALC_CONTENT_LENGTH;
92:
1.33 bmahe 93: // Our Locale
94: protected Locale locale;
95:
1.29 bmahe 96: /**
97: * Our temp stream.
98: */
99: protected ByteArrayOutputStream out = null;
100:
1.14 bmahe 101: protected JigsawHttpServletRequest jrequest = null;
102:
1.33 bmahe 103: protected int buffer_size;
104:
1.14 bmahe 105: protected void setServletRequest(JigsawHttpServletRequest jrequest) {
106: this.jrequest = jrequest;
107: }
108:
1.7 bmahe 109: public static final
1.29 bmahe 110: String INCLUDED = "org.w3c.jigsaw.servlet.included";
1.28 bmahe 111:
1.1 abaird 112: int state = STATE_INITIAL;
113: Reply reply = null;
114: Request request = null;
1.39 ! ylafon 115:
1.18 bmahe 116: /**
117: * Sets the content length for this response.
118: * @param len - the content length
119: */
1.5 abaird 120: public void setContentLength(int i) {
1.30 bmahe 121: fixedContentLength = i;
1.1 abaird 122: reply.setContentLength(i);
123: }
1.18 bmahe 124:
125: /**
126: * Sets the content type for this response. This type may later be
127: * implicitly modified by addition of properties such as the MIME
128: * charset=<value> if the service finds it necessary, and the appropriate
129: * media type property has not been set.
130: * <p>This response property may only be assigned one time. If a writer
131: * is to be used to write a text response, this method must be
132: * called before the method getWriter. If an output stream will be used
133: * to write a response, this method must be called before the
134: * output stream is used to write response data.
135: * @param spec - the content's MIME type
1.27 ylafon 136: * @see JigsawHttpServletResponse#getOutputStream
137: * @see JigsawHttpServletResponse#getWriter
1.18 bmahe 138: */
1.5 abaird 139: public void setContentType(String spec) {
1.1 abaird 140: try {
141: MimeType type= new MimeType(spec);
142: reply.setContentType(type);
1.30 bmahe 143: setContentTypeException = null;
1.1 abaird 144: } catch(MimeTypeFormatException ex) {
1.30 bmahe 145: //store exception
146: setContentTypeException = ex;
1.1 abaird 147: }
148: }
1.5 abaird 149:
1.28 bmahe 150: protected boolean isStreamObtained() {
151: return (stream_state != STREAM_STATE_INITIAL);
152: }
153:
1.29 bmahe 154: protected Reply getReply() {
155: return reply;
156: }
157:
1.18 bmahe 158: /**
159: * Returns an output stream for writing binary response data.
1.24 bmahe 160: * @return A ServletOutputStream
1.18 bmahe 161: * @exception IOException if an I/O exception has occurred
162: * @exception IllegalStateException if getWriter has been called on this
163: * same request.
1.27 ylafon 164: * @see JigsawHttpServletResponse#getWriter
1.18 bmahe 165: */
1.5 abaird 166: public synchronized ServletOutputStream getOutputStream()
167: throws IOException
168: {
1.12 bmahe 169: if (stream_state == STREAM_WRITER_USED)
170: throw new IllegalStateException("Writer used");
171: stream_state = OUTPUT_STREAM_USED;
1.34 bmahe 172: return getJigsawOutputStream(false);
1.11 bmahe 173: }
174:
1.34 bmahe 175: protected ServletOutputStream getJigsawOutputStream(boolean writerUsed)
1.11 bmahe 176: throws IOException
177: {
1.12 bmahe 178: if ( output != null )
179: return output;
1.30 bmahe 180:
181: // any exception during setContentType ?
182: if( setContentTypeException != null ) {
183: // "wrap" the exception from setContentType in an IOException
1.33 bmahe 184: throw new IOException("Illegal Content Type: "+
1.30 bmahe 185: setContentTypeException.toString());
186: }
187:
188: if( request.hasState(INCLUDED) ) {
189: out = new ByteArrayOutputStream();
190: output = new JigsawServletOutputStream(this,
1.33 bmahe 191: new DataOutputStream(out),
1.34 bmahe 192: buffer_size,
193: writerUsed);
1.30 bmahe 194: } else {
1.34 bmahe 195: output = new JigsawServletOutputStream(this,
196: reply,
197: buffer_size,
198: writerUsed);
1.30 bmahe 199: }
1.5 abaird 200: return output;
1.1 abaird 201: }
1.29 bmahe 202:
1.18 bmahe 203: /**
204: * Sets the status code and message for this response. If the field had
205: * already been set, the new value overwrites the previous one. The message
206: * is sent as the body of an HTML page, which is returned to the user to
207: * describe the problem. The page is sent with a default HTML header; the
208: * message is enclosed in simple body tags (<body></body>).
209: * @param i - the status code
210: * @param reason - the status message
1.28 bmahe 211: * @deprecated since jsdk2.1
1.18 bmahe 212: */
1.5 abaird 213: public void setStatus(int i, String reason) {
1.1 abaird 214: reply.setStatus(i);
215: reply.setReason(reason);
216: }
1.39 ! ylafon 217:
1.18 bmahe 218: /**
219: * Sets the status code for this response. This method is used to set the
220: * return status code when there is no error (for example, for the status
221: * codes SC_OK or SC_MOVED_TEMPORARILY). If there is an error, the
222: * sendError method should be used instead.
223: * @param i - the status code
1.27 ylafon 224: * @see JigsawHttpServletResponse#sendError
1.18 bmahe 225: */
1.5 abaird 226: public void setStatus(int i) {
1.1 abaird 227: setStatus(i, reply.getStandardReason(i));
228: }
1.39 ! ylafon 229:
1.18 bmahe 230: /**
231: * Adds a field to the response header with the given name and value. If
232: * the field had already been set, the new value overwrites the previous
233: * one. The containsHeader method can be used to test for the presence of a
234: * header before setting its value.
235: * @param name - the name of the header field
236: * @param value - the header field's value
1.27 ylafon 237: * @see JigsawHttpServletResponse#containsHeader
1.18 bmahe 238: */
1.5 abaird 239: public void setHeader(String name, String value) {
1.1 abaird 240: reply.setValue(name, value);
241: }
1.39 ! ylafon 242:
1.18 bmahe 243: /**
244: * Adds a field to the response header with the given name and integer
245: * value. If the field had already been set, the new value overwrites the
246: * previous one. The containsHeader method can be used to test for the
247: * presence of a header before setting its value.
248: * @param name - the name of the header field
249: * @param value - the header field's integer value
1.27 ylafon 250: * @see JigsawHttpServletResponse#containsHeader
1.18 bmahe 251: */
1.5 abaird 252: public void setIntHeader(String name, int value) {
1.1 abaird 253: setHeader(name, String.valueOf(value));
254: }
1.39 ! ylafon 255:
1.18 bmahe 256: /**
257: * Adds a field to the response header with the given name and date-valued
258: * field. The date is specified in terms of milliseconds since the epoch.
259: * If the date field had already been set, the new value overwrites the
260: * previous one. The containsHeader method can be used to test for the
261: * presence of a header before setting its value.
262: * @param name - the name of the header field
263: * @param value - the header field's date value
1.27 ylafon 264: * @see JigsawHttpServletResponse#containsHeader
1.18 bmahe 265: */
1.5 abaird 266: public void setDateHeader(String name, long date) {
1.32 bmahe 267: setHeader(name, HttpFactory.makeDate(date).toExternalForm());
1.1 abaird 268: }
1.39 ! ylafon 269:
1.5 abaird 270: public void unsetHeader(String name) {
1.1 abaird 271: setHeader(name, null);
272: }
1.39 ! ylafon 273:
1.18 bmahe 274: /**
275: * Sends an error response to the client using the specified status code
276: * and descriptive message. If setStatus has previously been called, it is
277: * reset to the error status code. The message is sent as the body of an
278: * HTML page, which is returned to the user to describe the problem. The
279: * page is sent with a default HTML header; the message is enclosed in
280: * simple body tags (<body></body>).
281: * @param sc - the status code
282: * @param msg - the detail message
283: * @exception IOException If an I/O error has occurred.
284: */
1.5 abaird 285: public void sendError(int i, String msg)
286: throws IOException
1.1 abaird 287: {
288: setStatus(i);
289: reply.setContent(msg);
290: state = STATE_ALL_DONE;
291: }
1.18 bmahe 292:
293: /**
294: * Sends an error response to the client using the specified status
295: * code and a default message.
296: * @param sc - the status code
297: * @exception IOException If an I/O error has occurred.
298: */
1.1 abaird 299: public void sendError(int i)
300: throws IOException
301: {
302: setStatus(i);
303: reply.setContent(reply.getStandardReason(i));
304: state = STATE_ALL_DONE;
305: }
1.39 ! ylafon 306:
1.18 bmahe 307: /**
308: * Sends a temporary redirect response to the client using the specified
309: * redirect location URL. The URL must be absolute (for example,
310: * https://hostname/path/file.html). Relative URLs are not permitted here.
311: * @param url - the redirect location URL
312: * @exception IOException If an I/O error has occurred.
313: */
1.1 abaird 314: public void sendRedirect(String url)
315: throws IOException
316: {
1.4 abaird 317: URL loc = null;
318: try {
1.37 bmahe 319: String requri = jrequest.getRequestURI();
320: URL requrl = request.getURL();
321: loc = new URL(requrl.getProtocol(),
322: requrl.getHost(),
323: requrl.getPort(),
324: requri);
325: loc = new URL(loc, url);
1.38 bmahe 326: // Removed (netscape doesn't know SEE OTHER! sig)
327: // if (jrequest.getMethod().equalsIgnoreCase("POST") &&
328: // jrequest.getProtocol().equals("HTTP/1.1")) {
329: // setStatus(SC_SEE_OTHER);
330: // } else {
1.35 bmahe 331: setStatus(SC_MOVED_TEMPORARILY);
1.38 bmahe 332: // }
1.4 abaird 333: reply.setLocation(loc);
334: state = STATE_ALL_DONE;
335: } catch (Exception ex) {
336: ex.printStackTrace();
337: }
1.1 abaird 338: }
1.6 bmahe 339:
1.18 bmahe 340: /**
341: * Checks whether the response message header has a field with the
342: * specified name.
343: * @param name - the header field name
344: * @return true if the response message header has a field with the
345: * specified name; false otherwise
346: */
1.12 bmahe 347: public boolean containsHeader(String header) {
348: return reply.hasHeader(header);
349: }
350:
1.18 bmahe 351: /**
352: * Adds the specified cookie to the response. It can be called multiple
353: * times to set more than one cookie.
354: * @param cookie - the Cookie to return to the client
355: */
1.12 bmahe 356: public void addCookie(Cookie cookie) {
1.13 bmahe 357: HttpSetCookieList clist = reply.getSetCookie();
358: if (clist == null) {
359: HttpSetCookie cookies [] = new HttpSetCookie[1];
360: cookies[0] = convertCookie(cookie);
361: clist = new HttpSetCookieList(cookies);
362: } else {
363: clist.addSetCookie(convertCookie(cookie));
364: }
365: reply.setSetCookie(clist);
366: }
1.12 bmahe 367:
1.31 ylafon 368: private HttpSetCookie convertCookie(Cookie cookie) {
1.13 bmahe 369: HttpSetCookie scookie = new HttpSetCookie(true,
370: cookie.getName(),
371: cookie.getValue());
372: scookie.setComment(cookie.getComment());
373: scookie.setDomain(cookie.getDomain());
374: scookie.setMaxAge(cookie.getMaxAge());
375: scookie.setPath(cookie.getPath());
376: scookie.setSecurity(cookie.getSecure());
377: scookie.setVersion(cookie.getVersion());
378: return scookie;
1.12 bmahe 379: }
380:
1.18 bmahe 381: /**
382: * Encodes the specified URL for use in the sendRedirect method or, if
383: * encoding is not needed, returns the URL unchanged. The implementation
384: * of this method should include the logic to determine whether the
385: * session ID needs to be encoded in the URL.
386: * Because the rules for making this determination differ from those used
387: * to decide whether to encode a normal link, this method is seperate from
388: * the encodeUrl method.
389: * <p>All URLs sent to the HttpServletResponse.sendRedirect method should
390: * be run through this method. Otherwise, URL rewriting canont be used
391: * with browsers which do not support cookies.
392: * @param url - the url to be encoded.
393: * @return the encoded URL if encoding is needed; the unchanged URL
394: * otherwise.
1.28 bmahe 395: * @deprecated since jsdk2.1
1.27 ylafon 396: * @see JigsawHttpServletResponse#sendRedirect
397: * @see JigsawHttpServletResponse#encodeUrl
1.18 bmahe 398: */
1.12 bmahe 399: public String encodeRedirectUrl(String url) {
1.19 bmahe 400: try {
401: URL redirect = new URL(url);
402: URL requested = new URL(jrequest.getRequestURI());
403: if ( redirect.getHost().equals(requested.getHost()) &&
404: redirect.getPort() == requested.getPort())
405: return encodeUrl(url);
406: } catch (MalformedURLException ex) {
407: //error so return url.
408: return url;
409: }
410: return url;
1.12 bmahe 411: }
412:
1.28 bmahe 413: /**
414: * Encodes the specified URL for use in the sendRedirect method or, if
415: * encoding is not needed, returns the URL unchanged. The implementation
416: * of this method should include the logic to determine whether the
417: * session ID needs to be encoded in the URL.
418: * Because the rules for making this determination differ from those used
419: * to decide whether to encode a normal link, this method is seperate from
420: * the encodeUrl method.
421: * <p>All URLs sent to the HttpServletResponse.sendRedirect method should
422: * be run through this method. Otherwise, URL rewriting canont be used
423: * with browsers which do not support cookies.
424: * @param url - the url to be encoded.
425: * @return the encoded URL if encoding is needed; the unchanged URL
426: * otherwise.
427: * @see JigsawHttpServletResponse#sendRedirect
428: * @see JigsawHttpServletResponse#encodeUrl
429: */
430: public String encodeRedirectURL(String url) {
431: return encodeRedirectUrl(url);
432: }
433:
1.18 bmahe 434: /**
435: * Encodes the specified URL by including the session ID in it, or, if
436: * encoding is not needed, returns the URL unchanged. The implementation of
437: * this method should include the logic to determine whether the session ID
438: * needs to be encoded in the URL. For example, if the browser supports
439: * cookies, or session tracking is turned off, URL encoding is unnecessary.
440: * <p>All URLs emitted by a Servlet should be run through this method.
441: * Otherwise, URL rewriting cannot be used with browsers which do not
442: * support cookies.
443: * @param url - the url to be encoded.
444: * @return the encoded URL if encoding is needed; the unchanged URL
445: * otherwise.
1.28 bmahe 446: * @deprecated since jsdk2.1
1.18 bmahe 447: */
1.12 bmahe 448: public String encodeUrl(String url) {
1.15 bmahe 449: if (! jrequest.isRequestedSessionIdFromCookie()) {
1.25 bmahe 450: url = url + ((url.indexOf("?") != -1) ? "&" : "?")+
451: jrequest.getCookieName()+"="+
1.15 bmahe 452: jrequest.getSession(true).getId();
453: }
1.12 bmahe 454: return url;
1.28 bmahe 455: }
456:
457: /**
458: * Encodes the specified URL by including the session ID in it, or, if
459: * encoding is not needed, returns the URL unchanged. The implementation of
460: * this method should include the logic to determine whether the session ID
461: * needs to be encoded in the URL. For example, if the browser supports
462: * cookies, or session tracking is turned off, URL encoding is unnecessary.
463: * <p>All URLs emitted by a Servlet should be run through this method.
464: * Otherwise, URL rewriting cannot be used with browsers which do not
465: * support cookies.
466: * @param url - the url to be encoded.
467: * @return the encoded URL if encoding is needed; the unchanged URL
468: * otherwise.
469: */
470: public String encodeURL(String url) {
471: return encodeUrl(url);
1.12 bmahe 472: }
473:
474: /**
475: * Return the Charset parameter of content type
476: * @return A String instance
477: */
478: public String getCharacterEncoding() {
479: org.w3c.www.mime.MimeType type = reply.getContentType();
1.33 bmahe 480: if ((type != null) && (type.hasParameter(CHARSET_PARAMETER))) {
481: return type.getParameterValue(CHARSET_PARAMETER);
1.12 bmahe 482: }
1.30 bmahe 483: return System.getProperty("file.encoding");
1.12 bmahe 484: }
485:
1.18 bmahe 486: /**
487: * Returns a print writer for writing formatted text responses.
488: * The MIME type of the response will be modified, if necessary, to
489: * reflect the character encoding used, through the charset=... property.
490: * This means that the content type must be set before calling this
491: * method.
492: * @exception UnsupportedEncodingException if no such encoding can be
493: * provided
494: * @exception IllegalStateException if getOutputStream has been called
495: * on this same request.
496: * @exception IOException on other errors.
1.27 ylafon 497: * @see JigsawHttpServletResponse#getOutputStream
498: * @see JigsawHttpServletResponse#setContentType
1.18 bmahe 499: */
1.12 bmahe 500: public synchronized PrintWriter getWriter()
501: throws IOException, UnsupportedEncodingException
502: {
503: if (stream_state == OUTPUT_STREAM_USED)
504: throw new IllegalStateException("Output stream used");
505: stream_state = STREAM_WRITER_USED;
1.39 ! ylafon 506:
1.12 bmahe 507: if (writer == null) {
508: writer = new PrintWriter(
1.34 bmahe 509: new OutputStreamWriter(getJigsawOutputStream(true),
1.29 bmahe 510: getCharacterEncoding()));
1.12 bmahe 511: }
512: return writer;
1.20 bmahe 513: }
514:
1.31 ylafon 515: /**
516: * Flush the output stream.
517: * @param close Close the stream if true.
518: * @exception IOException if an IO error occurs.
519: */
1.30 bmahe 520: protected synchronized void flushStream(boolean close)
521: throws IOException
522: {
1.34 bmahe 523: if (state == STATE_ALL_DONE) {
524: return;
525: }
1.30 bmahe 526: int writeLength;
1.29 bmahe 527:
1.30 bmahe 528: if (stream_state == OUTPUT_STREAM_USED) {
529: output.flush();
530: } else if (stream_state == STREAM_WRITER_USED) {
1.36 bmahe 531: writer.flush();
532: output.realFlush();
533: } else {
534: // force flush even if no stream are openned
535: getWriter();
1.30 bmahe 536: writer.flush();
1.34 bmahe 537: output.realFlush();
1.30 bmahe 538: }
1.29 bmahe 539:
1.30 bmahe 540: if (request.hasState(INCLUDED)) {
541: if (out == null)
542: return;
543:
544: if( fixedContentLength != CALC_CONTENT_LENGTH ) {
545: writeLength = (out.size() < fixedContentLength)
546: ? (out.size())
547: : (fixedContentLength);
1.29 bmahe 548: } else {
1.30 bmahe 549: writeLength = out.size();
1.29 bmahe 550: }
1.30 bmahe 551: reply.setContentLength(writeLength);
552:
553: OutputStream rout = reply.getOutputStream(false);
554: byte content[] = out.toByteArray();
555: if (close)
556: out.close();
557: else
558: out.reset();
559: rout.write(content);
560: rout.flush();
1.29 bmahe 561: }
1.13 bmahe 562: }
563:
1.33 bmahe 564: // 2.2
565:
566: /**
567: * Sets the preferred buffer size for the body of the response.
568: * The servlet container will use a buffer at least as large as
569: * the size requested. The actual buffer size used can be found
570: * using <code>getBufferSize</code>.
571: *
572: * <p>A larger buffer allows more content to be written before anything is
573: * actually sent, thus providing the servlet with more time to set
574: * appropriate status codes and headers. A smaller buffer decreases
575: * server memory load and allows the client to start receiving data more
576: * quickly.
577: *
578: * <p>This method must be called before any response body content is
579: * written; if content has been written, this method throws an
580: * <code>IllegalStateException</code>.
581: * @param size the preferred buffer size
582: * @exception IllegalStateException if this method is called after
583: * content has been written
584: * @see #getBufferSize
585: * @see #flushBuffer
586: * @see #isCommitted
587: * @see #reset
588: */
589: public void setBufferSize(int size) {
590: if (stream_state != STREAM_STATE_INITIAL) {
591: throw new IllegalStateException("Stream already initialized");
592: }
1.34 bmahe 593: buffer_size = size < MIN_BUFFER_SIZE ? MIN_BUFFER_SIZE : size;
1.33 bmahe 594: }
595:
596: /**
597: * Returns the actual buffer size used for the response. If no buffering
598: * is used, this method returns 0.
599: * @return the actual buffer size used
600: * @see #setBufferSize
601: * @see #flushBuffer
602: * @see #isCommitted
603: * @see #reset
604: */
605: public int getBufferSize() {
606: return buffer_size;
607: }
608:
609: /**
610: * Forces any content in the buffer to be written to the client. A call
611: * to this method automatically commits the response, meaning the status
612: * code and headers will be written.
613: * @see #setBufferSize
614: * @see #getBufferSize
615: * @see #isCommitted
616: * @see #reset
617: */
618: public void flushBuffer()
619: throws IOException
620: {
621: if (output != null) {
1.34 bmahe 622: if (stream_state == STREAM_WRITER_USED) {
623: writer.flush();
624: }
1.33 bmahe 625: output.flush();
626: }
627: }
628:
629: /**
630: * Returns a boolean indicating if the response has been
631: * committed. A commited response has already had its status
632: * code and headers written.
633: * @return a boolean indicating if the response has been
634: * committed
635: * @see #setBufferSize
636: * @see #getBufferSize
637: * @see #flushBuffer
638: * @see #reset
639: */
640: public boolean isCommitted() {
641: if (output != null) {
642: return output.isCommitted();
643: } else {
644: return false;
645: }
646: }
647:
648: /**
649: * Clears any data that exists in the buffer as well as the status code and
650: * headers. If the response has been committed, this method throws an
651: * <code>IllegalStateException</code>.
652: * @exception IllegalStateException if the response has already been
653: * committed
654: * @see #setBufferSize
655: * @see #getBufferSize
656: * @see #flushBuffer
657: * @see #isCommitted
658: */
659: public void reset() {
660: if (output != null) {
1.34 bmahe 661: if (stream_state == STREAM_WRITER_USED) {
662: writer.flush();
663: }
1.33 bmahe 664: output.reset();
665: }
666: }
667:
668: /**
669: * Sets the locale of the response, setting the headers (including the
670: * Content-Type's charset) as appropriate. This method should be called
671: * before a call to {@link #getWriter}. By default, the response locale
672: * is the default locale for the server.
673: * @param loc the locale of the response
674: * @see #getLocale
675: */
676: public void setLocale(Locale locale) {
677: if (locale == null) {
678: return;
679: }
680: this.locale = locale;
681:
682: // content language
683: String lang = locale.getLanguage();
684: if (lang.length() > 0) {
685: String array[] = new String[1];
686: array[0] = lang;
687: reply.setContentLanguage(array);
688: }
689:
690: // content type (charset)
691: String charset = org.w3c.www.mime.Utils.getCharset(locale);
692: if (charset != null) {
693: MimeType contentType = reply.getContentType();
694: // override charset
695: contentType.setParameter(CHARSET_PARAMETER, charset);
696: }
697: }
698:
699: /**
700: * Returns the locale assigned to the response.
701: * @see #setLocale
702: */
703: public Locale getLocale() {
704: return locale;
705: }
706:
707: public void addHeader(String name, String value) {
708: String lname = name.toLowerCase();
709: HeaderValue hvalue = reply.getHeaderValue(lname);
710: //
711: // Horrible, Shame on us, I hate that
712: //
713: if (hvalue == null) {
714: setHeader(name, value);
715: } else if (hvalue instanceof HttpAcceptCharsetList) {
716: HttpAcceptCharsetList acl = (HttpAcceptCharsetList) hvalue;
717: acl.addCharset(HttpFactory.parseAcceptCharset(value));
718: } else if (hvalue instanceof HttpAcceptEncodingList) {
719: HttpAcceptEncodingList ael = (HttpAcceptEncodingList) hvalue;
720: ael.addEncoding(HttpFactory.parseAcceptEncoding(value));
721: } else if (hvalue instanceof HttpAcceptLanguageList) {
722: HttpAcceptLanguageList all = (HttpAcceptLanguageList) hvalue;
723: all.addLanguage(HttpFactory.parseAcceptLanguage(value));
724: } else if (hvalue instanceof HttpAcceptList) {
725: HttpAcceptList al = (HttpAcceptList) hvalue;
726: al.addAccept(HttpFactory.parseAccept(value));
727: } else if (hvalue instanceof HttpEntityTagList) {
728: HttpEntityTagList etl = (HttpEntityTagList) hvalue;
729: etl.addTag(HttpFactory.parseETag(value));
730: } else if (hvalue instanceof HttpExtList) {
731: HttpExtList el = (HttpExtList) hvalue;
732: el.addHttpExt(new HttpExt(value, false));
733: } else if (hvalue instanceof HttpCookieList) {
734: // shouldn't be used, but who knows?
735: HttpCookieList cl = (HttpCookieList) hvalue;
736: HttpCookieList ncl = HttpFactory.parseCookieList(value);
737: HttpCookie scookies[] = ncl.getCookies();
738: for (int i = 0 ; i < scookies.length ; i++) {
739: HttpCookie cookie = scookies[i];
740: cl.addCookie(cookie.getName(), cookie.getValue());
741: }
742: } else if (hvalue instanceof HttpParamList) {
743: int idx = value.indexOf('=');
744: if (idx != -1) {
745: String pname = value.substring(0, idx);
746: String pvalue = value.substring(idx+1);
747: HttpParamList pl = (HttpParamList) hvalue;
748: pl.setParameter(pname, pvalue);
749: }
750: } else if (hvalue instanceof HttpRangeList) {
751: HttpRangeList rl = (HttpRangeList) hvalue;
752: rl.addRange(HttpFactory.parseRange(value));
753: } else if (hvalue instanceof HttpSetCookieList) {
754: HttpSetCookieList scl = (HttpSetCookieList) hvalue;
755: HttpSetCookieList nscl = HttpFactory.parseSetCookieList(value);
756: HttpSetCookie scookies[] = nscl.getSetCookies();
757: for (int i = 0 ; i < scookies.length ; i++) {
758: scl.addSetCookie(scookies[i]);
759: }
760: } else if (hvalue instanceof HttpTokenList) {
761: ((HttpTokenList) hvalue).addToken(value, true);
762: } else if (hvalue instanceof HttpWarningList) {
763: HttpWarningList wl = (HttpWarningList) hvalue;
764: wl.addWarning(HttpFactory.parseWarning(value));
765: } else if (hvalue instanceof HttpString) {
766: // this is the default type for unkown header
767: // we don't know what it is, so just append
768: HttpString s = (HttpString) hvalue;
769: String string = (String) s.getValue();
770: s.setValue(string+", "+value);
771: } else {
772: // not compliant with HTTP/1.1, override
773: setHeader(name, value);
774: }
775: }
776:
777: public void addDateHeader(String name, long date) {
778: addHeader(name, HttpFactory.makeDate(date).toExternalForm());
779: }
780:
781: public void addIntHeader(String name, int value) {
782: addHeader(name, String.valueOf(value));
783: }
784:
1.13 bmahe 785: JigsawHttpServletResponse(Request request, Reply reply) {
1.33 bmahe 786: this.request = request;
787: this.reply = reply;
788: this.buffer_size = DEFAULT_BUFFER_SIZE;
1.11 bmahe 789: }
1.6 bmahe 790:
1.1 abaird 791: }
Webmaster