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    }