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