001: package com.quadcap.http.server22;
002:
003: /* Copyright 1997 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.util.Calendar;
042: import java.util.Date;
043: import java.util.Enumeration;
044: import java.util.Hashtable;
045: import java.util.Locale;
046: import java.util.TimeZone;
047: import java.util.Vector;
048:
049: import java.text.DateFormat;
050: import java.text.SimpleDateFormat;
051:
052: import java.io.ByteArrayOutputStream;
053: import java.io.IOException;
054: import java.io.OutputStream;
055: import java.io.OutputStreamWriter;
056: import java.io.PrintWriter;
057:
058: import javax.servlet.ServletOutputStream;
059: import javax.servlet.http.Cookie;
060: import javax.servlet.http.HttpServletResponse;
061:
062: import com.quadcap.net.server.WorkerOutputStream;
063:
064: import com.quadcap.util.Debug;
065:
066: /**
067: * An HTTP servlet response. This interface allows a servlet's
068: * <code>service</code> method to manipulate HTTP-protocol specified
069: * header information and return data to its client. It is implemented
070: * by network service developers for use within servlets.
071: *
072: * @author Stan Bailes
073: */
074: public class HttpResponse implements HttpServletResponse {
075: WebWorker w;
076: Hashtable headers = new Hashtable();
077: String protocol;
078: int sc = SC_OK;
079: String sm = "Request Completed";
080: boolean changedCode = false;
081: Locale defaultLocale = Locale.getDefault();
082: Locale locale = defaultLocale;
083:
084: int contentLength = -1;
085: String contentType = null;
086: StringBuffer headerBuf = new StringBuffer();
087: boolean headerBufValid = true;
088: boolean anyHeaders = false;
089: boolean keepAlive = false;
090: public boolean getWriterCalled = false;
091: public boolean getOutputStreamCalled = false;
092:
093: byte[] fixedHeaders = null;
094: ByteArrayOutputStream fixedH = new ByteArrayOutputStream();
095:
096: HttpOutputStream os;
097: PrintWriter writer = null;
098: JspWriter jWriter = new JspWriter(8192, true);
099: boolean jWriterInit = false;
100:
101: Vector cookies = null;
102: long calbase = -1;
103: byte[] curTime = null;
104:
105: static DateFormat cookieDateFormat;
106: static TimeZone GMT;
107: static SimpleDateFormat df;
108: static SimpleDateFormat df2;
109:
110: static {
111: GMT = TimeZone.getTimeZone("GMT");
112: cookieDateFormat = new SimpleDateFormat(
113: "E, dd-MMM-yy HH:mm:ss 'GMT'");
114: cookieDateFormat.setTimeZone(GMT);
115: df = new SimpleDateFormat(
116: "'Date: 'EEE, dd MMM yyyy HH:mm:ss 'GMT\r\n'");
117: df.setTimeZone(GMT);
118: df2 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
119: df2.setTimeZone(GMT);
120: }
121:
122: /**
123: * Construct a new HttpResponse for the specified worker.
124: *
125: * @param w the worker handling this http request
126: */
127: public HttpResponse(WebWorker w) {
128: this .w = w;
129: }
130:
131: public void reset(HttpOutputStream os) {
132: this .os = os;
133: this .os.setResponse(this );
134: headers.clear();
135: sc = SC_OK;
136: jWriterInit = false;
137: sm = "Request Completed";
138: changedCode = false;
139: locale = defaultLocale;
140: contentLength = -1;
141: contentType = null;
142: headerBuf.setLength(0);
143: headerBufValid = true;
144: writer = null;
145: cookies = null;
146: anyHeaders = false;
147: getWriterCalled = false;
148: getOutputStreamCalled = false;
149: }
150:
151: void setProtocol(String p) {
152: this .protocol = p;
153: }
154:
155: void setKeepAlive(boolean v) {
156: this .keepAlive = v;
157: }
158:
159: static final void write(OutputStream out, String s)
160: throws IOException {
161: for (int i = 0; i < s.length(); i++) {
162: out.write((byte) s.charAt(i));
163: }
164: }
165:
166: static final byte[] headerSep = { (byte) ':', (byte) ' ' };
167: static final byte[] CRLF = { (byte) '\r', (byte) '\n' };
168: static final byte[] OKRESPONSE = " 200 Request Completed\r\n"
169: .getBytes();
170: static final byte[] CONTENT_TYPE = "Content-Type: ".getBytes();
171: static final byte[] CONTENT_LENGTH = "Content-Length: ".getBytes();
172: static final byte[] CONNECTION_KEEPALIVE = "Connection: Keep-Alive\r\n"
173: .getBytes();
174: static final byte[] CONNECTION_CLOSE = "Connection: Close\r\n"
175: .getBytes();
176: static final byte[] DEFAULT_CONTENT_TYPE = "Content-Type: text/plain\r\n"
177: .getBytes();
178:
179: final void writeHeader(WorkerOutputStream out, String name,
180: String val) throws IOException {
181: //#ifdef DEBUG
182: if (Trace.level() > 1) {
183: Debug.println("header: " + name + ": " + val);
184: }
185: //#endif
186: out.write(name);
187: out.write(headerSep);
188: out.write(val); // XXX folding?
189: out.write(CRLF);
190: }
191:
192: /**
193: * Write the result code and the message headers for the response
194: */
195: public void writeHeaders() throws IOException {
196: WorkerOutputStream out = os.getOutputStream();
197: out.write(protocol);
198: if (changedCode) {
199: out.write(' ');
200: out.writeInt(sc);
201: out.write(' ');
202: out.write(sm);
203: out.write(CRLF);
204: } else {
205: out.write(OKRESPONSE);
206: }
207:
208: if (keepAlive) {
209: out.write(CONNECTION_KEEPALIVE);
210: } else {
211: out.write(CONNECTION_CLOSE);
212: }
213:
214: if (contentType != null) {
215: out.write(CONTENT_TYPE);
216: out.write(contentType);
217: out.write(CRLF);
218: } else {
219: out.write(DEFAULT_CONTENT_TYPE);
220: }
221:
222: if (contentLength >= 0) {
223: out.write(CONTENT_LENGTH);
224: out.writeInt(contentLength);
225: out.write(CRLF);
226: }
227: if (fixedHeaders == null) {
228: if (fixedH != null) {
229: fixedHeaders = fixedH.toByteArray();
230: fixedH = null;
231: }
232: out.write(fixedHeaders);
233: }
234:
235: if (headerBufValid) {
236: out.write(headerBuf.toString());
237: } else if (anyHeaders) {
238: Enumeration hdrs = headers.keys();
239: while (hdrs.hasMoreElements()) {
240: String headerName = (String) hdrs.nextElement();
241: Object headerVal = headers.get(headerName);
242: if (headerVal instanceof String) {
243: writeHeader(out, headerName, headerVal.toString());
244: } else {
245: Vector v = (Vector) headerVal;
246: for (int i = 0; i < v.size(); i++) {
247: writeHeader(out, headerName, v.elementAt(i)
248: .toString());
249: }
250: }
251: }
252: }
253:
254: if (cookies != null) {
255: writeCookies(out);
256: }
257: writeDateHeader(out);
258: out.write(CRLF);
259: }
260:
261: static byte[] digits = "0123456789".getBytes();
262: static byte[] GMTString = " GMT\r\n".getBytes();
263:
264: final void writeDateHeader(WorkerOutputStream out)
265: throws IOException {
266: long now = System.currentTimeMillis();
267: long msec = now - calbase;
268: if (msec >= 60000) {
269: Calendar c = Calendar.getInstance(GMT);
270: c.setTime(new Date(now));
271: c.set(Calendar.SECOND, 0);
272: c.set(Calendar.MILLISECOND, 0);
273: Date d = c.getTime();
274: calbase = d.getTime();
275: curTime = df.format(d).getBytes();
276: msec = now - calbase;
277: if (msec < 0)
278: msec = 0;
279: }
280: int sec = (int) (msec / 1000);
281: curTime[29] = digits[sec / 10];
282: curTime[30] = digits[sec % 10];
283: out.write(curTime);
284: }
285:
286: void writeAttr(WorkerOutputStream out, String name, String val)
287: throws IOException {
288: if (val != null) {
289: out.write(';');
290: out.write(name);
291: out.write('=');
292: out.write(val);
293: }
294: }
295:
296: // from RFC 2068, token special case characters
297: private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
298:
299: /**
300: * Return true iff the string counts as an HTTP/1.1 "token".
301: */
302: private static boolean isToken(String value) {
303: final int len = value.length();
304:
305: for (int i = 0; i < len; i++) {
306: char c = value.charAt(i);
307: if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1)
308: return false;
309: }
310: return true;
311: }
312:
313: private final void writeTok(WorkerOutputStream out, String val,
314: int v) throws IOException {
315: if (v == 0 || isToken(val)) {
316: out.write(val);
317: } else {
318: out.write('"');
319: out.write(val);
320: out.write('"');
321: }
322: }
323:
324: void writeCookies(WorkerOutputStream out) throws IOException {
325: out.write("Set-Cookie: ");
326: for (int i = 0; i < cookies.size(); i++) {
327: Cookie cookie = (Cookie) cookies.elementAt(i);
328: final int version = cookie.getVersion();
329: if (i > 0)
330: out.write('\t');
331: out.write(cookie.getName());
332: String val = cookie.getValue();
333: if (val != null) {
334: out.write('=');
335: writeTok(out, val, version);
336: }
337:
338: writeAttr(out, "Path", cookie.getPath());
339: if (version == 0) {
340: writeAttr(out, "Domain", cookie.getDomain());
341: if (cookie.getMaxAge() >= 0) {
342: Date d = new Date();
343: long ms = d.getTime() + cookie.getMaxAge() * 1000;
344: d.setTime(ms);
345: writeAttr(out, "Expires", cookieDateFormat
346: .format(d));
347: }
348: } else {
349: out.write(";Version=1");
350: out.write(";Domain=");
351: writeTok(out, cookie.getDomain(), version);
352: if (cookie.getMaxAge() >= 0) {
353: writeAttr(out, "Max-Age", "" + cookie.getMaxAge());
354: } else {
355: out.write(";Discard");
356: }
357: writeAttr(out, "Comment", cookie.getComment());
358: }
359: if (cookie.getSecure())
360: out.write(";Ssecure");
361: out.write(CRLF);
362: }
363: }
364:
365: final void setFixedHeader(String name, String val) {
366: try {
367: write(fixedH, name);
368: write(fixedH, ": ");
369: write(fixedH, val);
370: fixedH.write(CRLF);
371: } catch (Exception e) {
372: }
373: }
374:
375: /**
376: * ----- SerlvetResponse methods -----
377: */
378:
379: /**
380: * Sets the content length for this response.
381: *
382: * @param len the content length
383: */
384: public void setContentLength(int len) {
385: contentLength = len;
386: }
387:
388: final void maybeSetContentLength(int len) {
389: if (contentLength == -1) {
390: contentLength = len;
391: }
392: }
393:
394: int getContentLength() {
395: return contentLength;
396: }
397:
398: /**
399: * Sets the content type for this response. This type may later
400: * be implicitly modified by addition of properties such as the MIME
401: * <em>charset=<value></em> if the service finds it necessary,
402: * and the appropriate media type property has not been set.
403: *
404: * <p>This response property may only be assigned one time. If a
405: * writer is to be used to write a text response, this method must
406: * be called before the method <code>getWriter</code>. If an
407: * output stream will be used to write a response, this method must
408: * be called before the output stream is used to write response
409: * data.
410: *
411: * @param type the content's MIME type
412: */
413: public void setContentType(String type) {
414: contentType = type;
415: }
416:
417: /**
418: * Returns an output stream for writing binary response data.
419: *
420: * @exception IllegalStateException if getWriter has been
421: * called on this same request.
422: * @exception IOException if an I/O exception has occurred
423: */
424: public ServletOutputStream getOutputStream() throws IOException {
425: if (getWriterCalled) {
426: throw new IllegalStateException(
427: "getWriter() already called");
428: }
429: getOutputStreamCalled = true;
430: return os;
431: }
432:
433: /**
434: * Returns a print writer for writing formatted text responses. The
435: * MIME type of the response will be modified, if necessary, to reflect
436: * the character encoding used, through the <em>charset=...</em>
437: * property. This means that the content type must be set before
438: * calling this method.
439: *
440: *
441: * @exception UnsupportedEncodingException if no such encoding can
442: * be provided
443: * @exception IllegalStateException if getOutputStream has been
444: * called on this same request.
445: * @exception IOException on other errors.
446: */
447: public PrintWriter getWriter() throws IOException {
448: if (getOutputStreamCalled) {
449: throw new IllegalStateException(
450: "getOutputStream() already called");
451: }
452: if (writer == null) {
453: String encoding = getCharacterEncoding();
454: OutputStreamWriter ww = null;
455: OutputStream os = getOutputStream();
456: if (encoding != null) {
457: ww = new OutputStreamWriter(os, encoding);
458: } else {
459: ww = new OutputStreamWriter(os);
460: }
461: writer = new PrintWriter(ww);
462: getWriterCalled = true;
463: }
464: return writer;
465: }
466:
467: public JspWriter getJspWriter(int bufferSize, boolean autoFlush)
468: throws IOException {
469: if (!jWriterInit) {
470: jWriterInit = true;
471: jWriter.setHttpOutputStream(os);
472: jWriter.setBufferSize(bufferSize);
473: jWriter.setAutoFlush(autoFlush);
474: }
475: return jWriter;
476: }
477:
478: /**
479: * Returns the character set encoding used for this MIME body.
480: * The character encoding is either the one specified in the
481: * assigned content type, or one which the client understands.
482: * If no content type has yet been assigned, it is implicitly
483: * set to <em>text/plain</em>
484: */
485: public String getCharacterEncoding() {
486: String s = contentType;
487: if (s == null)
488: return null;
489:
490: int idx = s.indexOf(';');
491: while (idx > 0) {
492: s = s.substring(idx + 1).trim();
493: idx = s.indexOf(';');
494: String c = s;
495: if (idx > 0) {
496: c = s.substring(0, idx).trim();
497: }
498: if (c.startsWith("charset=")) {
499: return c.substring(8);
500: }
501: }
502: return null;
503: }
504:
505: /**
506: * ----- HttpServletResponse methods -----
507: */
508:
509: /**
510: * Adds the specified cookie to the response. It can be called
511: * multiple times to set more than one cookie.
512: *
513: * @param cookie the Cookie to return to the client
514: */
515: public void addCookie(Cookie cookie) {
516: //#ifdef DEBUG
517: if (Trace.level() > 7) {
518: Debug.println("addCookie(" + cookie + ")");
519: }
520: //#endif
521: if (cookies == null) {
522: cookies = new Vector();
523: }
524: cookies.addElement(cookie);
525: }
526:
527: /**
528: * Checks whether the response message header has a field with
529: * the specified name.
530: *
531: * @param name the header field name
532: * @return true if the response message header has a field with
533: * the specified name; false otherwise
534: */
535: public boolean containsHeader(String name) {
536: return headers.get(name) != null;
537: }
538:
539: /**
540: * Sets the status code and message for this response. If the
541: * field had already been set, the new value overwrites the
542: * previous one. The message is sent as the body of an HTML
543: * page, which is returned to the user to describe the problem.
544: * The page is sent with a default HTML header; the message
545: * is enclosed in simple body tags (<body></body>).
546: *
547: * @param sc the status code
548: * @param sm the status message
549: */
550: public void setStatus(int sc, String sm) {
551: this .changedCode = true;
552: this .sc = sc;
553: this .sm = sm;
554: }
555:
556: /**
557: * Sets the status code for this response. This method is used to
558: * set the return status code when there is no error (for example,
559: * for the status codes SC_OK or SC_MOVED_TEMPORARILY). If there
560: * is an error, the <code>sendError</code> method should be used
561: * instead.
562: *
563: * @param sc the status code
564: *
565: */
566: public void setStatus(int sc) {
567: this .changedCode = true;
568: this .sc = sc;
569: this .sm = "";
570: }
571:
572: /**
573: *
574: * Adds a field to the response header with the given name and value.
575: * If the field had already been set, the new value overwrites the
576: * previous one. The <code>containsHeader</code> method can be
577: * used to test for the presence of a header before setting its
578: * value.
579: *
580: * @param name the name of the header field
581: * @param value the header field's value
582: *
583: */
584: public void setHeader(String name, String value) {
585: if (name.charAt(0) == 'C') {
586: if (name.equals("Content-Type")) {
587: contentType = value;
588: return;
589: } else if (name.equals("Content-Length")) {
590: contentLength = Integer.parseInt(value);
591: return;
592: }
593: }
594: if (headerBufValid) {
595: if (headers.get(name) == null) {
596: appendHeader(name, value);
597: } else {
598: headerBufValid = false;
599: }
600: }
601: anyHeaders = true;
602: headers.put(name, value);
603: }
604:
605: final void appendHeader(String name, String value) {
606: headerBuf.append(name);
607: headerBuf.append(": ");
608: headerBuf.append(value);
609: headerBuf.append("\r\n");
610: }
611:
612: /**
613: * Add a new header value -- this method allows multiple headers with
614: * the same name.
615: */
616: public void addHeader(String name, String value) {
617: Object obj = headers.get(name);
618: Vector v = null;
619: boolean mustInsert = true;
620: if (obj instanceof String) {
621: v = new Vector();
622: v.addElement(obj.toString());
623: } else if (obj == null) {
624: v = new Vector();
625: } else {
626: v = (Vector) obj;
627: mustInsert = false;
628: }
629: v.addElement(value);
630: if (mustInsert) {
631: anyHeaders = true;
632: headers.put(name, v);
633: }
634: appendHeader(name, value);
635: }
636:
637: /**
638: * Adds a field to the response header with the given name and
639: * integer value. If the field had already been set, the new value
640: * overwrites the previous one. The <code>containsHeader</code>
641: * method can be used to test for the presence of a header before
642: * setting its value.
643: *
644: * @param name the name of the header field
645: * @param value the header field's integer value
646: *
647: */
648: public void setIntHeader(String name, int value) {
649: setHeader(name, "" + value);
650: }
651:
652: public void addIntHeader(String name, int value) {
653: addHeader(name, "" + value);
654: }
655:
656: /**
657: *
658: * Adds a field to the response header with the given name and
659: * date-valued field. The date is specified in terms of
660: * milliseconds since the epoch. If the date field had already
661: * been set, the new value overwrites the previous one. The
662: * <code>containsHeader</code> method can be used to test for the
663: * presence of a header before setting its value.
664: *
665: * @param name the name of the header field
666: * @param value the header field's date value
667: *
668: */
669: public void setDateHeader(String name, long date) {
670: Date d = new Date(date);
671: setHeader(name, df2.format(d));
672: }
673:
674: public void addDateHeader(String name, long date) {
675: Date d = new Date(date);
676: addHeader(name, df2.format(d));
677: }
678:
679: /**
680: * Sends an error response to the client using the specified status
681: * code and descriptive message. If setStatus has previously been
682: * called, it is reset to the error status code. The message is
683: * sent as the body of an HTML page, which is returned to the user
684: * to describe the problem. The page is sent with a default HTML
685: * header; the message is enclosed in simple body tags
686: * (<body></body>).
687: *
688: * @param sc the status code
689: * @param msg the detail message
690: * @exception IOException If an I/O error has occurred. */
691: public void sendError(int scode, String msg) throws IOException {
692: setStatus(scode, msg);
693: if (os.discard()) {
694: writer = null;
695: }
696: getWriterCalled = getOutputStreamCalled = false;
697: setContentType("text/html");
698: PrintWriter p = getWriter();
699: p.println("<HEAD><TITLE>" + scode + " " + msg
700: + "</TITLE></HEAD>");
701: p.print("<BODY><H1>");
702: p.print(msg);
703: p.println("</H1>");
704: if (scode == HttpServletResponse.SC_NOT_FOUND) {
705: p.print("The requested URL was not found on this server");
706: p.println("<P>");
707: }
708: p.println("</BODY>");
709: flush();
710: }
711:
712: /**
713: * Sends an error response to the client using the specified
714: * status code and a default message.
715: * @param scode the status code
716: * @exception IOException If an I/O error has occurred.
717: */
718: public void sendError(int scode) throws IOException {
719: sm = "Error " + scode;
720: setStatus(scode);
721: if (os.discard()) {
722: writer = null;
723: }
724: getWriterCalled = getOutputStreamCalled = false;
725: PrintWriter p = getWriter();
726: p.println("<html><head><title>" + sm + "</title></head>");
727: p.println("<body>");
728: p.println(sm);
729: p.println("</body></html>");
730: flush();
731: }
732:
733: /**
734: * Flush the underlying output stream
735: */
736: void flush() throws IOException {
737: if (writer != null)
738: writer.flush();
739: os.flush();
740: }
741:
742: /**
743: * Sends a temporary redirect response to the client using the
744: * specified redirect location URL. The URL must be absolute (for
745: * example, <code><em>https://hostname/path/file.html</em></code>).
746: * Relative URLs are not permitted here.
747: *
748: * @param location the redirect location URL
749: * @exception IOException If an I/O error has occurred.
750: */
751: public void sendRedirect(String location) throws IOException {
752: setHeader("Location", location);
753: sendError(SC_MOVED_TEMPORARILY, "<a href=\"" + location + "\">"
754: + location + "</A>");
755: }
756:
757: /**
758: * Encodes the specified URL by including the session ID in it,
759: * or, if encoding is not needed, returns the URL unchanged.
760: * The implementation of this method should include the logic to
761: * determine whether the session ID needs to be encoded in the URL.
762: * For example, if the browser supports cookies, or session
763: * tracking is turned off, URL encoding is unnecessary.
764: *
765: * <p>All URLs emitted by a Servlet should be run through this
766: * method. Otherwise, URL rewriting cannot be used with browsers
767: * which do not support cookies.
768: *
769: * @param url the url to be encoded.
770: * @return the encoded URL if encoding is needed; the unchanged URL
771: * otherwise.
772: */
773: public String encodeUrl(String url) {
774: return url;
775: }
776:
777: public String encodeURL(String url) {
778: return url;
779: }
780:
781: /**
782: * Encodes the specified URL for use in the
783: * <code>sendRedirect</code> method or, if encoding is not needed,
784: * returns the URL unchanged. The implementation of this method
785: * should include the logic to determine whether the session ID
786: * needs to be encoded in the URL. Because the rules for making
787: * this determination differ from those used to decide whether to
788: * encode a normal link, this method is seperate from the
789: * <code>encodeUrl</code> method.
790: *
791: * <p>All URLs sent to the HttpServletResponse.sendRedirect
792: * method should be run through this method. Otherwise, URL
793: * rewriting canont be used with browsers which do not support
794: * cookies.
795: *
796: * @param url the url to be encoded.
797: * @return the encoded URL if encoding is needed; the unchanged URL
798: * otherwise.
799: *
800: */
801: public String encodeRedirectUrl(String url) {
802: return url;
803: }
804:
805: public String encodeRedirectURL(String url) {
806: return url;
807: }
808:
809: public void flushBuffer() throws IOException {
810: if (writer != null)
811: writer.flush();
812: os.flushBuffered();
813: }
814:
815: public void setBufferSize(int size) throws IllegalStateException {
816: os.setBufferSize(size);
817: }
818:
819: public int getBufferSize() {
820: return os.getBufferSize();
821: }
822:
823: public boolean isCommitted() {
824: return os.isCommitted();
825: }
826:
827: public void reset() throws IllegalStateException {
828: os.reset(); // checks for committed, may throw IllegalStateException
829: sc = SC_OK;
830: headers.clear();
831: sm = "Request Completed";
832: changedCode = false;
833: locale = defaultLocale;
834: contentLength = -1;
835: contentType = null;
836: headerBuf.setLength(0);
837: headerBufValid = true;
838: writer = null;
839: cookies = null;
840: anyHeaders = false;
841: getWriterCalled = false;
842: jWriterInit = false;
843: getOutputStreamCalled = false;
844: }
845:
846: public void setLocale(Locale locale) {
847: this .locale = locale;
848: if (locale != null) {
849: String lang = locale.getLanguage();
850: if (lang != null) {
851: setHeader("Content-Language", lang);
852: }
853: }
854:
855: }
856:
857: public Locale getLocale() {
858: return locale;
859: }
860: }
|