| package org.apache.fulcrum.upload; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.List; |
| |
| import javax.portlet.ActionRequest; |
| import javax.servlet.http.HttpServletRequest; |
| |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| import org.apache.avalon.framework.logger.AbstractLogEnabled; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.commons.fileupload.FileItem; |
| import org.apache.commons.fileupload.FileItemIterator; |
| import org.apache.commons.fileupload.FileUploadException; |
| import org.apache.commons.fileupload.disk.DiskFileItemFactory; |
| import org.apache.commons.fileupload.portlet.PortletFileUpload; |
| import org.apache.commons.fileupload.servlet.ServletFileUpload; |
| |
| /** |
| * <p> |
| * This class is an implementation of {@link UploadService}. |
| * |
| * <p> |
| * Files will be stored in temporary disk storage on in memory, depending on |
| * request size, and will be available from the |
| * <code>org.apache.fulcrum.util.parser.ParameterParser</code> as |
| * <code>org.apache.commons.fileupload.FileItem</code> objects. |
| * |
| * <p> |
| * This implementation of {@link UploadService} handles multiple files per |
| * single html form, sent using multipart/form-data encoding type, as specified |
| * by RFC 1867. Use |
| * <code>org.apache.fulcrum.parser.ParameterParser#getFileItems(String)</code> to |
| * acquire an array of <code>org.apache.commons.fileupload.FileItem</code> objects |
| * associated with given html form. |
| * |
| * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a> |
| * @author <a href="mailto:dlr@collab.net">Daniel Rall</a> |
| * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> |
| * @version $Id$ |
| */ |
| public class DefaultUploadService extends AbstractLogEnabled |
| implements UploadService, Initializable, Configurable, Contextualizable { |
| /** A File Item Factory object for the actual uploading */ |
| private DiskFileItemFactory itemFactory; |
| |
| private int sizeThreshold; |
| private int sizeMax; |
| |
| private String repositoryPath; |
| private String headerEncoding; |
| |
| /** |
| * The application root |
| */ |
| private String applicationRoot; |
| |
| /** |
| * The maximum allowed upload size |
| */ |
| @Override |
| public long getSizeMax() { |
| return sizeMax; |
| } |
| |
| /** |
| * The threshold beyond which files are written directly to disk. |
| */ |
| @Override |
| public long getSizeThreshold() { |
| return itemFactory.getSizeThreshold(); |
| } |
| |
| /** |
| * The location used to temporarily store files that are larger than the size |
| * threshold. |
| */ |
| @Override |
| public String getRepository() { |
| return itemFactory.getRepository().getAbsolutePath(); |
| } |
| |
| /** |
| * @return Returns the headerEncoding. |
| */ |
| @Override |
| public String getHeaderEncoding() { |
| return headerEncoding; |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The servlet request to be parsed. |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| @Override |
| public List<FileItem> parseRequest(HttpServletRequest req) throws ServiceException { |
| return parseRequest(req, this.sizeMax, this.itemFactory); |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The servlet request to be parsed. |
| * @param path The location where the files should be stored. |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| @Override |
| public List<FileItem> parseRequest(HttpServletRequest req, String path) throws ServiceException { |
| return parseRequest(req, this.sizeThreshold, this.sizeMax, path); |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The servlet request to be parsed. |
| * @param sizeThreshold the max size in bytes to be stored in memory |
| * @param sizeMax the maximum allowed upload size in bytes |
| * @param path The location where the files should be stored. |
| * @return list of file items |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| @Override |
| public List<FileItem> parseRequest(HttpServletRequest req, int sizeThreshold, int sizeMax, String path) |
| throws ServiceException { |
| return parseRequest(req, sizeMax, new DiskFileItemFactory(sizeThreshold, new File(path))); |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The servlet request to be parsed. |
| * @param sizeMax the maximum allowed upload size in bytes |
| * @param factory the file item factory to use |
| * @return list of file items |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| protected List<FileItem> parseRequest(HttpServletRequest req, int sizeMax, DiskFileItemFactory factory) |
| throws ServiceException { |
| try { |
| ServletFileUpload fileUpload = new ServletFileUpload(factory); |
| fileUpload.setSizeMax(sizeMax); |
| fileUpload.setHeaderEncoding(headerEncoding); |
| return fileUpload.parseRequest(req); |
| } catch (FileUploadException e) { |
| throw new ServiceException(UploadService.ROLE, e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> |
| * compliant <code>multipart/form-data</code> stream. |
| * |
| * @param req The servlet request to be parsed. |
| * |
| * @return An iterator to instances of <code>FileItemStream</code> parsed from |
| * the request, in the order that they were transmitted. |
| * |
| * @throws ServiceException if there are problems reading/parsing the request or |
| * storing files. This may also be a network error |
| * while communicating with the client or a problem |
| * while storing the uploaded content. |
| */ |
| @Override |
| public FileItemIterator getItemIterator(HttpServletRequest req) throws ServiceException { |
| ServletFileUpload upload = new ServletFileUpload(); |
| try { |
| return upload.getItemIterator(req); |
| } catch (FileUploadException e) { |
| throw new ServiceException(UploadService.ROLE, e.getMessage(), e); |
| } catch (IOException e) { |
| throw new ServiceException(UploadService.ROLE, e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The portlet request to be parsed. |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| @Override |
| public List<FileItem> parseRequest(ActionRequest req) throws ServiceException { |
| return parseRequest(req, this.sizeMax, this.itemFactory); |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The portlet request to be parsed. |
| * @param path The location where the files should be stored. |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| @Override |
| public List<FileItem> parseRequest(ActionRequest req, String path) throws ServiceException { |
| return parseRequest(req, this.sizeThreshold, this.sizeMax, path); |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The portlet request to be parsed. |
| * @param sizeThreshold the max size in bytes to be stored in memory |
| * @param sizeMax the maximum allowed upload size in bytes |
| * @param path The location where the files should be stored. |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| @Override |
| public List<FileItem> parseRequest(ActionRequest req, int sizeThreshold, int sizeMax, String path) |
| throws ServiceException { |
| return parseRequest(req, sizeMax, new DiskFileItemFactory(sizeThreshold, new File(path))); |
| } |
| |
| /** |
| * <p> |
| * Parses a <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant |
| * <code>multipart/form-data</code> stream. |
| * </p> |
| * |
| * @param req The portlet request to be parsed. |
| * @param sizeMax the maximum allowed upload size in bytes |
| * @param factory the file item factory to use |
| * @return The list of FileItem parts uploaded |
| * @throws ServiceException Problems reading/parsing the request or storing the |
| * uploaded file(s). |
| */ |
| protected List<FileItem> parseRequest(ActionRequest req, int sizeMax, DiskFileItemFactory factory) |
| throws ServiceException { |
| try { |
| PortletFileUpload fileUpload = new PortletFileUpload(factory); |
| fileUpload.setSizeMax(sizeMax); |
| fileUpload.setHeaderEncoding(headerEncoding); |
| return fileUpload.parseRequest(req); |
| } catch (FileUploadException e) { |
| throw new ServiceException(UploadService.ROLE, e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> |
| * compliant <code>multipart/form-data</code> stream. |
| * |
| * @param req The portlet request to be parsed. |
| * |
| * @return An iterator to instances of <code>FileItemStream</code> parsed from |
| * the request, in the order that they were transmitted. |
| * |
| * @throws ServiceException if there are problems reading/parsing the request or |
| * storing files. This may also be a network error |
| * while communicating with the client or a problem |
| * while storing the uploaded content. |
| */ |
| @Override |
| public FileItemIterator getItemIterator(ActionRequest req) throws ServiceException { |
| PortletFileUpload upload = new PortletFileUpload(); |
| try { |
| return upload.getItemIterator(req); |
| } catch (FileUploadException e) { |
| throw new ServiceException(UploadService.ROLE, e.getMessage(), e); |
| } catch (IOException e) { |
| throw new ServiceException(UploadService.ROLE, e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Utility method that determines whether the request contains multipart |
| * content. |
| * |
| * @param req The servlet request to be evaluated. Must be non-null. |
| * |
| * @return <code>true</code> if the request is multipart; <code>false</code> |
| * otherwise. |
| */ |
| @Override |
| public boolean isMultipart(HttpServletRequest req) { |
| return ServletFileUpload.isMultipartContent(req); |
| } |
| |
| /** |
| * Utility method that determines whether the request contains multipart |
| * content. |
| * |
| * @param req The portlet request to be evaluated. Must be non-null. |
| * |
| * @return <code>true</code> if the request is multipart; <code>false</code> |
| * otherwise. |
| */ |
| @Override |
| public boolean isMultipart(ActionRequest req) { |
| return PortletFileUpload.isMultipartContent(req); |
| } |
| |
| /** |
| * @see org.apache.fulcrum.ServiceBroker#getRealPath(String) |
| */ |
| private String getRealPath(String path) { |
| String absolutePath = null; |
| if (applicationRoot == null) { |
| absolutePath = new File(path).getAbsolutePath(); |
| } else { |
| absolutePath = new File(applicationRoot, path).getAbsolutePath(); |
| } |
| |
| return absolutePath; |
| } |
| |
| // ---------------- Avalon Lifecycle Methods --------------------- |
| /** |
| * Avalon component lifecycle method |
| */ |
| @Override |
| public void configure(Configuration conf) { |
| repositoryPath = conf.getAttribute(UploadService.REPOSITORY_KEY, UploadService.REPOSITORY_DEFAULT); |
| |
| headerEncoding = conf.getAttribute(UploadService.HEADER_ENCODING_KEY, UploadService.HEADER_ENCODING_DEFAULT); |
| |
| sizeMax = conf.getAttributeAsInteger(UploadService.SIZE_MAX_KEY, UploadService.SIZE_MAX_DEFAULT); |
| |
| sizeThreshold = conf.getAttributeAsInteger(UploadService.SIZE_THRESHOLD_KEY, |
| UploadService.SIZE_THRESHOLD_DEFAULT); |
| } |
| |
| /** |
| * Avalon component lifecycle method |
| * |
| * Initializes the service. |
| * |
| * This method processes the repository path, to make it relative to the web |
| * application root, if necessary |
| */ |
| @Override |
| public void initialize() throws Exception { |
| // test for the existence of the path within the webapp directory. |
| // if it does not exist, assume the path was to be used as is. |
| String testPath = getRealPath(repositoryPath); |
| File testDir = new File(testPath); |
| if (testDir.exists()) { |
| repositoryPath = testPath; |
| } |
| |
| getLogger().debug("Upload Service: REPOSITORY_KEY => " + repositoryPath); |
| |
| itemFactory = new DiskFileItemFactory(sizeThreshold, new File(repositoryPath)); |
| } |
| |
| /** |
| * Avalon component lifecycle method |
| */ |
| @Override |
| public void contextualize(Context context) throws ContextException { |
| this.applicationRoot = context.get("urn:avalon:home").toString(); |
| } |
| } |