Annotation of java/classes/org/w3c/jigsaw/servlet/JigsawHttpServletResponse.java, revision 1.39

1.1       abaird      1: // JigsawHttpServletReponse.java
1.39    ! ylafon      2: // $Id: JigsawHttpServletResponse.java,v 1.38 2000/07/25 08:58:08 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: 
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;
        !            13: import java.io.PrintWriter;
        !            14: import java.io.UnsupportedEncodingException;
        !            15: 
        !            16: import javax.servlet.ServletOutputStream;
        !            17: 
        !            18: import javax.servlet.http.Cookie;
        !            19: import javax.servlet.http.HttpServletResponse;
        !            20: import javax.servlet.http.HttpSession;
        !            21: 
        !            22: import java.net.MalformedURLException;
        !            23: import java.net.URL;
        !            24: 
1.33      bmahe      25: import java.util.Locale;
1.1       abaird     26: 
1.39    ! ylafon     27: import org.w3c.www.mime.MimeType;
        !            28: import org.w3c.www.mime.MimeTypeFormatException;
        !            29: import org.w3c.www.mime.Utils;
        !            30: 
        !            31: import org.w3c.www.http.BasicValue;
        !            32: import org.w3c.www.http.HttpAcceptCharsetList;
        !            33: import org.w3c.www.http.HttpAcceptEncodingList;
        !            34: import org.w3c.www.http.HttpAcceptLanguageList;
        !            35: import org.w3c.www.http.HttpAcceptList;
        !            36: import org.w3c.www.http.HttpCookie;
        !            37: import org.w3c.www.http.HttpCookieList;
        !            38: import org.w3c.www.http.HttpEntityMessage;
        !            39: import org.w3c.www.http.HttpEntityTagList;
        !            40: import org.w3c.www.http.HttpExt;
        !            41: import org.w3c.www.http.HttpExtList;
        !            42: import org.w3c.www.http.HttpFactory;
        !            43: import org.w3c.www.http.HttpMessage;
        !            44: import org.w3c.www.http.HttpParamList;
        !            45: import org.w3c.www.http.HttpRangeList;
        !            46: import org.w3c.www.http.HttpReplyMessage;
        !            47: import org.w3c.www.http.HttpRequestMessage;
        !            48: import org.w3c.www.http.HttpSetCookie;
        !            49: import org.w3c.www.http.HttpSetCookieList;
        !            50: import org.w3c.www.http.HttpString;
        !            51: import org.w3c.www.http.HttpTokenList;
        !            52: import org.w3c.www.http.HttpWarningList;
        !            53: 
        !            54: import org.w3c.jigsaw.http.Reply;
        !            55: import org.w3c.jigsaw.http.Request;
        !            56: 
1.33      bmahe      57: import org.w3c.www.http.HeaderValue;
1.2       abaird     58: 
                     59: /**
1.30      bmahe      60:  * @author Alexandre Rafalovitch <alex@access.com.au>
                     61:  * @author Anselm Baird-Smith <abaird@w3.org>
                     62:  * @author Benoît Mahé (bmahe@w3.org)
                     63:  * @author Roland Mainz (Roland.Mainz@informatik.med.uni-giessen.de)
1.2       abaird     64:  */
1.1       abaird     65: 
1.29      bmahe      66: public class JigsawHttpServletResponse implements HttpServletResponse {
                     67: 
1.33      bmahe      68:     public final static String CHARSET_PARAMETER = "charset";
                     69: 
                     70:     public final static int DEFAULT_BUFFER_SIZE   = 8 * 1024; // 8KB buffer
                     71:     public final static int MIN_BUFFER_SIZE       = 4 * 1024; // 4KB buffer
                     72: 
1.31      ylafon     73:     private final static int STATE_INITIAL        = 0;
                     74:     private final static int STATE_HEADERS_DONE   = 1;
                     75:     private final static int STATE_ALL_DONE       = 2;
1.34      bmahe      76: 
1.11      bmahe      77:     private final static int STREAM_STATE_INITIAL = 0;
1.31      ylafon     78:     private final static int STREAM_WRITER_USED   = 1;
                     79:     private final static int OUTPUT_STREAM_USED   = 2;
1.11      bmahe      80: 
                     81:     private int stream_state = STREAM_STATE_INITIAL;
1.20      bmahe      82: 
                     83:     private JigsawServletOutputStream output = null;
                     84:     private PrintWriter               writer = null;
1.39    ! ylafon     85: 
1.30      bmahe      86:     private MimeTypeFormatException setContentTypeException = null;
                     87: 
                     88:     // servlet has set a fixed content length or not, 
                     89:     // and cut (see flushStream) any data which are too much here...
                     90:     private final static int CALC_CONTENT_LENGTH = -1; 
                     91:     private              int fixedContentLength  = CALC_CONTENT_LENGTH;
                     92: 
1.33      bmahe      93:     // Our Locale
                     94:     protected Locale locale;
                     95: 
1.29      bmahe      96:     /**
                     97:      * Our temp stream.
                     98:      */
                     99:     protected ByteArrayOutputStream out = null;
                    100: 
1.14      bmahe     101:     protected JigsawHttpServletRequest jrequest = null;
                    102: 
1.33      bmahe     103:     protected int buffer_size;
                    104: 
1.14      bmahe     105:     protected void setServletRequest(JigsawHttpServletRequest jrequest) {
                    106:        this.jrequest = jrequest;
                    107:     }
                    108: 
1.7       bmahe     109:     public static final 
1.29      bmahe     110:        String INCLUDED = "org.w3c.jigsaw.servlet.included";
1.28      bmahe     111: 
1.1       abaird    112:     int   state = STATE_INITIAL;
                    113:     Reply reply = null;
                    114:     Request request = null;
1.39    ! ylafon    115: 
1.18      bmahe     116:     /**
                    117:      * Sets the content length for this response. 
                    118:      * @param len - the content length 
                    119:      */
1.5       abaird    120:     public void setContentLength(int i) {
1.30      bmahe     121:        fixedContentLength = i;
1.1       abaird    122:        reply.setContentLength(i);
                    123:     }
1.18      bmahe     124: 
                    125:     /**
                    126:      * Sets the content type for this response. This type may later be 
                    127:      * implicitly modified by addition of properties such as the MIME
                    128:      * charset=<value> if the service finds it necessary, and the appropriate
                    129:      * media type property has not been set.
                    130:      * <p>This response property may only be assigned one time. If a writer 
                    131:      * is to be used to write a text response, this method must be
                    132:      * called before the method getWriter. If an output stream will be used 
                    133:      * to write a response, this method must be called before the
                    134:      * output stream is used to write response data. 
                    135:      * @param spec - the content's MIME type 
1.27      ylafon    136:      * @see JigsawHttpServletResponse#getOutputStream
                    137:      * @see JigsawHttpServletResponse#getWriter
1.18      bmahe     138:      */    
1.5       abaird    139:     public void setContentType(String spec) {
1.1       abaird    140:        try {
                    141:            MimeType type= new MimeType(spec);
                    142:            reply.setContentType(type);
1.30      bmahe     143:            setContentTypeException = null;
1.1       abaird    144:        } catch(MimeTypeFormatException ex) {
1.30      bmahe     145:            //store exception
                    146:            setContentTypeException = ex;
1.1       abaird    147:        }
                    148:     }
1.5       abaird    149: 
1.28      bmahe     150:     protected boolean isStreamObtained() {
                    151:        return (stream_state != STREAM_STATE_INITIAL);
                    152:     }
                    153: 
1.29      bmahe     154:     protected Reply getReply() {
                    155:        return reply;
                    156:     }
                    157: 
1.18      bmahe     158:     /**
                    159:      * Returns an output stream for writing binary response data.
1.24      bmahe     160:      * @return A ServletOutputStream
1.18      bmahe     161:      * @exception IOException if an I/O exception has occurred 
                    162:      * @exception IllegalStateException if getWriter has been called on this 
                    163:      * same request. 
1.27      ylafon    164:      * @see JigsawHttpServletResponse#getWriter
1.18      bmahe     165:      */    
1.5       abaird    166:     public synchronized ServletOutputStream getOutputStream()
                    167:        throws IOException
                    168:     {
1.12      bmahe     169:        if (stream_state == STREAM_WRITER_USED)
                    170:            throw new IllegalStateException("Writer used");
                    171:        stream_state = OUTPUT_STREAM_USED;
1.34      bmahe     172:        return getJigsawOutputStream(false);
1.11      bmahe     173:     }
                    174: 
1.34      bmahe     175:     protected ServletOutputStream getJigsawOutputStream(boolean writerUsed)
1.11      bmahe     176:        throws IOException
                    177:     {
1.12      bmahe     178:        if ( output != null )
                    179:            return output;
1.30      bmahe     180: 
                    181:        // any exception during setContentType ?    
                    182:        if( setContentTypeException != null ) {
                    183:            // "wrap" the exception from setContentType in an IOException
1.33      bmahe     184:            throw new IOException("Illegal Content Type: "+
1.30      bmahe     185:                                  setContentTypeException.toString());
                    186:        }
                    187: 
                    188:        if( request.hasState(INCLUDED) ) {
                    189:            out = new ByteArrayOutputStream();
                    190:            output = new JigsawServletOutputStream(this, 
1.33      bmahe     191:                                                   new DataOutputStream(out),
1.34      bmahe     192:                                                   buffer_size,
                    193:                                                   writerUsed);
1.30      bmahe     194:        } else {
1.34      bmahe     195:            output = new JigsawServletOutputStream(this, 
                    196:                                                   reply, 
                    197:                                                   buffer_size,
                    198:                                                   writerUsed);
1.30      bmahe     199:        }
1.5       abaird    200:        return output;
1.1       abaird    201:     }
1.29      bmahe     202: 
1.18      bmahe     203:     /**
                    204:      * Sets the status code and message for this response. If the field had
                    205:      * already been set, the new value overwrites the previous one. The message
                    206:      * is sent as the body of an HTML page, which is returned to the user to
                    207:      * describe the problem. The page is sent with a default HTML header; the
                    208:      * message is enclosed in simple body tags (<body></body>).
                    209:      * @param i - the status code 
                    210:      * @param reason - the status message
1.28      bmahe     211:      * @deprecated since jsdk2.1
1.18      bmahe     212:      */
1.5       abaird    213:     public void setStatus(int i, String reason) {
1.1       abaird    214:        reply.setStatus(i);
                    215:        reply.setReason(reason);
                    216:     }
1.39    ! ylafon    217: 
1.18      bmahe     218:     /**
                    219:      * Sets the status code for this response. This method is used to set the
                    220:      * return status code when there is no error (for example, for the status
                    221:      * codes SC_OK or SC_MOVED_TEMPORARILY). If there is an error, the 
                    222:      * sendError method should be used instead.
                    223:      * @param i - the status code 
1.27      ylafon    224:      * @see JigsawHttpServletResponse#sendError
1.18      bmahe     225:      */
1.5       abaird    226:     public void setStatus(int i) {
1.1       abaird    227:        setStatus(i, reply.getStandardReason(i));
                    228:     }
1.39    ! ylafon    229: 
1.18      bmahe     230:     /**
                    231:      * Adds a field to the response header with the given name and value. If
                    232:      * the field had already been set, the new value overwrites the previous
                    233:      * one. The containsHeader method can be used to test for the presence of a
                    234:      * header before setting its value.
                    235:      * @param name - the name of the header field 
                    236:      * @param value - the header field's value 
1.27      ylafon    237:      * @see JigsawHttpServletResponse#containsHeader
1.18      bmahe     238:      */
1.5       abaird    239:     public void setHeader(String name, String value) {
1.1       abaird    240:        reply.setValue(name, value);
                    241:     }
1.39    ! ylafon    242: 
1.18      bmahe     243:     /**
                    244:      * Adds a field to the response header with the given name and integer
                    245:      * value. If the field had already been set, the new value overwrites the
                    246:      * previous one. The containsHeader method can be used to test for the
                    247:      * presence of a header before setting its value.
                    248:      * @param name - the name of the header field 
                    249:      * @param value - the header field's integer value 
1.27      ylafon    250:      * @see JigsawHttpServletResponse#containsHeader
1.18      bmahe     251:      */
1.5       abaird    252:     public void setIntHeader(String name, int value) {
1.1       abaird    253:        setHeader(name, String.valueOf(value));
                    254:     }
1.39    ! ylafon    255: 
1.18      bmahe     256:     /**
                    257:      * Adds a field to the response header with the given name and date-valued
                    258:      * field. The date is specified in terms of milliseconds since the epoch. 
                    259:      * If the date field had already been set, the new value overwrites the
                    260:      * previous one. The containsHeader method can be used to test for the
                    261:      * presence of a header before setting its value.
                    262:      * @param name - the name of the header field 
                    263:      * @param value - the header field's date value 
1.27      ylafon    264:      * @see JigsawHttpServletResponse#containsHeader 
1.18      bmahe     265:      */
1.5       abaird    266:     public void setDateHeader(String name, long date) {
1.32      bmahe     267:        setHeader(name, HttpFactory.makeDate(date).toExternalForm());
1.1       abaird    268:     }
1.39    ! ylafon    269: 
1.5       abaird    270:     public void unsetHeader(String name) {
1.1       abaird    271:        setHeader(name, null);
                    272:     }
1.39    ! ylafon    273: 
1.18      bmahe     274:     /**
                    275:      * Sends an error response to the client using the specified status code
                    276:      * and descriptive message. If setStatus has previously been called, it is
                    277:      * reset to the error status code. The message is sent as the body of an
                    278:      * HTML page, which is returned to the user to describe the problem. The
                    279:      * page is sent with a default HTML header; the message is enclosed in
                    280:      * simple body tags (<body></body>).
                    281:      * @param sc - the status code 
                    282:      * @param msg - the detail message 
                    283:      * @exception IOException If an I/O error has occurred.
                    284:      */
1.5       abaird    285:     public void sendError(int i, String msg) 
                    286:        throws IOException
1.1       abaird    287:     {
                    288:        setStatus(i);
                    289:        reply.setContent(msg);
                    290:        state = STATE_ALL_DONE;
                    291:     }
1.18      bmahe     292: 
                    293:     /**
                    294:      * Sends an error response to the client using the specified status 
                    295:      * code and a default message. 
                    296:      * @param sc - the status code 
                    297:      * @exception IOException If an I/O error has occurred.
                    298:      */
1.1       abaird    299:     public void sendError(int i)
                    300:         throws IOException
                    301:     {
                    302:        setStatus(i);
                    303:        reply.setContent(reply.getStandardReason(i));
                    304:        state = STATE_ALL_DONE;
                    305:     }
1.39    ! ylafon    306: 
1.18      bmahe     307:     /**
                    308:      * Sends a temporary redirect response to the client using the specified
                    309:      *  redirect location URL. The URL must be absolute (for example, 
                    310:      * https://hostname/path/file.html). Relative URLs are not permitted here. 
                    311:      * @param url - the redirect location URL 
                    312:      * @exception IOException If an I/O error has occurred. 
                    313:      */    
1.1       abaird    314:     public void sendRedirect(String url)
                    315:         throws IOException
                    316:     {
1.4       abaird    317:        URL loc = null;
                    318:        try {
1.37      bmahe     319:            String requri = jrequest.getRequestURI();
                    320:            URL    requrl = request.getURL();
                    321:            loc = new URL(requrl.getProtocol(), 
                    322:                          requrl.getHost(), 
                    323:                          requrl.getPort(),
                    324:                          requri);
                    325:            loc = new URL(loc, url);
1.38      bmahe     326: //  Removed (netscape doesn't know SEE OTHER! sig)
                    327: //         if (jrequest.getMethod().equalsIgnoreCase("POST") &&
                    328: //             jrequest.getProtocol().equals("HTTP/1.1")) {
                    329: //             setStatus(SC_SEE_OTHER);
                    330: //         } else {
1.35      bmahe     331:                setStatus(SC_MOVED_TEMPORARILY);
1.38      bmahe     332: //         }
1.4       abaird    333:            reply.setLocation(loc);
                    334:            state = STATE_ALL_DONE;
                    335:        } catch (Exception ex) {
                    336:            ex.printStackTrace();
                    337:        }
1.1       abaird    338:     }
1.6       bmahe     339: 
1.18      bmahe     340:     /**
                    341:      * Checks whether the response message header has a field with the
                    342:      * specified name. 
                    343:      * @param name - the header field name 
                    344:      * @return true if the response message header has a field with the 
                    345:      * specified name; false otherwise
                    346:      */
1.12      bmahe     347:     public boolean containsHeader(String header) {
                    348:        return reply.hasHeader(header);
                    349:     }
                    350: 
1.18      bmahe     351:     /**
                    352:      * Adds the specified cookie to the response. It can be called multiple 
                    353:      * times to set more than one cookie. 
                    354:      * @param cookie - the Cookie to return to the client 
                    355:      */
1.12      bmahe     356:     public void addCookie(Cookie cookie) {
1.13      bmahe     357:        HttpSetCookieList clist = reply.getSetCookie();
                    358:        if (clist == null) {
                    359:            HttpSetCookie cookies [] = new HttpSetCookie[1];
                    360:            cookies[0] = convertCookie(cookie);
                    361:            clist = new HttpSetCookieList(cookies);
                    362:        } else {
                    363:            clist.addSetCookie(convertCookie(cookie));
                    364:        }
                    365:        reply.setSetCookie(clist);
                    366:     }
1.12      bmahe     367: 
1.31      ylafon    368:     private HttpSetCookie convertCookie(Cookie cookie) {
1.13      bmahe     369:        HttpSetCookie scookie = new HttpSetCookie(true, 
                    370:                                                  cookie.getName(),
                    371:                                                  cookie.getValue());
                    372:        scookie.setComment(cookie.getComment());
                    373:        scookie.setDomain(cookie.getDomain());
                    374:        scookie.setMaxAge(cookie.getMaxAge());
                    375:        scookie.setPath(cookie.getPath());
                    376:        scookie.setSecurity(cookie.getSecure());
                    377:        scookie.setVersion(cookie.getVersion());
                    378:        return scookie;
1.12      bmahe     379:     }
                    380: 
1.18      bmahe     381:     /**
                    382:      * Encodes the specified URL for use in the sendRedirect method or, if 
                    383:      * encoding is not needed, returns the URL unchanged. The implementation 
                    384:      * of this method should include the logic to determine whether the 
                    385:      * session ID needs to be encoded in the URL.
                    386:      * Because the rules for making this determination differ from those used
                    387:      * to decide whether to encode a normal link, this method is seperate from
                    388:      * the encodeUrl method.
                    389:      * <p>All URLs sent to the HttpServletResponse.sendRedirect method should
                    390:      * be run through this method. Otherwise, URL rewriting canont be used 
                    391:      * with browsers which do not support cookies. 
                    392:      * @param url - the url to be encoded. 
                    393:      * @return the encoded URL if encoding is needed; the unchanged URL 
                    394:      * otherwise. 
1.28      bmahe     395:      * @deprecated since jsdk2.1
1.27      ylafon    396:      * @see JigsawHttpServletResponse#sendRedirect
                    397:      * @see JigsawHttpServletResponse#encodeUrl
1.18      bmahe     398:      */
1.12      bmahe     399:     public String encodeRedirectUrl(String url) {
1.19      bmahe     400:        try {
                    401:            URL redirect = new URL(url);
                    402:            URL requested = new URL(jrequest.getRequestURI());
                    403:            if ( redirect.getHost().equals(requested.getHost()) &&
                    404:                 redirect.getPort() == requested.getPort())
                    405:                return encodeUrl(url);
                    406:        } catch (MalformedURLException ex) {
                    407:            //error so return url.
                    408:            return url;
                    409:        }
                    410:        return url;
1.12      bmahe     411:     }
                    412: 
1.28      bmahe     413:         /**
                    414:      * Encodes the specified URL for use in the sendRedirect method or, if 
                    415:      * encoding is not needed, returns the URL unchanged. The implementation 
                    416:      * of this method should include the logic to determine whether the 
                    417:      * session ID needs to be encoded in the URL.
                    418:      * Because the rules for making this determination differ from those used
                    419:      * to decide whether to encode a normal link, this method is seperate from
                    420:      * the encodeUrl method.
                    421:      * <p>All URLs sent to the HttpServletResponse.sendRedirect method should
                    422:      * be run through this method. Otherwise, URL rewriting canont be used 
                    423:      * with browsers which do not support cookies. 
                    424:      * @param url - the url to be encoded. 
                    425:      * @return the encoded URL if encoding is needed; the unchanged URL 
                    426:      * otherwise. 
                    427:      * @see JigsawHttpServletResponse#sendRedirect
                    428:      * @see JigsawHttpServletResponse#encodeUrl
                    429:      */
                    430:     public String encodeRedirectURL(String url) {
                    431:        return encodeRedirectUrl(url);
                    432:     }
                    433: 
1.18      bmahe     434:     /**
                    435:      * Encodes the specified URL by including the session ID in it, or, if 
                    436:      * encoding is not needed, returns the URL unchanged. The implementation of
                    437:      * this method should include the logic to determine whether the session ID
                    438:      * needs to be encoded in the URL. For example, if the browser supports
                    439:      * cookies, or session tracking is turned off, URL encoding is unnecessary.
                    440:      * <p>All URLs emitted by a Servlet should be run through this method. 
                    441:      * Otherwise, URL rewriting cannot be used with browsers which do not 
                    442:      * support cookies.
                    443:      * @param url - the url to be encoded. 
                    444:      * @return the encoded URL if encoding is needed; the unchanged URL 
                    445:      * otherwise. 
1.28      bmahe     446:      * @deprecated since jsdk2.1
1.18      bmahe     447:      */
1.12      bmahe     448:     public String encodeUrl(String url) {
1.15      bmahe     449:        if (! jrequest.isRequestedSessionIdFromCookie()) {
1.25      bmahe     450:            url = url + ((url.indexOf("?") != -1) ? "&" : "?")+
                    451:                jrequest.getCookieName()+"="+
1.15      bmahe     452:                jrequest.getSession(true).getId();
                    453:        }
1.12      bmahe     454:        return url;
1.28      bmahe     455:     }
                    456: 
                    457:     /**
                    458:      * Encodes the specified URL by including the session ID in it, or, if 
                    459:      * encoding is not needed, returns the URL unchanged. The implementation of
                    460:      * this method should include the logic to determine whether the session ID
                    461:      * needs to be encoded in the URL. For example, if the browser supports
                    462:      * cookies, or session tracking is turned off, URL encoding is unnecessary.
                    463:      * <p>All URLs emitted by a Servlet should be run through this method. 
                    464:      * Otherwise, URL rewriting cannot be used with browsers which do not 
                    465:      * support cookies.
                    466:      * @param url - the url to be encoded. 
                    467:      * @return the encoded URL if encoding is needed; the unchanged URL 
                    468:      * otherwise. 
                    469:      */
                    470:     public String encodeURL(String url) {
                    471:        return encodeUrl(url);
1.12      bmahe     472:     }
                    473: 
                    474:     /**
                    475:      * Return the Charset parameter of content type
                    476:      * @return A String instance
                    477:      */
                    478:     public String getCharacterEncoding() {
                    479:        org.w3c.www.mime.MimeType type = reply.getContentType();
1.33      bmahe     480:        if ((type != null) && (type.hasParameter(CHARSET_PARAMETER))) {
                    481:            return type.getParameterValue(CHARSET_PARAMETER);
1.12      bmahe     482:        }
1.30      bmahe     483:        return System.getProperty("file.encoding");
1.12      bmahe     484:     }
                    485: 
1.18      bmahe     486:     /**
                    487:      * Returns a print writer for writing formatted text responses. 
                    488:      * The MIME type of the response will be modified, if necessary, to
                    489:      * reflect the character encoding used, through the charset=... property. 
                    490:      * This means that the content type must be set before calling this 
                    491:      * method. 
                    492:      * @exception UnsupportedEncodingException if no such encoding can be 
                    493:      * provided 
                    494:      * @exception IllegalStateException if getOutputStream has been called 
                    495:      * on this same request.
                    496:      * @exception IOException on other errors. 
1.27      ylafon    497:      * @see JigsawHttpServletResponse#getOutputStream
                    498:      * @see JigsawHttpServletResponse#setContentType 
1.18      bmahe     499:      */    
1.12      bmahe     500:     public synchronized PrintWriter getWriter() 
                    501:        throws IOException, UnsupportedEncodingException
                    502:     {
                    503:        if (stream_state == OUTPUT_STREAM_USED)
                    504:            throw new IllegalStateException("Output stream used");
                    505:        stream_state = STREAM_WRITER_USED;
1.39    ! ylafon    506: 
1.12      bmahe     507:        if (writer == null) {
                    508:            writer = new PrintWriter(
1.34      bmahe     509:                         new OutputStreamWriter(getJigsawOutputStream(true), 
1.29      bmahe     510:                                                getCharacterEncoding()));
1.12      bmahe     511:        }
                    512:        return writer;
1.20      bmahe     513:     }
                    514: 
1.31      ylafon    515:     /**
                    516:      * Flush the output stream.
                    517:      * @param close Close the stream if true.
                    518:      * @exception IOException if an IO error occurs.
                    519:      */
1.30      bmahe     520:     protected synchronized void flushStream(boolean close) 
                    521:        throws IOException
                    522:     {
1.34      bmahe     523:        if (state == STATE_ALL_DONE) {
                    524:          return;
                    525:        }
1.30      bmahe     526:        int writeLength;
1.29      bmahe     527: 
1.30      bmahe     528:        if (stream_state == OUTPUT_STREAM_USED) {
                    529:            output.flush();
                    530:        } else if (stream_state == STREAM_WRITER_USED) {
1.36      bmahe     531:            writer.flush();
                    532:            output.realFlush();
                    533:        } else {
                    534:            // force flush even if no stream are openned
                    535:            getWriter();
1.30      bmahe     536:            writer.flush();
1.34      bmahe     537:            output.realFlush();
1.30      bmahe     538:        }
1.29      bmahe     539: 
1.30      bmahe     540:        if (request.hasState(INCLUDED)) {
                    541:            if (out == null)
                    542:                return;
                    543: 
                    544:            if( fixedContentLength != CALC_CONTENT_LENGTH ) {
                    545:                writeLength = (out.size() < fixedContentLength) 
                    546:                    ? (out.size())
                    547:                    : (fixedContentLength);
1.29      bmahe     548:            } else {
1.30      bmahe     549:                writeLength = out.size();
1.29      bmahe     550:            }
1.30      bmahe     551:            reply.setContentLength(writeLength);
                    552:            
                    553:            OutputStream rout = reply.getOutputStream(false);
                    554:            byte content[] = out.toByteArray();
                    555:            if (close)
                    556:                out.close();
                    557:            else 
                    558:                out.reset();
                    559:            rout.write(content);
                    560:            rout.flush();
1.29      bmahe     561:        }
1.13      bmahe     562:     }
                    563: 
1.33      bmahe     564:     // 2.2
                    565: 
                    566:     /**
                    567:      * Sets the preferred buffer size for the body of the response.  
                    568:      * The servlet container will use a buffer at least as large as 
                    569:      * the size requested.  The actual buffer size used can be found
                    570:      * using <code>getBufferSize</code>.
                    571:      *
                    572:      * <p>A larger buffer allows more content to be written before anything is
                    573:      * actually sent, thus providing the servlet with more time to set
                    574:      * appropriate status codes and headers.  A smaller buffer decreases 
                    575:      * server memory load and allows the client to start receiving data more
                    576:      * quickly.
                    577:      *
                    578:      * <p>This method must be called before any response body content is
                    579:      * written; if content has been written, this method throws an 
                    580:      * <code>IllegalStateException</code>.
                    581:      * @param size the preferred buffer size
                    582:      * @exception IllegalStateException        if this method is called after
                    583:      * content has been written
                    584:      * @see #getBufferSize
                    585:      * @see #flushBuffer
                    586:      * @see #isCommitted
                    587:      * @see #reset
                    588:      */
                    589:     public void setBufferSize(int size) {
                    590:        if (stream_state != STREAM_STATE_INITIAL) {
                    591:            throw new IllegalStateException("Stream already initialized");
                    592:        }
1.34      bmahe     593:        buffer_size = size < MIN_BUFFER_SIZE ? MIN_BUFFER_SIZE : size;
1.33      bmahe     594:     }
                    595: 
                    596:     /**
                    597:      * Returns the actual buffer size used for the response.  If no buffering
                    598:      * is used, this method returns 0.
                    599:      * @return the actual buffer size used
                    600:      * @see #setBufferSize
                    601:      * @see #flushBuffer
                    602:      * @see #isCommitted
                    603:      * @see #reset
                    604:      */
                    605:     public int getBufferSize() {
                    606:        return buffer_size;
                    607:     }
                    608: 
                    609:     /**
                    610:      * Forces any content in the buffer to be written to the client.  A call
                    611:      * to this method automatically commits the response, meaning the status 
                    612:      * code and headers will be written.
                    613:      * @see #setBufferSize
                    614:      * @see #getBufferSize
                    615:      * @see #isCommitted
                    616:      * @see #reset
                    617:      */
                    618:     public void flushBuffer() 
                    619:        throws IOException
                    620:     {
                    621:        if (output != null) {
1.34      bmahe     622:            if (stream_state == STREAM_WRITER_USED) {
                    623:                writer.flush();
                    624:            }
1.33      bmahe     625:            output.flush();
                    626:        }
                    627:     }
                    628: 
                    629:     /**
                    630:      * Returns a boolean indicating if the response has been
                    631:      * committed.  A commited response has already had its status 
                    632:      * code and headers written.
                    633:      * @return a boolean indicating if the response has been
                    634:      * committed
                    635:      * @see #setBufferSize
                    636:      * @see #getBufferSize
                    637:      * @see #flushBuffer
                    638:      * @see #reset
                    639:      */
                    640:     public boolean isCommitted() {
                    641:        if (output != null) {
                    642:            return output.isCommitted();
                    643:        } else {
                    644:            return false;
                    645:        }
                    646:     }
                    647: 
                    648:     /**
                    649:      * Clears any data that exists in the buffer as well as the status code and
                    650:      * headers.  If the response has been committed, this method throws an 
                    651:      * <code>IllegalStateException</code>.
                    652:      * @exception IllegalStateException  if the response has already been
                    653:      *                                   committed
                    654:      * @see #setBufferSize
                    655:      * @see #getBufferSize
                    656:      * @see #flushBuffer
                    657:      * @see #isCommitted
                    658:      */
                    659:     public void reset() {
                    660:        if (output != null) {
1.34      bmahe     661:            if (stream_state == STREAM_WRITER_USED) {
                    662:                writer.flush();
                    663:            }
1.33      bmahe     664:            output.reset();
                    665:        }
                    666:     }
                    667: 
                    668:     /**
                    669:      * Sets the locale of the response, setting the headers (including the
                    670:      * Content-Type's charset) as appropriate.  This method should be called
                    671:      * before a call to {@link #getWriter}.  By default, the response locale
                    672:      * is the default locale for the server.
                    673:      * @param loc  the locale of the response
                    674:      * @see #getLocale
                    675:      */
                    676:     public void setLocale(Locale locale) {
                    677:        if (locale == null) {
                    678:            return;
                    679:        }
                    680:        this.locale = locale;
                    681: 
                    682:        // content language
                    683:        String lang = locale.getLanguage();
                    684:        if (lang.length() > 0) {
                    685:            String array[] = new String[1];
                    686:            array[0] = lang;
                    687:            reply.setContentLanguage(array);
                    688:        }
                    689:        
                    690:        // content type (charset)
                    691:        String charset = org.w3c.www.mime.Utils.getCharset(locale);
                    692:        if (charset != null) {
                    693:            MimeType contentType = reply.getContentType();
                    694:            // override charset
                    695:            contentType.setParameter(CHARSET_PARAMETER, charset);
                    696:        }
                    697:     }
                    698: 
                    699:     /**
                    700:      * Returns the locale assigned to the response.
                    701:      * @see #setLocale
                    702:      */
                    703:     public Locale getLocale() {
                    704:        return locale;
                    705:     }
                    706: 
                    707:     public void addHeader(String name, String value) {
                    708:        String      lname = name.toLowerCase();
                    709:        HeaderValue hvalue = reply.getHeaderValue(lname);
                    710:        //
                    711:        // Horrible, Shame on us, I hate that
                    712:        //
                    713:        if (hvalue == null) {
                    714:            setHeader(name, value);
                    715:        } else if (hvalue instanceof HttpAcceptCharsetList) {
                    716:            HttpAcceptCharsetList acl = (HttpAcceptCharsetList) hvalue;
                    717:            acl.addCharset(HttpFactory.parseAcceptCharset(value));
                    718:        } else if (hvalue instanceof HttpAcceptEncodingList) {
                    719:            HttpAcceptEncodingList ael = (HttpAcceptEncodingList) hvalue;
                    720:            ael.addEncoding(HttpFactory.parseAcceptEncoding(value));
                    721:        } else if (hvalue instanceof HttpAcceptLanguageList) {
                    722:            HttpAcceptLanguageList all = (HttpAcceptLanguageList) hvalue;
                    723:            all.addLanguage(HttpFactory.parseAcceptLanguage(value));
                    724:        } else if (hvalue instanceof HttpAcceptList) {
                    725:            HttpAcceptList al = (HttpAcceptList) hvalue;
                    726:            al.addAccept(HttpFactory.parseAccept(value));
                    727:        } else if (hvalue instanceof HttpEntityTagList) {
                    728:            HttpEntityTagList etl = (HttpEntityTagList) hvalue;
                    729:            etl.addTag(HttpFactory.parseETag(value));
                    730:        } else if (hvalue instanceof HttpExtList) {
                    731:            HttpExtList el = (HttpExtList) hvalue;
                    732:            el.addHttpExt(new HttpExt(value, false));
                    733:        } else if (hvalue instanceof HttpCookieList) {
                    734:            // shouldn't be used, but who knows?
                    735:            HttpCookieList cl  = (HttpCookieList) hvalue;
                    736:            HttpCookieList ncl = HttpFactory.parseCookieList(value);
                    737:            HttpCookie scookies[] = ncl.getCookies();
                    738:            for (int i = 0 ; i < scookies.length ; i++) {
                    739:                HttpCookie cookie = scookies[i];
                    740:                cl.addCookie(cookie.getName(), cookie.getValue());
                    741:            }
                    742:        } else if (hvalue instanceof HttpParamList) {
                    743:            int idx = value.indexOf('=');
                    744:            if (idx != -1) {
                    745:                String pname  = value.substring(0, idx);
                    746:                String pvalue = value.substring(idx+1);
                    747:                HttpParamList pl = (HttpParamList) hvalue;
                    748:                pl.setParameter(pname, pvalue);
                    749:            }
                    750:        } else if (hvalue instanceof HttpRangeList) {
                    751:            HttpRangeList rl = (HttpRangeList) hvalue;
                    752:            rl.addRange(HttpFactory.parseRange(value));
                    753:        } else if (hvalue instanceof HttpSetCookieList) {
                    754:            HttpSetCookieList scl = (HttpSetCookieList) hvalue;
                    755:            HttpSetCookieList nscl = HttpFactory.parseSetCookieList(value);
                    756:            HttpSetCookie scookies[] = nscl.getSetCookies();
                    757:            for (int i = 0 ; i < scookies.length ; i++) {
                    758:                scl.addSetCookie(scookies[i]);
                    759:            }
                    760:        } else if (hvalue instanceof HttpTokenList) {
                    761:            ((HttpTokenList) hvalue).addToken(value, true);
                    762:        } else if (hvalue instanceof HttpWarningList) {
                    763:            HttpWarningList wl = (HttpWarningList) hvalue;
                    764:            wl.addWarning(HttpFactory.parseWarning(value));
                    765:        } else if (hvalue instanceof HttpString) {
                    766:            // this is the default type for unkown header
                    767:            // we don't know what it is, so just append
                    768:            HttpString s = (HttpString) hvalue;
                    769:            String string = (String) s.getValue();
                    770:            s.setValue(string+", "+value);
                    771:        } else {
                    772:            // not compliant with HTTP/1.1, override
                    773:            setHeader(name, value);
                    774:        }
                    775:     }
                    776: 
                    777:     public void addDateHeader(String name, long date) {
                    778:        addHeader(name, HttpFactory.makeDate(date).toExternalForm());
                    779:     }
                    780: 
                    781:     public void addIntHeader(String name, int value) {
                    782:        addHeader(name, String.valueOf(value));
                    783:     }
                    784: 
1.13      bmahe     785:     JigsawHttpServletResponse(Request request, Reply reply) {
1.33      bmahe     786:        this.request     = request;
                    787:        this.reply       = reply;
                    788:        this.buffer_size = DEFAULT_BUFFER_SIZE;
1.11      bmahe     789:     }
1.6       bmahe     790: 
1.1       abaird    791: }

Webmaster