001 package org.apache.fulcrum.upload;
002
003
004 /*
005 * Licensed to the Apache Software Foundation (ASF) under one
006 * or more contributor license agreements. See the NOTICE file
007 * distributed with this work for additional information
008 * regarding copyright ownership. The ASF licenses this file
009 * to you under the Apache License, Version 2.0 (the
010 * "License"); you may not use this file except in compliance
011 * with the License. You may obtain a copy of the License at
012 *
013 * http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing,
016 * software distributed under the License is distributed on an
017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018 * KIND, either express or implied. See the License for the
019 * specific language governing permissions and limitations
020 * under the License.
021 */
022
023
024 import java.io.File;
025 import java.io.IOException;
026 import java.util.List;
027
028 import javax.servlet.http.HttpServletRequest;
029
030 import org.apache.avalon.framework.activity.Initializable;
031 import org.apache.avalon.framework.configuration.Configurable;
032 import org.apache.avalon.framework.configuration.Configuration;
033 import org.apache.avalon.framework.context.Context;
034 import org.apache.avalon.framework.context.ContextException;
035 import org.apache.avalon.framework.context.Contextualizable;
036 import org.apache.avalon.framework.logger.AbstractLogEnabled;
037 import org.apache.avalon.framework.service.ServiceException;
038 import org.apache.commons.fileupload.FileItem;
039 import org.apache.commons.fileupload.FileItemIterator;
040 import org.apache.commons.fileupload.FileUploadException;
041 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
042 import org.apache.commons.fileupload.servlet.ServletFileUpload;
043
044 /**
045 * <p> This class is an implementation of {@link UploadService}.
046 *
047 * <p> Files will be stored in temporary disk storage on in memory,
048 * depending on request size, and will be available from the {@link
049 * org.apache.fulcrum.util.parser.ParameterParser} as {@link
050 * org.apache.commons.fileupload.FileItem} objects.
051 *
052 * <p>This implementation of {@link UploadService} handles multiple
053 * files per single html form, sent using multipart/form-data encoding
054 * type, as specified by RFC 1867. Use {@link
055 * org.apache.fulcrum.parser.ParameterParser#getFileItems(String)} to
056 * acquire an array of {@link
057 * org.apache.commons.fileupload.FileItem} objects associated with given
058 * html form.
059 *
060 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
061 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
062 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
063 * @version $Id: DefaultUploadService.java 1351114 2012-06-17 15:59:20Z tv $
064 */
065 public class DefaultUploadService
066 extends AbstractLogEnabled
067 implements UploadService, Initializable, Configurable, Contextualizable
068 {
069 /** A File Item Factory object for the actual uploading */
070 private DiskFileItemFactory itemFactory;
071
072 private int sizeThreshold;
073 private int sizeMax;
074
075 private String repositoryPath;
076 private String headerEncoding;
077
078 /**
079 * The application root
080 */
081 private String applicationRoot;
082
083 /**
084 * The maximum allowed upload size
085 */
086 public long getSizeMax()
087 {
088 return sizeMax;
089 }
090
091 /**
092 * The threshold beyond which files are written directly to disk.
093 */
094 public long getSizeThreshold()
095 {
096 return itemFactory.getSizeThreshold();
097 }
098
099 /**
100 * The location used to temporarily store files that are larger
101 * than the size threshold.
102 */
103 public String getRepository()
104 {
105 return itemFactory.getRepository().getAbsolutePath();
106 }
107
108 /**
109 * @return Returns the headerEncoding.
110 */
111 public String getHeaderEncoding()
112 {
113 return headerEncoding;
114 }
115
116 /**
117 * <p>Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
118 * compliant <code>multipart/form-data</code> stream.</p>
119 *
120 * @param req The servlet request to be parsed.
121 * @exception ServiceException Problems reading/parsing the
122 * request or storing the uploaded file(s).
123 */
124 public List<FileItem> parseRequest(HttpServletRequest req)
125 throws ServiceException
126 {
127 return parseRequest(req, this.sizeMax, this.itemFactory);
128 }
129
130 /**
131 * <p>Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
132 * compliant <code>multipart/form-data</code> stream.</p>
133 *
134 * @param req The servlet request to be parsed.
135 * @param path The location where the files should be stored.
136 * @exception ServiceException Problems reading/parsing the
137 * request or storing the uploaded file(s).
138 */
139 public List<FileItem> parseRequest(HttpServletRequest req, String path)
140 throws ServiceException
141 {
142 return parseRequest(req, this.sizeThreshold, this.sizeMax, path);
143 }
144
145 /**
146 * <p>Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
147 * compliant <code>multipart/form-data</code> stream.</p>
148 *
149 * @param req The servlet request to be parsed.
150 * @param sizeThreshold the max size in bytes to be stored in memory
151 * @param sizeMax the maximum allowed upload size in bytes
152 * @param path The location where the files should be stored.
153 * @exception ServiceException Problems reading/parsing the
154 * request or storing the uploaded file(s).
155 */
156 public List<FileItem> parseRequest(HttpServletRequest req, int sizeThreshold,
157 int sizeMax, String path)
158 throws ServiceException
159 {
160 return parseRequest(req, sizeMax, new DiskFileItemFactory(sizeThreshold, new File(path)));
161 }
162
163 /**
164 * <p>Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
165 * compliant <code>multipart/form-data</code> stream.</p>
166 *
167 * @param req The servlet request to be parsed.
168 * @param sizeMax the maximum allowed upload size in bytes
169 * @param factory the file item factory to use
170 *
171 * @exception ServiceException Problems reading/parsing the
172 * request or storing the uploaded file(s).
173 */
174 @SuppressWarnings("unchecked")
175 protected List<FileItem> parseRequest(HttpServletRequest req, int sizeMax, DiskFileItemFactory factory)
176 throws ServiceException
177 {
178 try
179 {
180 ServletFileUpload fileUpload = new ServletFileUpload(factory);
181 fileUpload.setSizeMax(sizeMax);
182 fileUpload.setHeaderEncoding(headerEncoding);
183 return fileUpload.parseRequest(req);
184 }
185 catch (FileUploadException e)
186 {
187 throw new ServiceException(UploadService.ROLE, e.getMessage(), e);
188 }
189 }
190
191 /**
192 * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
193 * compliant <code>multipart/form-data</code> stream.
194 *
195 * @param req The servlet request to be parsed.
196 *
197 * @return An iterator to instances of <code>FileItemStream</code>
198 * parsed from the request, in the order that they were
199 * transmitted.
200 *
201 * @throws ServiceException if there are problems reading/parsing
202 * the request or storing files. This
203 * may also be a network error while
204 * communicating with the client or a
205 * problem while storing the uploaded
206 * content.
207 */
208 public FileItemIterator getItemIterator(HttpServletRequest req) throws ServiceException
209 {
210 ServletFileUpload upload = new ServletFileUpload();
211 try
212 {
213 return upload.getItemIterator(req);
214 }
215 catch (FileUploadException e)
216 {
217 throw new ServiceException(UploadService.ROLE, e.getMessage(), e);
218 }
219 catch (IOException e)
220 {
221 throw new ServiceException(UploadService.ROLE, e.getMessage(), e);
222 }
223 }
224
225 /**
226 * Utility method that determines whether the request contains multipart
227 * content.
228 *
229 * @param req The servlet request to be evaluated. Must be non-null.
230 *
231 * @return <code>true</code> if the request is multipart;
232 * <code>false</code> otherwise.
233 */
234 public boolean isMultipart(HttpServletRequest req)
235 {
236 return ServletFileUpload.isMultipartContent(req);
237 }
238
239 /**
240 * @see org.apache.fulcrum.ServiceBroker#getRealPath(String)
241 */
242 private String getRealPath(String path)
243 {
244 String absolutePath = null;
245 if (applicationRoot == null)
246 {
247 absolutePath = new File(path).getAbsolutePath();
248 }
249 else
250 {
251 absolutePath = new File(applicationRoot, path).getAbsolutePath();
252 }
253
254 return absolutePath;
255 }
256
257 // ---------------- Avalon Lifecycle Methods ---------------------
258 /**
259 * Avalon component lifecycle method
260 */
261 public void configure(Configuration conf)
262 {
263 repositoryPath = conf.getAttribute(
264 UploadService.REPOSITORY_KEY,
265 UploadService.REPOSITORY_DEFAULT);
266
267 headerEncoding = conf.getAttribute(
268 UploadService.HEADER_ENCODING_KEY,
269 UploadService.HEADER_ENCODING_DEFAULT);
270
271 sizeMax = conf.getAttributeAsInteger(
272 UploadService.SIZE_MAX_KEY,
273 UploadService.SIZE_MAX_DEFAULT);
274
275 sizeThreshold = conf.getAttributeAsInteger(
276 UploadService.SIZE_THRESHOLD_KEY,
277 UploadService.SIZE_THRESHOLD_DEFAULT);
278 }
279
280 /**
281 * Avalon component lifecycle method
282 *
283 * Initializes the service.
284 *
285 * This method processes the repository path, to make it relative to the
286 * web application root, if necessary
287 */
288 public void initialize() throws Exception
289 {
290 // test for the existence of the path within the webapp directory.
291 // if it does not exist, assume the path was to be used as is.
292 String testPath = getRealPath(repositoryPath);
293 File testDir = new File(testPath);
294 if ( testDir.exists() )
295 {
296 repositoryPath = testPath;
297 }
298
299 getLogger().debug(
300 "Upload Service: REPOSITORY_KEY => " + repositoryPath);
301
302 itemFactory = new DiskFileItemFactory(sizeThreshold, new File(repositoryPath));
303 }
304
305 /**
306 * Avalon component lifecycle method
307 */
308 public void contextualize(Context context) throws ContextException
309 {
310 this.applicationRoot = context.get( "urn:avalon:home" ).toString();
311 }
312 }