001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.portlet.multipart; 018 019import java.util.List; 020import javax.portlet.ActionRequest; 021import javax.portlet.PortletContext; 022 023import org.apache.commons.fileupload.FileItem; 024import org.apache.commons.fileupload.FileItemFactory; 025import org.apache.commons.fileupload.FileUpload; 026import org.apache.commons.fileupload.FileUploadBase; 027import org.apache.commons.fileupload.FileUploadException; 028import org.apache.commons.fileupload.portlet.PortletFileUpload; 029 030import org.springframework.util.Assert; 031import org.springframework.web.multipart.MaxUploadSizeExceededException; 032import org.springframework.web.multipart.MultipartException; 033import org.springframework.web.multipart.commons.CommonsFileUploadSupport; 034import org.springframework.web.portlet.context.PortletContextAware; 035import org.springframework.web.portlet.util.PortletUtils; 036 037/** 038 * {@link PortletMultipartResolver} implementation for 039 * <a href="https://commons.apache.org/proper/commons-fileupload">Apache Commons FileUpload</a> 040 * 1.2 or above. 041 * 042 * <p>Provides "maxUploadSize", "maxInMemorySize" and "defaultEncoding" settings as 043 * bean properties (inherited from {@link CommonsFileUploadSupport}). See corresponding 044 * PortletFileUpload / DiskFileItemFactory properties ("sizeMax", "sizeThreshold", 045 * "headerEncoding") for details in terms of defaults and accepted values. 046 * 047 * <p>Saves temporary files to the portlet container's temporary directory. 048 * Needs to be initialized <i>either</i> by an application context <i>or</i> 049 * via the constructor that takes a PortletContext (for standalone usage). 050 * 051 * @author Juergen Hoeller 052 * @since 2.0 053 * @see #CommonsPortletMultipartResolver(javax.portlet.PortletContext) 054 * @see #setResolveLazily 055 * @see org.springframework.web.multipart.commons.CommonsMultipartResolver 056 * @see org.apache.commons.fileupload.portlet.PortletFileUpload 057 * @see org.apache.commons.fileupload.disk.DiskFileItemFactory 058 */ 059public class CommonsPortletMultipartResolver extends CommonsFileUploadSupport 060 implements PortletMultipartResolver, PortletContextAware { 061 062 private boolean resolveLazily = false; 063 064 065 /** 066 * Constructor for use as bean. Determines the portlet container's 067 * temporary directory via the PortletContext passed in as through the 068 * PortletContextAware interface (typically by an ApplicationContext). 069 * @see #setPortletContext 070 * @see org.springframework.web.portlet.context.PortletContextAware 071 */ 072 public CommonsPortletMultipartResolver() { 073 super(); 074 } 075 076 /** 077 * Constructor for standalone usage. Determines the portlet container's 078 * temporary directory via the given PortletContext. 079 * @param portletContext the PortletContext to use 080 */ 081 public CommonsPortletMultipartResolver(PortletContext portletContext) { 082 this(); 083 setPortletContext(portletContext); 084 } 085 086 087 /** 088 * Set whether to resolve the multipart request lazily at the time of 089 * file or parameter access. 090 * <p>Default is "false", resolving the multipart elements immediately, throwing 091 * corresponding exceptions at the time of the {@link #resolveMultipart} call. 092 * Switch this to "true" for lazy multipart parsing, throwing parse exceptions 093 * once the application attempts to obtain multipart files or parameters. 094 */ 095 public void setResolveLazily(boolean resolveLazily) { 096 this.resolveLazily = resolveLazily; 097 } 098 099 /** 100 * Initialize the underlying {@code org.apache.commons.fileupload.portlet.PortletFileUpload} 101 * instance. Can be overridden to use a custom subclass, e.g. for testing purposes. 102 * @return the new PortletFileUpload instance 103 */ 104 @Override 105 protected FileUpload newFileUpload(FileItemFactory fileItemFactory) { 106 return new PortletFileUpload(fileItemFactory); 107 } 108 109 @Override 110 public void setPortletContext(PortletContext portletContext) { 111 if (!isUploadTempDirSpecified()) { 112 getFileItemFactory().setRepository(PortletUtils.getTempDir(portletContext)); 113 } 114 } 115 116 117 @Override 118 public boolean isMultipart(ActionRequest request) { 119 return (request != null && PortletFileUpload.isMultipartContent(request)); 120 } 121 122 @Override 123 public MultipartActionRequest resolveMultipart(final ActionRequest request) throws MultipartException { 124 Assert.notNull(request, "Request must not be null"); 125 if (this.resolveLazily) { 126 return new DefaultMultipartActionRequest(request) { 127 @Override 128 protected void initializeMultipart() { 129 MultipartParsingResult parsingResult = parseRequest(request); 130 setMultipartFiles(parsingResult.getMultipartFiles()); 131 setMultipartParameters(parsingResult.getMultipartParameters()); 132 setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes()); 133 } 134 }; 135 } 136 else { 137 MultipartParsingResult parsingResult = parseRequest(request); 138 return new DefaultMultipartActionRequest(request, parsingResult.getMultipartFiles(), 139 parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes()); 140 } 141 } 142 143 /** 144 * Parse the given portlet request, resolving its multipart elements. 145 * @param request the request to parse 146 * @return the parsing result 147 * @throws MultipartException if multipart resolution failed. 148 */ 149 protected MultipartParsingResult parseRequest(ActionRequest request) throws MultipartException { 150 String encoding = determineEncoding(request); 151 FileUpload fileUpload = prepareFileUpload(encoding); 152 try { 153 List<FileItem> fileItems = ((PortletFileUpload) fileUpload).parseRequest(request); 154 return parseFileItems(fileItems, encoding); 155 } 156 catch (FileUploadBase.SizeLimitExceededException ex) { 157 throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex); 158 } 159 catch (FileUploadBase.FileSizeLimitExceededException ex) { 160 throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex); 161 } 162 catch (FileUploadException ex) { 163 throw new MultipartException("Failed to parse multipart portlet request", ex); 164 } 165 } 166 167 /** 168 * Determine the encoding for the given request. 169 * Can be overridden in subclasses. 170 * <p>The default implementation checks the request encoding, 171 * falling back to the default encoding specified for this resolver. 172 * @param request current portlet request 173 * @return the encoding for the request (never {@code null}) 174 * @see javax.portlet.ActionRequest#getCharacterEncoding 175 * @see #setDefaultEncoding 176 */ 177 protected String determineEncoding(ActionRequest request) { 178 String encoding = request.getCharacterEncoding(); 179 if (encoding == null) { 180 encoding = getDefaultEncoding(); 181 } 182 return encoding; 183 } 184 185 @Override 186 public void cleanupMultipart(MultipartActionRequest request) { 187 if (request != null) { 188 try { 189 cleanupFileItems(request.getMultiFileMap()); 190 } 191 catch (Throwable ex) { 192 logger.warn("Failed to perform multipart cleanup for portlet request", ex); 193 } 194 } 195 } 196 197}