    001    // Copyright 2007, 2008, 2010, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.upload.internal.services;
    016
    017    import org.apache.commons.fileupload.FileItem;
    018    import org.apache.commons.fileupload.FileItemFactory;
    019    import org.apache.commons.fileupload.FileUploadException;
    020    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    021    import org.apache.tapestry5.SymbolConstants;
    022    import org.apache.tapestry5.ioc.annotations.Symbol;
    023    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    024    import org.apache.tapestry5.ioc.services.ThreadCleanupListener;
    025    import org.apache.tapestry5.upload.services.MultipartDecoder;
    026    import org.apache.tapestry5.upload.services.UploadSymbols;
    027    import org.apache.tapestry5.upload.services.UploadedFile;
    028
    029    import javax.servlet.http.HttpServletRequest;
    030    import java.io.UnsupportedEncodingException;
    031    import java.util.Collections;
    032    import java.util.List;
    033    import java.util.Map;
    034
    035    /**
    036     * Implementation of multipart decoder for servlets. This implementation is perthread scope.
    037     */
    038    public class MultipartDecoderImpl implements MultipartDecoder, ThreadCleanupListener
    039    {
    040        private final Map<String, UploadedFileItem> uploads = CollectionFactory.newMap();
    041
    042        private final FileItemFactory fileItemFactory;
    043
    044        private final long maxRequestSize;
    045
    046        private final long maxFileSize;
    047
    048        private final String requestEncoding;
    049
    050        private FileUploadException uploadException;
    051
    052        public MultipartDecoderImpl(
    053
    054                FileItemFactory fileItemFactory,
    055
    056                @Symbol(UploadSymbols.REQUESTSIZE_MAX)
    057                long maxRequestSize,
    058
    059                @Symbol(UploadSymbols.FILESIZE_MAX)
    060                long maxFileSize,
    061
    062                @Symbol(SymbolConstants.CHARSET)
    063                String requestEncoding)
    064        {
    065            this.fileItemFactory = fileItemFactory;
    066            this.maxRequestSize = maxRequestSize;
    067            this.maxFileSize = maxFileSize;
    068            this.requestEncoding = requestEncoding;
    069        }
    070
    071        public UploadedFile getFileUpload(String parameterName)
    072        {
    073            return uploads.get(parameterName);
    074        }
    075
    076        public HttpServletRequest decode(HttpServletRequest request)
    077        {
    078            try
    079            {
    080                request.setCharacterEncoding(requestEncoding);
    081            } catch (UnsupportedEncodingException ex)
    082            {
    083                throw new RuntimeException(ex);
    084            }
    085
    086            List<FileItem> fileItems = parseRequest(request);
    087
    088            return processFileItems(request, fileItems);
    089        }
    090
    091        public void threadDidCleanup()
    092        {
    093            for (UploadedFileItem uploaded : uploads.values())
    094            {
    095                uploaded.cleanup();
    096            }
    097        }
    098
    099        @SuppressWarnings("unchecked")
    100        protected List<FileItem> parseRequest(HttpServletRequest request)
    101        {
    102            try
    103            {
    104                return createFileUpload().parseRequest(request);
    105            } catch (FileUploadException ex)
    106            {
    107                uploadException = ex;
    108
    109                return Collections.emptyList();
    110            }
    111        }
    112
    113        protected ServletFileUpload createFileUpload()
    114        {
    115            ServletFileUpload upload = new ServletFileUpload(fileItemFactory);
    116
    117            // set maximum file upload size
    118            upload.setSizeMax(maxRequestSize);
    119            upload.setFileSizeMax(maxFileSize);
    120
    121            return upload;
    122        }
    123
    124        protected HttpServletRequest processFileItems(HttpServletRequest request, List<FileItem> fileItems)
    125        {
    126            if (uploadException == null && fileItems.isEmpty())
    127            {
    128                return request;
    129            }
    130
    131            ParametersServletRequestWrapper wrapper = new ParametersServletRequestWrapper(request);
    132
    133            // First add parameters from the request
    134            for (Object e : request.getParameterMap().entrySet())
    135            {
    136                Map.Entry<String, String[]> ee = (Map.Entry<String, String[]>) e;
    137                for (String s : ee.getValue())
    138                    wrapper.addParameter(ee.getKey(), s);
    139            }
    140
    141            for (FileItem item : fileItems)
    142            {
    143                if (item.isFormField())
    144                {
    145                    String fieldValue;
    146
    147                    try
    148                    {
    149
    150                        fieldValue = item.getString(requestEncoding);
    151                    } catch (UnsupportedEncodingException ex)
    152                    {
    153                        throw new RuntimeException(ex);
    154                    }
    155
    156                    wrapper.addParameter(item.getFieldName(), fieldValue);
    157                } else
    158                {
    159                    wrapper.addParameter(item.getFieldName(), item.getName());
    160                    addUploadedFile(item.getFieldName(), new UploadedFileItem(item));
    161                }
    162            }
    163
    164            return wrapper;
    165        }
    166
    167        protected void addUploadedFile(String name, UploadedFileItem file)
    168        {
    169            uploads.put(name, file);
    170        }
    171
    172        public FileUploadException getUploadException()
    173        {
    174            return uploadException;
    175        }
    176    }