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 * 043 * multi.startResponse("text/plain"); 044 * out.println("On your mark"); 045 * multi.endResponse(); 046 * 047 * try { Thread.sleep(1000); } catch (InterruptedException e) { } 048 * 049 * multi.startResponse("text/plain"); 050 * out.println("Get set"); 051 * multi.endResponse(); 052 * 053 * try { Thread.sleep(1000); } catch (InterruptedException e) { } 054 * 055 * multi.startResponse("image/gif"); 056 * ServletUtils.returnFile(req.getRealPath("/images/go.gif"), out); 057 * 058 * multi.finish(); 059 * </pre></blockquote> 060 * 061 * @see ServletUtils 062 * 063 * @author <b>Jason Hunter</b>, Copyright © 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}