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