001// Copyright (C) 1998-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
002// All rights reserved.  Use of this class is limited.
003// Please see the LICENSE for more information.
004
005package com.oreilly.servlet;
006
007import java.io.*;
008import javax.servlet.*;
009import javax.servlet.http.*;
010
011/** 
012 * A utility class to generate <tt>multipart/x-mixed-replace</tt> responses,
013 * the kind of responses that implement server push.  Note that Microsoft
014 * Internet Explorer does not understand this sort of response.
015 * <p>
016 * To use this class, first construct a new MultipartResponse 
017 * passing to its constructor the servlet's response parameter.  
018 * MultipartResponse uses the response object to fetch the 
019 * servlet's output stream and to set the response's content type.
020 * <p>
021 * Then, for each page of content, begin by calling <tt>startResponse()</tt>
022 * passing in the content type for that page.  Send the content for the 
023 * page by writing to the output stream as usual.  A call to 
024 * <tt>endResponse()</tt> ends the page and flushes the content so the 
025 * client can see it.  At this point a <tt>sleep()</tt> or other delay
026 * can be added until the next page is ready for sending.
027 * <p>
028 * The call to <tt>endResponse()</tt> is optional.  The 
029 * <tt>startResponse()</tt> method knows whether the last response has 
030 * been ended, and ends it itself if necessary.  However, it's wise to 
031 * call <tt>endResponse()</tt> if there's to be a delay between the
032 * time one response ends and the next begins.  It lets the client display 
033 * the latest response during the time it waits for the next one.
034 * <p>
035 * Finally, after each response page has been sent, a call to the 
036 * <tt>finish()</tt> method finishes the multipart response and sends a 
037 * code telling the client there will be no more responses.
038 * <p>
039 * For example:
040 * <blockquote><pre>
041 * MultipartResponse multi = new MultipartResponse(res);
042 * &nbsp;
043 * multi.startResponse("text/plain");
044 * out.println("On your mark");
045 * multi.endResponse();
046 * &nbsp;
047 * try { Thread.sleep(1000); } catch (InterruptedException e) { }
048 * &nbsp;
049 * multi.startResponse("text/plain");
050 * out.println("Get set");
051 * multi.endResponse();
052 * &nbsp;
053 * try { Thread.sleep(1000); } catch (InterruptedException e) { }
054 * &nbsp;
055 * multi.startResponse("image/gif");
056 * ServletUtils.returnFile(req.getRealPath("/images/go.gif"), out);
057 * &nbsp;
058 * multi.finish();
059 * </pre></blockquote>
060 *
061 * @see ServletUtils
062 *
063 * @author <b>Jason Hunter</b>, Copyright &#169; 1998
064 * @version 1.0, 98/09/18
065 */
066public class MultipartResponse {
067
068  HttpServletResponse res;
069  ServletOutputStream out;
070  boolean endedLastResponse = true;
071
072  /**
073   * Constructs a new MultipartResponse to send content to the given
074   * servlet response.
075   *
076   * @param response the servlet response
077   * @exception IOException if an I/O error occurs
078   */
079  public MultipartResponse(HttpServletResponse response) throws IOException {
080    // Save the response object and output stream
081    res = response;
082    out = res.getOutputStream();
083
084    // Set things up
085    res.setContentType("multipart/x-mixed-replace;boundary=End");
086    out.println();
087    out.println("--End");
088  }
089
090  /**
091   * Begins a single response with the specified content type.
092   * This method knows whether the last response has been ended, and 
093   * ends it itself if necessary.
094   *
095   * @param contentType the content type of this response part
096   * @exception IOException if an I/O error occurs
097   */
098  public void startResponse(String contentType) throws IOException {
099    // End the last response if necessary
100    if (!endedLastResponse) {
101      endResponse();
102    }
103    // Start the next one
104    out.println("Content-type: " + contentType);
105    out.println();
106    endedLastResponse = false;
107  }
108
109  /**
110   * Ends a single response.  Flushes the output.
111   *
112   * @exception IOException if an I/O error occurs
113   */
114  public void endResponse() throws IOException {
115    // End the last response, and flush so the client sees the content
116    out.println();
117    out.println("--End");
118    out.flush();
119    endedLastResponse = true;
120  }
121
122  /**
123   * Finishes the multipart response.  Sends a code telling the client
124   * there will be no more responses and flushes the output.
125   *
126   * @exception IOException if an I/O error occurs
127   */
128  public void finish() throws IOException {
129    out.println("--End--");
130    out.flush();
131  }
132}