001: /*
002: * $Id: $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts.actions;
022:
023: import org.apache.struts.action.ActionForm;
024: import org.apache.struts.action.ActionForward;
025: import org.apache.struts.action.ActionMapping;
026:
027: import javax.servlet.ServletContext;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030:
031: import java.io.BufferedInputStream;
032: import java.io.File;
033: import java.io.FileInputStream;
034: import java.io.IOException;
035: import java.io.InputStream;
036: import java.io.OutputStream;
037:
038: /**
039: * This is an abstract base class that minimizes the amount of special coding
040: * that needs to be written to download a file. All that is required to use
041: * this class is to extend it and implement the <code>getStreamInfo()</code>
042: * method so that it returns the relevant information for the file (or other
043: * stream) to be downloaded. Optionally, the <code>getBufferSize()</code>
044: * method may be overridden to customize the size of the buffer used to
045: * transfer the file.
046: *
047: * @since Struts 1.2.6
048: */
049: public abstract class DownloadAction extends BaseAction {
050: /**
051: * If the <code>getBufferSize()</code> method is not overridden, this is
052: * the buffer size that will be used to transfer the data to the servlet
053: * output stream.
054: */
055: protected static final int DEFAULT_BUFFER_SIZE = 4096;
056:
057: /**
058: * Returns the information on the file, or other stream, to be downloaded
059: * by this action. This method must be implemented by an extending class.
060: *
061: * @param mapping The ActionMapping used to select this instance.
062: * @param form The optional ActionForm bean for this request (if
063: * any).
064: * @param request The HTTP request we are processing.
065: * @param response The HTTP response we are creating.
066: * @return The information for the file to be downloaded.
067: * @throws Exception if an exception occurs.
068: */
069: protected abstract StreamInfo getStreamInfo(ActionMapping mapping,
070: ActionForm form, HttpServletRequest request,
071: HttpServletResponse response) throws Exception;
072:
073: /**
074: * Returns the size of the buffer to be used in transferring the data to
075: * the servlet output stream. This method may be overridden by an
076: * extending class in order to customize the buffer size.
077: *
078: * @return The size of the transfer buffer, in bytes.
079: */
080: protected int getBufferSize() {
081: return DEFAULT_BUFFER_SIZE;
082: }
083:
084: /**
085: * Process the specified HTTP request, and create the corresponding HTTP
086: * response (or forward to another web component that will create it).
087: * Return an <code>ActionForward</code> instance describing where and how
088: * control should be forwarded, or <code>null</code> if the response has
089: * already been completed.
090: *
091: * @param mapping The ActionMapping used to select this instance.
092: * @param form The optional ActionForm bean for this request (if
093: * any).
094: * @param request The HTTP request we are processing.
095: * @param response The HTTP response we are creating.
096: * @return The forward to which control should be transferred, or
097: * <code>null</code> if the response has been completed.
098: * @throws Exception if an exception occurs.
099: */
100: public ActionForward execute(ActionMapping mapping,
101: ActionForm form, HttpServletRequest request,
102: HttpServletResponse response) throws Exception {
103: StreamInfo info = getStreamInfo(mapping, form, request,
104: response);
105: String contentType = info.getContentType();
106: InputStream stream = info.getInputStream();
107:
108: try {
109: response.setContentType(contentType);
110: copy(stream, response.getOutputStream());
111: } finally {
112: if (stream != null) {
113: stream.close();
114: }
115: }
116:
117: // Tell Struts that we are done with the response.
118: return null;
119: }
120:
121: /**
122: * Copy bytes from an <code>InputStream</code> to an
123: * <code>OutputStream</code>.
124: *
125: * @param input The <code>InputStream</code> to read from.
126: * @param output The <code>OutputStream</code> to write to.
127: * @return the number of bytes copied
128: * @throws IOException In case of an I/O problem
129: */
130: public int copy(InputStream input, OutputStream output)
131: throws IOException {
132: byte[] buffer = new byte[getBufferSize()];
133: int count = 0;
134: int n = 0;
135:
136: while (-1 != (n = input.read(buffer))) {
137: output.write(buffer, 0, n);
138: count += n;
139: }
140:
141: return count;
142: }
143:
144: /**
145: * The information on a file, or other stream, to be downloaded by the
146: * <code>DownloadAction</code>.
147: */
148: public static interface StreamInfo {
149: /**
150: * Returns the content type of the stream to be downloaded.
151: *
152: * @return The content type of the stream.
153: */
154: String getContentType();
155:
156: /**
157: * Returns an input stream on the content to be downloaded. This
158: * stream will be closed by the <code>DownloadAction</code>.
159: *
160: * @return The input stream for the content to be downloaded.
161: * @throws IOException if an error occurs
162: */
163: InputStream getInputStream() throws IOException;
164: }
165:
166: /**
167: * A concrete implementation of the <code>StreamInfo</code> interface
168: * which simplifies the downloading of a file from the disk.
169: */
170: public static class FileStreamInfo implements StreamInfo {
171: /**
172: * The content type for this stream.
173: */
174: private String contentType;
175:
176: /**
177: * The file to be downloaded.
178: */
179: private File file;
180:
181: /**
182: * Constructs an instance of this class, based on the supplied
183: * parameters.
184: *
185: * @param contentType The content type of the file.
186: * @param file The file to be downloaded.
187: */
188: public FileStreamInfo(String contentType, File file) {
189: this .contentType = contentType;
190: this .file = file;
191: }
192:
193: /**
194: * Returns the content type of the stream to be downloaded.
195: *
196: * @return The content type of the stream.
197: */
198: public String getContentType() {
199: return this .contentType;
200: }
201:
202: /**
203: * Returns an input stream on the file to be downloaded. This stream
204: * will be closed by the <code>DownloadAction</code>.
205: *
206: * @return The input stream for the file to be downloaded.
207: * @throws IOException if an error occurs
208: */
209: public InputStream getInputStream() throws IOException {
210: FileInputStream fis = new FileInputStream(file);
211: BufferedInputStream bis = new BufferedInputStream(fis);
212:
213: return bis;
214: }
215: }
216:
217: /**
218: * A concrete implementation of the <code>StreamInfo</code> interface
219: * which simplifies the downloading of a web application resource.
220: */
221: public static class ResourceStreamInfo implements StreamInfo {
222: /**
223: * The content type for this stream.
224: */
225: private String contentType;
226:
227: /**
228: * The servlet context for the resource to be downloaded.
229: */
230: private ServletContext context;
231:
232: /**
233: * The path to the resource to be downloaded.
234: */
235: private String path;
236:
237: /**
238: * Constructs an instance of this class, based on the supplied
239: * parameters.
240: *
241: * @param contentType The content type of the file.
242: * @param context The servlet context for the resource.
243: * @param path The path to the resource to be downloaded.
244: */
245: public ResourceStreamInfo(String contentType,
246: ServletContext context, String path) {
247: this .contentType = contentType;
248: this .context = context;
249: this .path = path;
250: }
251:
252: /**
253: * Returns the content type of the stream to be downloaded.
254: *
255: * @return The content type of the stream.
256: */
257: public String getContentType() {
258: return this .contentType;
259: }
260:
261: /**
262: * Returns an input stream on the resource to be downloaded. This
263: * stream will be closed by the <code>DownloadAction</code>.
264: *
265: * @return The input stream for the resource to be downloaded.
266: * @throws IOException if an error occurs
267: */
268: public InputStream getInputStream() throws IOException {
269: return context.getResourceAsStream(path);
270: }
271: }
272: }
|