View Javadoc

1   /*
2    * $Id: $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts.actions;
22  
23  import org.apache.struts.action.ActionForm;
24  import org.apache.struts.action.ActionForward;
25  import org.apache.struts.action.ActionMapping;
26  
27  import javax.servlet.ServletContext;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpServletResponse;
30  
31  import java.io.BufferedInputStream;
32  import java.io.File;
33  import java.io.FileInputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.OutputStream;
37  
38  /**
39   * This is an abstract base class that minimizes the amount of special coding
40   * that needs to be written to download a file. All that is required to use
41   * this class is to extend it and implement the <code>getStreamInfo()</code>
42   * method so that it returns the relevant information for the file (or other
43   * stream) to be downloaded. Optionally, the <code>getBufferSize()</code>
44   * method may be overridden to customize the size of the buffer used to
45   * transfer the file.
46   *
47   * @since Struts 1.2.6
48   */
49  public abstract class DownloadAction extends BaseAction {
50      /**
51       * If the <code>getBufferSize()</code> method is not overridden, this is
52       * the buffer size that will be used to transfer the data to the servlet
53       * output stream.
54       */
55      protected static final int DEFAULT_BUFFER_SIZE = 4096;
56  
57      /**
58       * Returns the information on the file, or other stream, to be downloaded
59       * by this action. This method must be implemented by an extending class.
60       *
61       * @param mapping  The ActionMapping used to select this instance.
62       * @param form     The optional ActionForm bean for this request (if
63       *                 any).
64       * @param request  The HTTP request we are processing.
65       * @param response The HTTP response we are creating.
66       * @return The information for the file to be downloaded.
67       * @throws Exception if an exception occurs.
68       */
69      protected abstract StreamInfo getStreamInfo(ActionMapping mapping,
70          ActionForm form, HttpServletRequest request,
71          HttpServletResponse response)
72          throws Exception;
73  
74      /**
75       * Returns the size of the buffer to be used in transferring the data to
76       * the servlet output stream. This method may be overridden by an
77       * extending class in order to customize the buffer size.
78       *
79       * @return The size of the transfer buffer, in bytes.
80       */
81      protected int getBufferSize() {
82          return DEFAULT_BUFFER_SIZE;
83      }
84  
85      /**
86       * Process the specified HTTP request, and create the corresponding HTTP
87       * response (or forward to another web component that will create it).
88       * Return an <code>ActionForward</code> instance describing where and how
89       * control should be forwarded, or <code>null</code> if the response has
90       * already been completed.
91       *
92       * @param mapping  The ActionMapping used to select this instance.
93       * @param form     The optional ActionForm bean for this request (if
94       *                 any).
95       * @param request  The HTTP request we are processing.
96       * @param response The HTTP response we are creating.
97       * @return The forward to which control should be transferred, or
98       *         <code>null</code> if the response has been completed.
99       * @throws Exception if an exception occurs.
100      */
101     public ActionForward execute(ActionMapping mapping, ActionForm form,
102         HttpServletRequest request, HttpServletResponse response)
103         throws Exception {
104         StreamInfo info = getStreamInfo(mapping, form, request, 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()
164             throws IOException;
165     }
166 
167     /**
168      * A concrete implementation of the <code>StreamInfo</code> interface
169      * which simplifies the downloading of a file from the disk.
170      */
171     public static class FileStreamInfo implements StreamInfo {
172         /**
173          * The content type for this stream.
174          */
175         private String contentType;
176 
177         /**
178          * The file to be downloaded.
179          */
180         private File file;
181 
182         /**
183          * Constructs an instance of this class, based on the supplied
184          * parameters.
185          *
186          * @param contentType The content type of the file.
187          * @param file        The file to be downloaded.
188          */
189         public FileStreamInfo(String contentType, File file) {
190             this.contentType = contentType;
191             this.file = file;
192         }
193 
194         /**
195          * Returns the content type of the stream to be downloaded.
196          *
197          * @return The content type of the stream.
198          */
199         public String getContentType() {
200             return this.contentType;
201         }
202 
203         /**
204          * Returns an input stream on the file to be downloaded. This stream
205          * will be closed by the <code>DownloadAction</code>.
206          *
207          * @return The input stream for the file to be downloaded.
208          * @throws IOException if an error occurs
209          */
210         public InputStream getInputStream()
211             throws IOException {
212             FileInputStream fis = new FileInputStream(file);
213             BufferedInputStream bis = new BufferedInputStream(fis);
214 
215             return bis;
216         }
217     }
218 
219     /**
220      * A concrete implementation of the <code>StreamInfo</code> interface
221      * which simplifies the downloading of a web application resource.
222      */
223     public static class ResourceStreamInfo implements StreamInfo {
224         /**
225          * The content type for this stream.
226          */
227         private String contentType;
228 
229         /**
230          * The servlet context for the resource to be downloaded.
231          */
232         private ServletContext context;
233 
234         /**
235          * The path to the resource to be downloaded.
236          */
237         private String path;
238 
239         /**
240          * Constructs an instance of this class, based on the supplied
241          * parameters.
242          *
243          * @param contentType The content type of the file.
244          * @param context     The servlet context for the resource.
245          * @param path        The path to the resource to be downloaded.
246          */
247         public ResourceStreamInfo(String contentType, ServletContext context,
248             String path) {
249             this.contentType = contentType;
250             this.context = context;
251             this.path = path;
252         }
253 
254         /**
255          * Returns the content type of the stream to be downloaded.
256          *
257          * @return The content type of the stream.
258          */
259         public String getContentType() {
260             return this.contentType;
261         }
262 
263         /**
264          * Returns an input stream on the resource to be downloaded. This
265          * stream will be closed by the <code>DownloadAction</code>.
266          *
267          * @return The input stream for the resource to be downloaded.
268          * @throws IOException if an error occurs
269          */
270         public InputStream getInputStream()
271             throws IOException {
272             return context.getResourceAsStream(path);
273         }
274     }
275 }