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

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

Webmaster