Annotation of java/classes/org/w3c/jigsaw/servlet/JigsawHttpServletResponse.java, revision 1.31
1.1 abaird 1: // JigsawHttpServletReponse.java
1.31 ! ylafon 2: // $Id: JigsawHttpServletResponse.java,v 1.31 1999/04/08 15:09: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:
8: import java.io.*;
1.6 bmahe 9: import javax.servlet.*;
10: import javax.servlet.http.*;
1.4 abaird 11: import java.net.*;
1.1 abaird 12:
1.10 bmahe 13: import org.w3c.www.mime.*;
14: import org.w3c.www.http.*;
15: import org.w3c.jigsaw.http.*;
1.2 abaird 16:
17: /**
1.30 bmahe 18: * @author Alexandre Rafalovitch <alex@access.com.au>
19: * @author Anselm Baird-Smith <abaird@w3.org>
20: * @author Benoît Mahé (bmahe@w3.org)
21: * @author Roland Mainz (Roland.Mainz@informatik.med.uni-giessen.de)
1.2 abaird 22: */
1.1 abaird 23:
1.29 bmahe 24: public class JigsawHttpServletResponse implements HttpServletResponse {
25:
1.31 ! ylafon 26: private final static int STATE_INITIAL = 0;
! 27: private final static int STATE_HEADERS_DONE = 1;
! 28: private final static int STATE_ALL_DONE = 2;
1.11 bmahe 29: private final static int STREAM_STATE_INITIAL = 0;
1.31 ! ylafon 30: private final static int STREAM_WRITER_USED = 1;
! 31: private final static int OUTPUT_STREAM_USED = 2;
1.11 bmahe 32:
33: private int stream_state = STREAM_STATE_INITIAL;
1.20 bmahe 34:
35: private JigsawServletOutputStream output = null;
36: private PrintWriter writer = null;
1.7 bmahe 37:
1.30 bmahe 38: private MimeTypeFormatException setContentTypeException = null;
39:
40: // servlet has set a fixed content length or not,
41: // and cut (see flushStream) any data which are too much here...
42: private final static int CALC_CONTENT_LENGTH = -1;
43: private int fixedContentLength = CALC_CONTENT_LENGTH;
44:
1.29 bmahe 45: /**
46: * Our temp stream.
47: */
48: protected ByteArrayOutputStream out = null;
49:
1.14 bmahe 50: protected JigsawHttpServletRequest jrequest = null;
51:
52: protected void setServletRequest(JigsawHttpServletRequest jrequest) {
53: this.jrequest = jrequest;
54: }
55:
1.7 bmahe 56: public static final
1.29 bmahe 57: String INCLUDED = "org.w3c.jigsaw.servlet.included";
1.28 bmahe 58:
1.1 abaird 59: int state = STATE_INITIAL;
60: Reply reply = null;
61: Request request = null;
62:
1.18 bmahe 63: /**
64: * Sets the content length for this response.
65: * @param len - the content length
66: */
1.5 abaird 67: public void setContentLength(int i) {
1.30 bmahe 68: fixedContentLength = i;
1.1 abaird 69: reply.setContentLength(i);
70: }
1.18 bmahe 71:
72: /**
73: * Sets the content type for this response. This type may later be
74: * implicitly modified by addition of properties such as the MIME
75: * charset=<value> if the service finds it necessary, and the appropriate
76: * media type property has not been set.
77: * <p>This response property may only be assigned one time. If a writer
78: * is to be used to write a text response, this method must be
79: * called before the method getWriter. If an output stream will be used
80: * to write a response, this method must be called before the
81: * output stream is used to write response data.
82: * @param spec - the content's MIME type
1.27 ylafon 83: * @see JigsawHttpServletResponse#getOutputStream
84: * @see JigsawHttpServletResponse#getWriter
1.18 bmahe 85: */
1.5 abaird 86: public void setContentType(String spec) {
1.1 abaird 87: try {
88: MimeType type= new MimeType(spec);
89: reply.setContentType(type);
1.30 bmahe 90: setContentTypeException = null;
1.1 abaird 91: } catch(MimeTypeFormatException ex) {
1.30 bmahe 92: //store exception
93: setContentTypeException = ex;
1.1 abaird 94: }
95: }
1.5 abaird 96:
1.28 bmahe 97: protected boolean isStreamObtained() {
98: return (stream_state != STREAM_STATE_INITIAL);
99: }
100:
1.29 bmahe 101: protected Reply getReply() {
102: return reply;
103: }
104:
1.18 bmahe 105: /**
106: * Returns an output stream for writing binary response data.
1.24 bmahe 107: * @return A ServletOutputStream
1.18 bmahe 108: * @exception IOException if an I/O exception has occurred
109: * @exception IllegalStateException if getWriter has been called on this
110: * same request.
1.27 ylafon 111: * @see JigsawHttpServletResponse#getWriter
1.18 bmahe 112: */
1.5 abaird 113: public synchronized ServletOutputStream getOutputStream()
114: throws IOException
115: {
1.12 bmahe 116: if (stream_state == STREAM_WRITER_USED)
117: throw new IllegalStateException("Writer used");
118: stream_state = OUTPUT_STREAM_USED;
119: return getJigsawOutputStream();
1.11 bmahe 120: }
121:
122: protected ServletOutputStream getJigsawOutputStream()
123: throws IOException
124: {
1.12 bmahe 125: if ( output != null )
126: return output;
1.30 bmahe 127:
128: // any exception during setContentType ?
129: if( setContentTypeException != null ) {
130: // "wrap" the exception from setContentType in an IOException
131: throw new IOException("Illegal Contenttype: "+
132: setContentTypeException.toString());
133: }
134:
135: if( request.hasState(INCLUDED) ) {
136: out = new ByteArrayOutputStream();
137: output = new JigsawServletOutputStream(this,
138: new DataOutputStream(out));
139: } else {
140: output = new JigsawServletOutputStream(this, reply);
141: }
1.5 abaird 142: return output;
1.1 abaird 143: }
1.29 bmahe 144:
1.18 bmahe 145: /**
146: * Sets the status code and message for this response. If the field had
147: * already been set, the new value overwrites the previous one. The message
148: * is sent as the body of an HTML page, which is returned to the user to
149: * describe the problem. The page is sent with a default HTML header; the
150: * message is enclosed in simple body tags (<body></body>).
151: * @param i - the status code
152: * @param reason - the status message
1.28 bmahe 153: * @deprecated since jsdk2.1
1.18 bmahe 154: */
1.5 abaird 155: public void setStatus(int i, String reason) {
1.1 abaird 156: reply.setStatus(i);
157: reply.setReason(reason);
158: }
159:
1.18 bmahe 160: /**
161: * Sets the status code for this response. This method is used to set the
162: * return status code when there is no error (for example, for the status
163: * codes SC_OK or SC_MOVED_TEMPORARILY). If there is an error, the
164: * sendError method should be used instead.
165: * @param i - the status code
1.27 ylafon 166: * @see JigsawHttpServletResponse#sendError
1.18 bmahe 167: */
1.5 abaird 168: public void setStatus(int i) {
1.1 abaird 169: setStatus(i, reply.getStandardReason(i));
170: }
171:
1.18 bmahe 172: /**
173: * Adds a field to the response header with the given name and value. If
174: * the field had already been set, the new value overwrites the previous
175: * one. The containsHeader method can be used to test for the presence of a
176: * header before setting its value.
177: * @param name - the name of the header field
178: * @param value - the header field's value
1.27 ylafon 179: * @see JigsawHttpServletResponse#containsHeader
1.18 bmahe 180: */
1.5 abaird 181: public void setHeader(String name, String value) {
1.1 abaird 182: reply.setValue(name, value);
183: }
184:
1.18 bmahe 185: /**
186: * Adds a field to the response header with the given name and integer
187: * value. If the field had already been set, the new value overwrites the
188: * previous one. The containsHeader method can be used to test for the
189: * presence of a header before setting its value.
190: * @param name - the name of the header field
191: * @param value - the header field's integer value
1.27 ylafon 192: * @see JigsawHttpServletResponse#containsHeader
1.18 bmahe 193: */
1.5 abaird 194: public void setIntHeader(String name, int value) {
1.1 abaird 195: setHeader(name, String.valueOf(value));
196: }
197:
1.18 bmahe 198: /**
199: * Adds a field to the response header with the given name and date-valued
200: * field. The date is specified in terms of milliseconds since the epoch.
201: * If the date field had already been set, the new value overwrites the
202: * previous one. The containsHeader method can be used to test for the
203: * presence of a header before setting its value.
204: * @param name - the name of the header field
205: * @param value - the header field's date value
1.27 ylafon 206: * @see JigsawHttpServletResponse#containsHeader
1.18 bmahe 207: */
1.5 abaird 208: public void setDateHeader(String name, long date) {
1.1 abaird 209: setHeader(name, String.valueOf(date));
210: }
211:
1.5 abaird 212: public void unsetHeader(String name) {
1.1 abaird 213: setHeader(name, null);
214: }
215:
1.18 bmahe 216: /**
217: * Sends an error response to the client using the specified status code
218: * and descriptive message. If setStatus has previously been called, it is
219: * reset to the error status code. The message is sent as the body of an
220: * HTML page, which is returned to the user to describe the problem. The
221: * page is sent with a default HTML header; the message is enclosed in
222: * simple body tags (<body></body>).
223: * @param sc - the status code
224: * @param msg - the detail message
225: * @exception IOException If an I/O error has occurred.
226: */
1.5 abaird 227: public void sendError(int i, String msg)
228: throws IOException
1.1 abaird 229: {
230: setStatus(i);
231: reply.setContent(msg);
232: state = STATE_ALL_DONE;
233: }
1.18 bmahe 234:
235: /**
236: * Sends an error response to the client using the specified status
237: * code and a default message.
238: * @param sc - the status code
239: * @exception IOException If an I/O error has occurred.
240: */
1.1 abaird 241: public void sendError(int i)
242: throws IOException
243: {
244: setStatus(i);
245: reply.setContent(reply.getStandardReason(i));
246: state = STATE_ALL_DONE;
247: }
248:
1.18 bmahe 249: /**
250: * Sends a temporary redirect response to the client using the specified
251: * redirect location URL. The URL must be absolute (for example,
252: * https://hostname/path/file.html). Relative URLs are not permitted here.
253: * @param url - the redirect location URL
254: * @exception IOException If an I/O error has occurred.
255: */
1.1 abaird 256: public void sendRedirect(String url)
257: throws IOException
258: {
1.4 abaird 259: URL loc = null;
260: try {
261: loc = new URL(request.getURL(), url);
262: setStatus(SC_MOVED_TEMPORARILY);
263: reply.setLocation(loc);
264: state = STATE_ALL_DONE;
265: } catch (Exception ex) {
266: ex.printStackTrace();
267: }
1.1 abaird 268: }
1.6 bmahe 269:
1.18 bmahe 270: /**
271: * Checks whether the response message header has a field with the
272: * specified name.
273: * @param name - the header field name
274: * @return true if the response message header has a field with the
275: * specified name; false otherwise
276: */
1.12 bmahe 277: public boolean containsHeader(String header) {
278: return reply.hasHeader(header);
279: }
280:
1.18 bmahe 281: /**
282: * Adds the specified cookie to the response. It can be called multiple
283: * times to set more than one cookie.
284: * @param cookie - the Cookie to return to the client
285: */
1.12 bmahe 286: public void addCookie(Cookie cookie) {
1.13 bmahe 287: HttpSetCookieList clist = reply.getSetCookie();
288: if (clist == null) {
289: HttpSetCookie cookies [] = new HttpSetCookie[1];
290: cookies[0] = convertCookie(cookie);
291: clist = new HttpSetCookieList(cookies);
292: } else {
293: clist.addSetCookie(convertCookie(cookie));
294: }
295: reply.setSetCookie(clist);
296: }
1.12 bmahe 297:
1.31 ! ylafon 298: private HttpSetCookie convertCookie(Cookie cookie) {
1.13 bmahe 299: HttpSetCookie scookie = new HttpSetCookie(true,
300: cookie.getName(),
301: cookie.getValue());
302: scookie.setComment(cookie.getComment());
303: scookie.setDomain(cookie.getDomain());
304: scookie.setMaxAge(cookie.getMaxAge());
305: scookie.setPath(cookie.getPath());
306: scookie.setSecurity(cookie.getSecure());
307: scookie.setVersion(cookie.getVersion());
308: return scookie;
1.12 bmahe 309: }
310:
1.18 bmahe 311: /**
312: * Encodes the specified URL for use in the sendRedirect method or, if
313: * encoding is not needed, returns the URL unchanged. The implementation
314: * of this method should include the logic to determine whether the
315: * session ID needs to be encoded in the URL.
316: * Because the rules for making this determination differ from those used
317: * to decide whether to encode a normal link, this method is seperate from
318: * the encodeUrl method.
319: * <p>All URLs sent to the HttpServletResponse.sendRedirect method should
320: * be run through this method. Otherwise, URL rewriting canont be used
321: * with browsers which do not support cookies.
322: * @param url - the url to be encoded.
323: * @return the encoded URL if encoding is needed; the unchanged URL
324: * otherwise.
1.28 bmahe 325: * @deprecated since jsdk2.1
1.27 ylafon 326: * @see JigsawHttpServletResponse#sendRedirect
327: * @see JigsawHttpServletResponse#encodeUrl
1.18 bmahe 328: */
1.12 bmahe 329: public String encodeRedirectUrl(String url) {
1.19 bmahe 330: try {
331: URL redirect = new URL(url);
332: URL requested = new URL(jrequest.getRequestURI());
333: if ( redirect.getHost().equals(requested.getHost()) &&
334: redirect.getPort() == requested.getPort())
335: return encodeUrl(url);
336: } catch (MalformedURLException ex) {
337: //error so return url.
338: return url;
339: }
340: return url;
1.12 bmahe 341: }
342:
1.28 bmahe 343: /**
344: * Encodes the specified URL for use in the sendRedirect method or, if
345: * encoding is not needed, returns the URL unchanged. The implementation
346: * of this method should include the logic to determine whether the
347: * session ID needs to be encoded in the URL.
348: * Because the rules for making this determination differ from those used
349: * to decide whether to encode a normal link, this method is seperate from
350: * the encodeUrl method.
351: * <p>All URLs sent to the HttpServletResponse.sendRedirect method should
352: * be run through this method. Otherwise, URL rewriting canont be used
353: * with browsers which do not support cookies.
354: * @param url - the url to be encoded.
355: * @return the encoded URL if encoding is needed; the unchanged URL
356: * otherwise.
357: * @see JigsawHttpServletResponse#sendRedirect
358: * @see JigsawHttpServletResponse#encodeUrl
359: */
360: public String encodeRedirectURL(String url) {
361: return encodeRedirectUrl(url);
362: }
363:
1.18 bmahe 364: /**
365: * Encodes the specified URL by including the session ID in it, or, if
366: * encoding is not needed, returns the URL unchanged. The implementation of
367: * this method should include the logic to determine whether the session ID
368: * needs to be encoded in the URL. For example, if the browser supports
369: * cookies, or session tracking is turned off, URL encoding is unnecessary.
370: * <p>All URLs emitted by a Servlet should be run through this method.
371: * Otherwise, URL rewriting cannot be used with browsers which do not
372: * support cookies.
373: * @param url - the url to be encoded.
374: * @return the encoded URL if encoding is needed; the unchanged URL
375: * otherwise.
1.28 bmahe 376: * @deprecated since jsdk2.1
1.18 bmahe 377: */
1.12 bmahe 378: public String encodeUrl(String url) {
1.15 bmahe 379: if (! jrequest.isRequestedSessionIdFromCookie()) {
1.25 bmahe 380: url = url + ((url.indexOf("?") != -1) ? "&" : "?")+
381: jrequest.getCookieName()+"="+
1.15 bmahe 382: jrequest.getSession(true).getId();
383: }
1.12 bmahe 384: return url;
1.28 bmahe 385: }
386:
387: /**
388: * Encodes the specified URL by including the session ID in it, or, if
389: * encoding is not needed, returns the URL unchanged. The implementation of
390: * this method should include the logic to determine whether the session ID
391: * needs to be encoded in the URL. For example, if the browser supports
392: * cookies, or session tracking is turned off, URL encoding is unnecessary.
393: * <p>All URLs emitted by a Servlet should be run through this method.
394: * Otherwise, URL rewriting cannot be used with browsers which do not
395: * support cookies.
396: * @param url - the url to be encoded.
397: * @return the encoded URL if encoding is needed; the unchanged URL
398: * otherwise.
399: */
400: public String encodeURL(String url) {
401: return encodeUrl(url);
1.12 bmahe 402: }
403:
404: /**
405: * Return the Charset parameter of content type
406: * @return A String instance
407: */
408: public String getCharacterEncoding() {
409: org.w3c.www.mime.MimeType type = reply.getContentType();
410: if ((type != null) && (type.hasParameter("charset"))) {
411: return type.getParameterValue("charset");
412: }
1.30 bmahe 413: return System.getProperty("file.encoding");
1.12 bmahe 414: }
415:
1.18 bmahe 416: /**
417: * Returns a print writer for writing formatted text responses.
418: * The MIME type of the response will be modified, if necessary, to
419: * reflect the character encoding used, through the charset=... property.
420: * This means that the content type must be set before calling this
421: * method.
422: * @exception UnsupportedEncodingException if no such encoding can be
423: * provided
424: * @exception IllegalStateException if getOutputStream has been called
425: * on this same request.
426: * @exception IOException on other errors.
1.27 ylafon 427: * @see JigsawHttpServletResponse#getOutputStream
428: * @see JigsawHttpServletResponse#setContentType
1.18 bmahe 429: */
1.12 bmahe 430: public synchronized PrintWriter getWriter()
431: throws IOException, UnsupportedEncodingException
432: {
433: if (stream_state == OUTPUT_STREAM_USED)
434: throw new IllegalStateException("Output stream used");
435: stream_state = STREAM_WRITER_USED;
1.11 bmahe 436:
1.12 bmahe 437: if (writer == null) {
438: writer = new PrintWriter(
1.29 bmahe 439: new OutputStreamWriter(getJigsawOutputStream(),
440: getCharacterEncoding()));
1.12 bmahe 441: }
442: return writer;
1.20 bmahe 443: }
444:
1.31 ! ylafon 445: /**
! 446: * Flush the output stream.
! 447: * @param close Close the stream if true.
! 448: * @exception IOException if an IO error occurs.
! 449: */
1.30 bmahe 450: protected synchronized void flushStream(boolean close)
451: throws IOException
452: {
453: int writeLength;
1.29 bmahe 454:
1.30 bmahe 455: if (stream_state == OUTPUT_STREAM_USED) {
456: output.flush();
457: } else if (stream_state == STREAM_WRITER_USED) {
458: writer.flush();
459: }
1.29 bmahe 460:
1.30 bmahe 461: if (request.hasState(INCLUDED)) {
462: if (out == null)
463: return;
464:
465: if( fixedContentLength != CALC_CONTENT_LENGTH ) {
466: writeLength = (out.size() < fixedContentLength)
467: ? (out.size())
468: : (fixedContentLength);
1.29 bmahe 469: } else {
1.30 bmahe 470: writeLength = out.size();
1.29 bmahe 471: }
1.30 bmahe 472: reply.setContentLength(writeLength);
473:
474: OutputStream rout = reply.getOutputStream(false);
475: byte content[] = out.toByteArray();
476: if (close)
477: out.close();
478: else
479: out.reset();
480: rout.write(content);
481: rout.flush();
1.29 bmahe 482: }
1.13 bmahe 483: }
484:
485: JigsawHttpServletResponse(Request request, Reply reply) {
486: this.request = request;
487: this.reply = reply;
1.11 bmahe 488: }
1.6 bmahe 489:
1.1 abaird 490: }
Webmaster