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

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

Webmaster