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.commons.util.CollectionFactory;
022import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
023import org.apache.tapestry5.ioc.annotations.Symbol;
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(TapestryHttpSymbolConstants.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    protected List<FileItem> parseRequest(HttpServletRequest request)
103    {
104        try
105        {
106            return createFileUpload().parseRequest(request);
107        } catch (FileUploadException ex)
108        {
109            uploadException = ex;
110
111            return Collections.emptyList();
112        }
113    }
114
115    protected ServletFileUpload createFileUpload()
116    {
117        ServletFileUpload upload = new ServletFileUpload(fileItemFactory);
118
119        // set maximum file upload size
120        upload.setSizeMax(maxRequestSize);
121        upload.setFileSizeMax(maxFileSize);
122
123        return upload;
124    }
125
126    protected HttpServletRequest processFileItems(HttpServletRequest request, List<FileItem> fileItems)
127    {
128        if (uploadException == null && fileItems.isEmpty())
129        {
130            return request;
131        }
132
133        ParametersServletRequestWrapper wrapper = new ParametersServletRequestWrapper(request);
134
135        // First add parameters from the request
136        for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet())
137        {
138            for (String s : e.getValue())
139                wrapper.addParameter(e.getKey(), s);
140        }
141
142        for (FileItem item : fileItems)
143        {
144            if (item.isFormField())
145            {
146                String fieldValue;
147
148                try
149                {
150
151                    fieldValue = item.getString(requestEncoding);
152                } catch (UnsupportedEncodingException ex)
153                {
154                    throw new RuntimeException(ex);
155                }
156
157                wrapper.addParameter(item.getFieldName(), fieldValue);
158            } else
159            {
160                wrapper.addParameter(item.getFieldName(), item.getName());
161                addUploadedFile(item.getFieldName(), new UploadedFileItem(item));
162            }
163        }
164
165        return wrapper;
166    }
167
168    protected void addUploadedFile(String name, UploadedFileItem file)
169    {
170        uploads.put(name, file);
171    }
172
173    @Override
174    public FileUploadException getUploadException()
175    {
176        return uploadException;
177    }
178}