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.util.List;
026    
027    import javax.servlet.http.HttpServletRequest;
028    
029    import org.apache.avalon.framework.activity.Initializable;
030    import org.apache.avalon.framework.configuration.Configurable;
031    import org.apache.avalon.framework.configuration.Configuration;
032    import org.apache.avalon.framework.context.Context;
033    import org.apache.avalon.framework.context.ContextException;
034    import org.apache.avalon.framework.context.Contextualizable;
035    import org.apache.avalon.framework.logger.AbstractLogEnabled;
036    import org.apache.avalon.framework.service.ServiceException;
037    import org.apache.commons.fileupload.FileUploadException;
038    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
039    import org.apache.commons.fileupload.servlet.ServletFileUpload;
040    
041    /**
042     * <p> This class is an implementation of {@link UploadService}.
043     *
044     * <p> Files will be stored in temporary disk storage on in memory,
045     * depending on request size, and will be available from the {@link
046     * org.apache.fulcrum.util.parser.ParameterParser} as {@link
047     * org.apache.fulcrum.upload.FileItem}s.
048     *
049     * <p>This implementation of {@link UploadService} handles multiple
050     * files per single html widget, sent using multipar/mixed encoding
051     * type, as specified by RFC 1867.  Use {@link
052     * org.apache.fulcrum.util.parser.ParameterParser#getFileItems(String)} to
053     * acquire an array of {@link
054     * org.apache.fulcrum.upload.FileItem}s associated with given
055     * html widget.
056     *
057     * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
058     * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
059     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
060     * @version $Id: DefaultUploadService.java 732085 2009-01-06 19:58:22Z tv $
061     */
062    public class DefaultUploadService
063        extends AbstractLogEnabled
064        implements UploadService, Initializable, Configurable, Contextualizable
065    {
066        /** A File Item Factory object for the actual uploading */
067        private DiskFileItemFactory itemFactory;
068    
069        private int sizeThreshold;
070        private int sizeMax;
071    
072        private String repositoryPath;
073        private String headerEncoding;
074    
075        /**
076         * The application root
077         */
078        private String applicationRoot;
079    
080        /**
081         * The maximum allowed upload size
082         */
083        public long getSizeMax()
084        {
085            return sizeMax;
086        }
087    
088        /**
089         * The threshold beyond which files are written directly to disk.
090         */
091        public long getSizeThreshold()
092        {
093            return itemFactory.getSizeThreshold();
094        }
095    
096        /**
097         * The location used to temporarily store files that are larger
098         * than the size threshold.
099         */
100        public String getRepository()
101        {
102            return itemFactory.getRepository().getAbsolutePath();
103        }
104    
105        /**
106         * @return Returns the headerEncoding.
107         */
108        public String getHeaderEncoding() 
109        {
110            return headerEncoding;
111        }
112        
113        /**
114         * <p>Parses a <a href="http://rf.cx/rfc1867.html">RFC 1867</a>
115         * compliant <code>multipart/form-data</code> stream.</p>
116         *
117         * @param req The servlet request to be parsed.
118         * @exception ServiceException Problems reading/parsing the
119         * request or storing the uploaded file(s).
120         */
121        public List parseRequest(HttpServletRequest req)
122            throws ServiceException
123        {
124            return parseRequest(req, this.sizeMax, this.itemFactory);
125        }
126    
127        /**
128         * <p>Parses a <a href="http://rf.cx/rfc1867.html">RFC 1867</a>
129         * compliant <code>multipart/form-data</code> stream.</p>
130         *
131         * @param req The servlet request to be parsed.
132         * @param path The location where the files should be stored.
133         * @exception ServiceException Problems reading/parsing the
134         * request or storing the uploaded file(s).
135         */
136        public List parseRequest(HttpServletRequest req, String path)
137            throws ServiceException
138        {
139            return parseRequest(req, this.sizeThreshold, this.sizeMax, path);
140        }
141    
142        /**
143         * <p>Parses a <a href="http://rf.cx/rfc1867.html">RFC 1867</a>
144         * compliant <code>multipart/form-data</code> stream.</p>
145         *
146         * @param req The servlet request to be parsed.
147         * @param sizeThreshold the max size in bytes to be stored in memory
148         * @param sizeMax the maximum allowed upload size in bytes
149         * @param path The location where the files should be stored.
150         * @exception ServiceException Problems reading/parsing the
151         * request or storing the uploaded file(s).
152         */
153        public List parseRequest(HttpServletRequest req, int sizeThreshold,
154                                      int sizeMax, String path)
155                throws ServiceException
156        {
157            return parseRequest(req, sizeMax, new DiskFileItemFactory(sizeThreshold, new File(path)));
158        }
159    
160        /**
161         * <p>Parses a <a href="http://rf.cx/rfc1867.html">RFC 1867</a>
162         * compliant <code>multipart/form-data</code> stream.</p>
163         *
164         * @param req The servlet request to be parsed.
165         * @param sizeMax the maximum allowed upload size in bytes
166         * @param factory the file item factory to use
167         * 
168         * @exception ServiceException Problems reading/parsing the
169         * request or storing the uploaded file(s).
170         */
171        private List parseRequest(HttpServletRequest req, int sizeMax, DiskFileItemFactory factory)
172                throws ServiceException
173        {
174            try
175            {
176                ServletFileUpload fileUpload = new ServletFileUpload(factory);
177                fileUpload.setSizeMax(sizeMax);
178                fileUpload.setHeaderEncoding(headerEncoding);
179                return fileUpload.parseRequest(req);
180            }
181            catch (FileUploadException e)
182            {
183                throw new ServiceException(UploadService.ROLE, e.getMessage(), e);
184            }
185        }
186    
187        /**
188         * @see org.apache.fulcrum.ServiceBroker#getRealPath(String)
189         */
190        private String getRealPath(String path)
191        {
192            String absolutePath = null;
193            if (applicationRoot == null)
194            {
195                absolutePath = new File(path).getAbsolutePath();
196            }
197            else
198            {
199                absolutePath = new File(applicationRoot, path).getAbsolutePath();
200            }
201    
202            return absolutePath;
203        }
204    
205        // ---------------- Avalon Lifecycle Methods ---------------------
206        /**
207         * Avalon component lifecycle method
208         */
209        public void configure(Configuration conf)
210        {
211            repositoryPath = conf.getAttribute(
212                    UploadService.REPOSITORY_KEY,
213                    UploadService.REPOSITORY_DEFAULT);
214    
215            headerEncoding = conf.getAttribute(
216                    UploadService.HEADER_ENCODING_KEY,
217                    UploadService.HEADER_ENCODING_DEFAULT);
218            
219            sizeMax = conf.getAttributeAsInteger(
220                    UploadService.SIZE_MAX_KEY,
221                    UploadService.SIZE_MAX_DEFAULT);
222    
223            sizeThreshold = conf.getAttributeAsInteger(
224                    UploadService.SIZE_THRESHOLD_KEY,
225                    UploadService.SIZE_THRESHOLD_DEFAULT);
226        }
227    
228        /**
229         * Initializes the service.
230         *
231         * This method processes the repository path, to make it relative to the
232         * web application root, if necessary
233         */
234        public void initialize() throws Exception
235        {
236            // test for the existence of the path within the webapp directory.
237            // if it does not exist, assume the path was to be used as is.
238            String testPath = getRealPath(repositoryPath);
239            File testDir = new File(testPath);
240            if ( testDir.exists() )
241            {
242                repositoryPath = testPath;
243            }
244    
245            getLogger().debug(
246                    "Upload Service: REPOSITORY_KEY => " + repositoryPath);
247    
248            itemFactory = new DiskFileItemFactory(sizeThreshold, new File(repositoryPath));
249        }
250    
251        public void contextualize(Context context) throws ContextException
252        {
253            this.applicationRoot = context.get( "urn:avalon:home" ).toString();
254        }
255    }