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