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