    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
    015package org.apache.tapestry5.upload.internal.services;
    016
    017import org.apache.commons.fileupload.FileItem;
    018import org.apache.commons.fileupload.FileItemFactory;
    019import org.apache.commons.fileupload.FileUploadException;
    020import org.apache.commons.fileupload.servlet.ServletFileUpload;
    021import org.apache.tapestry5.SymbolConstants;
    022import org.apache.tapestry5.ioc.annotations.Symbol;
    023import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    024import org.apache.tapestry5.ioc.services.ThreadCleanupListener;
    025import org.apache.tapestry5.upload.services.MultipartDecoder;
    026import org.apache.tapestry5.upload.services.UploadSymbols;
    027import org.apache.tapestry5.upload.services.UploadedFile;
    028
    029import javax.servlet.http.HttpServletRequest;
    030import java.io.UnsupportedEncodingException;
    031import java.util.Collections;
    032import java.util.List;
    033import java.util.Map;
    034
    035/**
    036 * Implementation of multipart decoder for servlets. This implementation is perthread scope.
    037 */
    038public 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    @Override
    072    public UploadedFile getFileUpload(String parameterName)
    073    {
    074        return uploads.get(parameterName);
    075    }
    076
    077    @Override
    078    public HttpServletRequest decode(HttpServletRequest request)
    079    {
    080        try
    081        {
    082            request.setCharacterEncoding(requestEncoding);
    083        } catch (UnsupportedEncodingException ex)
    084        {
    085            throw new RuntimeException(ex);
    086        }
    087
    088        List<FileItem> fileItems = parseRequest(request);
    089
    090        return processFileItems(request, fileItems);
    091    }
    092
    093    @Override
    094    public void threadDidCleanup()
    095    {
    096        for (UploadedFileItem uploaded : uploads.values())
    097        {
    098            uploaded.cleanup();
    099        }
    100    }
    101
    102    @SuppressWarnings("unchecked")
    103    protected List<FileItem> parseRequest(HttpServletRequest request)
    104    {
    105        try
    106        {
    107            return createFileUpload().parseRequest(request);
    108        } catch (FileUploadException ex)
    109        {
    110            uploadException = ex;
    111
    112            return Collections.emptyList();
    113        }
    114    }
    115
    116    protected ServletFileUpload createFileUpload()
    117    {
    118        ServletFileUpload upload = new ServletFileUpload(fileItemFactory);
    119
    120        // set maximum file upload size
    121        upload.setSizeMax(maxRequestSize);
    122        upload.setFileSizeMax(maxFileSize);
    123
    124        return upload;
    125    }
    126
    127    protected HttpServletRequest processFileItems(HttpServletRequest request, List<FileItem> fileItems)
    128    {
    129        if (uploadException == null && fileItems.isEmpty())
    130        {
    131            return request;
    132        }
    133
    134        ParametersServletRequestWrapper wrapper = new ParametersServletRequestWrapper(request);
    135
    136        // First add parameters from the request
    137        for (Object e : request.getParameterMap().entrySet())
    138        {
    139            Map.Entry<String, String[]> ee = (Map.Entry<String, String[]>) e;
    140            for (String s : ee.getValue())
    141                wrapper.addParameter(ee.getKey(), s);
    142        }
    143
    144        for (FileItem item : fileItems)
    145        {
    146            if (item.isFormField())
    147            {
    148                String fieldValue;
    149
    150                try
    151                {
    152
    153                    fieldValue = item.getString(requestEncoding);
    154                } catch (UnsupportedEncodingException ex)
    155                {
    156                    throw new RuntimeException(ex);
    157                }
    158
    159                wrapper.addParameter(item.getFieldName(), fieldValue);
    160            } else
    161            {
    162                wrapper.addParameter(item.getFieldName(), item.getName());
    163                addUploadedFile(item.getFieldName(), new UploadedFileItem(item));
    164            }
    165        }
    166
    167        return wrapper;
    168    }
    169
    170    protected void addUploadedFile(String name, UploadedFileItem file)
    171    {
    172        uploads.put(name, file);
    173    }
    174
    175    @Override
    176    public FileUploadException getUploadException()
    177    {
    178        return uploadException;
    179    }
    180}