View Javadoc

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