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}