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    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}