Revision: 380
Author:   tfenne
Date:     2006-08-14 17:18:37 -0700 (Mon, 14 Aug 2006)
ViewCVS:  http://svn.sourceforge.net/stripes/?rev=380&view=rev

Log Message:
-----------
Changes to implement the pluggability of the file upload code, and include an 
apache commons implementation.

Modified Paths:
--------------
    trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java
    trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java
    trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java
    
trunk/stripes/src/net/sourceforge/stripes/controller/FileUploadLimitExceededException.java
    trunk/stripes/src/net/sourceforge/stripes/controller/StripesFilter.java
    
trunk/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java
    trunk/stripes/stripes.iml

Added Paths:
-----------
    trunk/stripes/lib/test/commons-fileupload-1.1.1.jar
    trunk/stripes/lib/test/commons-io-1.2.jar
    trunk/stripes/src/net/sourceforge/stripes/controller/multipart/
    
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CommonsMultipartWrapper.java
    
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CosMultipartWrapper.java
    
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/DefaultMultipartWrapperFactory.java
    
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapper.java
    
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapperFactory.java
Added: trunk/stripes/lib/test/commons-fileupload-1.1.1.jar
===================================================================
(Binary files differ)


Property changes on: trunk/stripes/lib/test/commons-fileupload-1.1.1.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/stripes/lib/test/commons-io-1.2.jar
===================================================================
(Binary files differ)


Property changes on: trunk/stripes/lib/test/commons-io-1.2.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java 
2006-08-14 18:24:12 UTC (rev 379)
+++ trunk/stripes/src/net/sourceforge/stripes/config/Configuration.java 
2006-08-15 00:18:37 UTC (rev 380)
@@ -25,6 +25,7 @@
 import net.sourceforge.stripes.format.FormatterFactory;
 import net.sourceforge.stripes.controller.Interceptor;
 import net.sourceforge.stripes.controller.LifecycleStage;
+import net.sourceforge.stripes.controller.multipart.MultipartWrapperFactory;
 import net.sourceforge.stripes.exception.ExceptionHandler;
 
 import javax.servlet.ServletContext;
@@ -175,4 +176,13 @@
      * @return ExceptionHandler an instance of ExceptionHandler
      */
     ExceptionHandler getExceptionHandler();
+
+    /**
+     * Returns an instance of MultipartWrapperFactory that can be used by 
Stripes to construct
+     * MultipartWrapper instances for dealing with multipart requests (those 
containing file
+     * uploads).
+     *
+     * @return MultipartWrapperFactory an instance of the wrapper factory
+     */
+    MultipartWrapperFactory getMultipartWrapperFactory();
 }

Modified: 
trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java  
2006-08-14 18:24:12 UTC (rev 379)
+++ trunk/stripes/src/net/sourceforge/stripes/config/DefaultConfiguration.java  
2006-08-15 00:18:37 UTC (rev 380)
@@ -24,6 +24,8 @@
 import net.sourceforge.stripes.controller.Intercepts;
 import net.sourceforge.stripes.controller.LifecycleStage;
 import net.sourceforge.stripes.controller.NameBasedActionResolver;
+import net.sourceforge.stripes.controller.multipart.MultipartWrapperFactory;
+import 
net.sourceforge.stripes.controller.multipart.DefaultMultipartWrapperFactory;
 import net.sourceforge.stripes.exception.DefaultExceptionHandler;
 import net.sourceforge.stripes.exception.ExceptionHandler;
 import net.sourceforge.stripes.exception.StripesRuntimeException;
@@ -80,6 +82,7 @@
     private PopulationStrategy populationStrategy;
     private Map<LifecycleStage,Collection<Interceptor>> interceptors;
     private ExceptionHandler exceptionHandler;
+    private MultipartWrapperFactory multipartWrapperFactory;
 
     /** Gratefully accepts the BootstrapPropertyResolver handed to the 
Configuration. */
     public void setBootstrapPropertyResolver(BootstrapPropertyResolver 
resolver) {
@@ -152,6 +155,12 @@
                 this.exceptionHandler.init(this);
             }
 
+            this.multipartWrapperFactory = initMultipartWrapperFactory();
+            if (this.multipartWrapperFactory == null) {
+                this.multipartWrapperFactory = new 
DefaultMultipartWrapperFactory();
+                this.multipartWrapperFactory.init(this);
+            }
+
             this.interceptors = initInterceptors();
             if (this.interceptors == null) {
                 this.interceptors = new HashMap<LifecycleStage, 
Collection<Interceptor>>();
@@ -296,6 +305,21 @@
     protected ExceptionHandler initExceptionHandler() { return null; }
 
     /**
+     * Returns an instance of MultipartWrapperFactory that can be used by 
Stripes to construct
+     * MultipartWrapper instances for dealing with multipart requests (those 
containing file
+     * uploads).
+     *
+     * @return MultipartWrapperFactory an instance of the wrapper factory
+     */
+    public MultipartWrapperFactory getMultipartWrapperFactory() {
+        return this.multipartWrapperFactory;
+    }
+
+
+    /** Allows subclasses to initialize a non-default MultipartWrapperFactory. 
*/
+    protected MultipartWrapperFactory initMultipartWrapperFactory() { return 
null; }
+
+    /**
      * Returns a list of interceptors that should be executed around the 
lifecycle stage
      * indicated.  By default returns a single element list containing the 
      * [EMAIL PROTECTED] BeforeAfterMethodInterceptor}.

Modified: 
trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java  
2006-08-14 18:24:12 UTC (rev 379)
+++ trunk/stripes/src/net/sourceforge/stripes/config/RuntimeConfiguration.java  
2006-08-15 00:18:37 UTC (rev 380)
@@ -20,6 +20,7 @@
 import net.sourceforge.stripes.controller.Interceptor;
 import net.sourceforge.stripes.controller.Intercepts;
 import net.sourceforge.stripes.controller.LifecycleStage;
+import net.sourceforge.stripes.controller.multipart.MultipartWrapperFactory;
 import net.sourceforge.stripes.exception.StripesRuntimeException;
 import net.sourceforge.stripes.exception.ExceptionHandler;
 import net.sourceforge.stripes.format.FormatterFactory;
@@ -85,6 +86,9 @@
     /** The Configuration Key for looking up the name of the ExceptionHandler 
class */
     public static final String EXCEPTION_HANDLER = "ExceptionHandler.Class";
 
+    /** The Configuration Key for looking up the name of the 
MultipartWrapperFactory class */
+    public static final String MULTIPART_WRAPPER_FACTORY = 
"MultipartWrapperFactory.Class";
+
     /** The Configuration Key for looking up the comma separated list of 
interceptor classes. */
     public static final String INTERCEPTOR_LIST = "Interceptor.Classes";
 
@@ -139,6 +143,11 @@
         return initializeComponent(ExceptionHandler.class, EXCEPTION_HANDLER);
     }
 
+    /** Looks for a class name in config and uses that to create the 
component. */
+    @Override protected MultipartWrapperFactory initMultipartWrapperFactory() {
+        return initializeComponent(MultipartWrapperFactory.class, 
MULTIPART_WRAPPER_FACTORY);
+    }
+
     /**
      * Looks for a list of class names separated by commas under the 
configuration key
      * [EMAIL PROTECTED] #INTERCEPTOR_LIST}.  White space surrounding the 
class names is trimmed,

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/FileUploadLimitExceededException.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/FileUploadLimitExceededException.java
  2006-08-14 18:24:12 UTC (rev 379)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/FileUploadLimitExceededException.java
  2006-08-15 00:18:37 UTC (rev 380)
@@ -23,8 +23,8 @@
  * @author Tim Fennell
  */
 public class FileUploadLimitExceededException extends StripesServletException {
-    private int maximum;
-    private int posted;
+    private long maximum;
+    private long posted;
 
     /**
      * Constructs a new exception that contains the limt that was violated, 
and the size
@@ -33,15 +33,15 @@
      * @param max the current post size limit
      * @param posted the size of the post
      */
-    public FileUploadLimitExceededException(int max, int posted) {
+    public FileUploadLimitExceededException(long max, long posted) {
         super("File post limit exceeded. Limit: " + max + " bytes. Posted: " + 
posted + " bytes.");
         this.maximum = max;
         this.posted = posted;
     }
 
     /** Gets the limit in bytes for HTTP POSTs. */
-    public int getMaximum() { return maximum; }
+    public long getMaximum() { return maximum; }
 
     /** The size in bytes of the HTTP POST. */
-    public int getPosted() { return posted; }
+    public long getPosted() { return posted; }
 }
\ No newline at end of file

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/StripesFilter.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/controller/StripesFilter.java     
2006-08-14 18:24:12 UTC (rev 379)
+++ trunk/stripes/src/net/sourceforge/stripes/controller/StripesFilter.java     
2006-08-15 00:18:37 UTC (rev 380)
@@ -17,8 +17,8 @@
 import net.sourceforge.stripes.config.BootstrapPropertyResolver;
 import net.sourceforge.stripes.config.Configuration;
 import net.sourceforge.stripes.config.RuntimeConfiguration;
-import net.sourceforge.stripes.exception.StripesServletException;
 import net.sourceforge.stripes.exception.StripesRuntimeException;
+import net.sourceforge.stripes.exception.StripesServletException;
 import net.sourceforge.stripes.util.Log;
 import net.sourceforge.stripes.util.ReflectUtil;
 
@@ -30,14 +30,11 @@
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Iterator;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * The Stripes filter is used to ensure that all requests coming to a Stripes 
application
@@ -52,21 +49,12 @@
     /** Key used to lookup the name of the Configuration class used to 
configure Stripes. */
     public static final String CONFIG_CLASS = "Configuration.Class";
 
-    /** Key used to lookup the name of the maximum post size. */
-    public static final String MAX_POST = "FileUpload.MaximumPostSize";
-
-    /** Path to a temporary directory that will be used to process file 
uploads. */
-    protected String temporaryDirectoryPath;
-
     /** Log used throughout the class. */
     private static Log log = Log.getInstance(StripesFilter.class);
 
     /** The configuration instance for Stripes. */
     private Configuration configuration;
 
-    /** Stores the maximum post size for a form upload request. */
-    private int maxPostSize = 1024 * 1024 * 10;
-
     /**
      * A place to stash the Configuration object so that other classes in 
Stripes can access it
      * without resorting to ferrying it, or the request, to every class that 
needs access to the
@@ -107,40 +95,6 @@
         this.configuration.setBootstrapPropertyResolver(bootstrap);
         this.configuration.init();
 
-        // Figure out where the temp directory is, and store that info
-        File tempDir = (File) 
filterConfig.getServletContext().getAttribute("javax.servlet.context.tempdir");
-        if (tempDir != null) {
-            this.temporaryDirectoryPath = tempDir.getAbsolutePath();
-        }
-        else {
-            this.temporaryDirectoryPath = System.getProperty("java.io.tmpdir");
-        }
-
-        // See if a maximum post size was configured
-        String limit = bootstrap.getProperty(MAX_POST);
-        if (limit != null) {
-            Pattern pattern = Pattern.compile("([\\d,]+)([kKmMgG]?).*");
-            Matcher matcher = pattern.matcher(limit);
-            if (!matcher.matches()) {
-                log.warn("Did not understand value of configuration parameter 
", MAX_POST,
-                         " You supplied: ", limit, ". Valid values are any 
string of numbers ",
-                         "optionally followed by (case insensitive) 
[k|kb|m|mb|g|gb]. ",
-                         "Default value of ", this.maxPostSize, " bytes will 
be used instead.");
-            }
-            else {
-                String digits = matcher.group(1);
-                String suffix = matcher.group(2).toLowerCase();
-                int number = Integer.parseInt(digits);
-
-                if ("k".equals(suffix)) { number = number * 1024; }
-                else if ("m".equals(suffix)) {  number = number * 1024 * 1024; 
}
-                else if ("g".equals(suffix)) { number = number * 1024 * 1024 * 
1024; }
-
-                this.maxPostSize = number;
-                log.info("Configured file upload post size limit: ", number, " 
bytes.");
-            }
-        }
-
         Package pkg = getClass().getPackage();
         log.info("Stripes Initialization Complete. Version: ", 
pkg.getSpecificationVersion(),
                  ", Build: ", pkg.getImplementationVersion());
@@ -246,11 +200,7 @@
      */
     protected StripesRequestWrapper wrapRequest(HttpServletRequest 
servletRequest)
             throws StripesServletException {
-        String tempDirPath = getTempDirectoryPath();
-
-        StripesRequestWrapper request =
-                new StripesRequestWrapper(servletRequest, tempDirPath, 
this.maxPostSize);
-        return request;
+        return new StripesRequestWrapper(servletRequest);
     }
 
     /**
@@ -292,12 +242,6 @@
         }
     }
 
-    /** Returns the path to the temporary directory that is used to store file 
uploads. */
-    protected String getTempDirectoryPath() {
-        return this.temporaryDirectoryPath;
-    }
-
-
     /** Does nothing. */
     public void destroy() {
         // Do nothing

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java 
    2006-08-14 18:24:12 UTC (rev 379)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/StripesRequestWrapper.java 
    2006-08-15 00:18:37 UTC (rev 380)
@@ -14,23 +14,21 @@
  */
 package net.sourceforge.stripes.controller;
 
-import com.oreilly.servlet.MultipartRequest;
 import net.sourceforge.stripes.action.FileBean;
+import net.sourceforge.stripes.controller.multipart.MultipartWrapper;
 import net.sourceforge.stripes.exception.StripesServletException;
 
+import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.ServletRequest;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.Map;
-import java.util.Locale;
-import java.util.Collections;
 import java.util.List;
-import java.util.ArrayList;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
 
 /**
  * HttpServletRequestWrapper that is used to make the file upload 
functionality transparent.
@@ -40,15 +38,9 @@
  *
  * @author Tim Fennell
  */
[EMAIL PROTECTED]("CLASS") // Request has some deprecated methods we don't 
touch, so don't warn us!
 public class StripesRequestWrapper extends HttpServletRequestWrapper {
-
-    /** Pattern used to parse useful info out of the IOException cos throws. */
-    private static Pattern exceptionPattern =
-            Pattern.compile("Posted content length of (\\d*) exceeds limit of 
(\\d*)");
-
     /** The Multipart Request that parses out all the pieces. */
-    private MultipartRequest multipart;
+    private MultipartWrapper multipart;
 
     /** The Locale that is going to be used to process the request. */
     private Locale locale;
@@ -91,55 +83,38 @@
      * just wrapped and the behaviour is unchanged.
      *
      * @param request the HttpServletRequest to wrap
-     * @param pathToTempDir the path to a temporary directory in which to 
store files during upload
-     * @param maxTotalPostSize a limit on how much can be uploaded in a single 
request. Note that
      *        this is not a file size limit, but a post size limit.
      * @throws FileUploadLimitExceededException if the total post size is 
larger than the limit
      * @throws StripesServletException if any other error occurs constructing 
the wrapper
      */
-    public StripesRequestWrapper(HttpServletRequest request,
-                                 String pathToTempDir,
-                                 int maxTotalPostSize) throws 
StripesServletException {
+    public StripesRequestWrapper(HttpServletRequest request) throws 
StripesServletException {
         super(request);
 
         String contentType = request.getContentType();
         if (contentType != null && 
contentType.startsWith("multipart/form-data")) {
-            constructMultipartRequest(request, pathToTempDir, 
maxTotalPostSize);
+            constructMultipartWrapper(request);
         }
     }
 
     /**
-     * Responsible for constructing the MultipartRequest object and setting it 
on to
+     * Responsible for constructing the MultipartWrapper object and setting it 
on to
      * the instnace variable 'multipart'.
      *
      * @param request the HttpServletRequest to wrap
-     * @param pathToTempDir the path to a temporary directory in which to 
store files during upload
-     * @param maxTotalPostSize a limit on how much can be uploaded in a single 
request. Note that
      *        this is not a file size limit, but a post size limit.
      * @throws StripesServletException if any other error occurs constructing 
the wrapper
      */
-    protected void constructMultipartRequest(HttpServletRequest request,
-                                             String pathToTempDir,
-                                             int maxTotalPostSize) throws 
StripesServletException {
+    protected void constructMultipartWrapper(HttpServletRequest request) 
throws StripesServletException {
         try {
-            this.multipart = new MultipartRequest(request,
-                                                  pathToTempDir,
-                                                  maxTotalPostSize,
-                                                  
request.getCharacterEncoding());
+            this.multipart =
+                    
StripesFilter.getConfiguration().getMultipartWrapperFactory().wrap(request);
         }
         catch (IOException e) {
-            Matcher matcher = exceptionPattern.matcher(e.getMessage());
-
-            if (matcher.matches()) {
-                throw new 
FileUploadLimitExceededException(Integer.parseInt(matcher.group(2)),
-                                                           
Integer.parseInt(matcher.group(1)));
-            }
-            else {
-                throw new StripesServletException("Could not construct request 
wrapper.", e);
-            }
+            throw new StripesServletException("Could not construct request 
wrapper.", e);
         }
     }
 
+    /** Returns true if this request is wrapping a multipart request, false 
otherwise. */
     public boolean isMultipart() {
         return this.multipart != null;
     }
@@ -167,16 +142,7 @@
      */
     public String[] getParameterValues(String name) {
         if ( isMultipart() ) {
-            String[] values = this.multipart.getParameterValues(name);
-            if (values != null) {
-                for (int i=0; i<values.length; ++i) {
-                    if (values[i] == null) {
-                        values[i] = "";
-                    }
-                }
-            }
-
-            return values;
+            return this.multipart.getParameterValues(name);
         }
         else {
             return super.getParameterValues(name);
@@ -252,7 +218,7 @@
      * no file upload parameters are submitted returns an empty enumeration.
      */
     public Enumeration<String> getFileParameterNames() {
-        return this.multipart.getFileNames();
+        return this.multipart.getFileParameterNames();
     }
 
     /**
@@ -264,11 +230,8 @@
      * @return a FileBean if a file was actually submitted by the user, 
otherwise null
      */
     public FileBean getFileParameterValue(String name) {
-
-        if (this.multipart.getFile(name) != null) {
-            return new FileBean(this.multipart.getFile(name),
-                                this.multipart.getContentType(name),
-                                this.multipart.getOriginalFileName(name));
+        if (this.multipart != null) {
+            return this.multipart.getFileParameterValue(name);
         }
         else {
             return null;

Added: 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CommonsMultipartWrapper.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CommonsMultipartWrapper.java
                         (rev 0)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CommonsMultipartWrapper.java
 2006-08-15 00:18:37 UTC (rev 380)
@@ -0,0 +1,196 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.controller.multipart;
+
+import net.sourceforge.stripes.action.FileBean;
+import net.sourceforge.stripes.controller.FileUploadLimitExceededException;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.FileUploadBase;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An implementation of MultipartWrapper that uses Jakarta Commons FileUpload 
(from apache)
+ * to parse the request parts. This implementation requires that both 
commons-fileupload and
+ * commons-io be present in the classpath.  While this implementation does 
introduce additional
+ * dependencies, it's licensing (ASL 2.0) is significantly less restrictive 
than the licesing
+ * for COS - the other alternative provided by Stripes.
+ *
+ * @author Tim Fennell
+ * @since Stripes 1.4
+ */
+public class CommonsMultipartWrapper implements MultipartWrapper {
+    private Map<String,FileItem> files = new HashMap<String,FileItem>();
+    private Map<String,String[]> parameters = new HashMap<String, String[]>();
+
+    /**
+     * Pseudo-constructor that allows the class to perform any initialization 
necessary.
+     *
+     * @param request     an HttpServletRequest that has a content-type of 
multipart.
+     * @param tempDir a File representing the temporary directory that can be 
used to store
+     *        file parts as they are uploaded if this is desirable
+     * @param maxPostSize the size in bytes beyond which the request should 
not be read, and a
+     *                    FileUploadLimitExceeded exception should be thrown
+     * @throws IOException if a problem occurrs processing the request of 
storing temporary
+     *                    files
+     * @throws FileUploadLimitExceededException if the POST content is longer 
than the
+     *                     maxPostSize supplied.
+     */
+    public void build(HttpServletRequest request, File tempDir, long 
maxPostSize)
+            throws IOException, FileUploadLimitExceededException {
+        try {
+            DiskFileItemFactory factory = new DiskFileItemFactory();
+            factory.setRepository(tempDir);
+            ServletFileUpload upload = new ServletFileUpload(factory);
+            upload.setSizeMax(maxPostSize);
+            List<FileItem> items = upload.parseRequest(request);
+            Map<String,List<String>> params = new HashMap<String, 
List<String>>();
+
+            for (FileItem item : items) {
+                // If it's a form field, add the string value to the list
+                if (item.isFormField()) {
+                    List<String> values = params.get(item.getFieldName());
+                    if (values == null) {
+                        values = new ArrayList<String>();
+                        params.put(item.getFieldName(), values);
+                    }
+                    values.add(item.getString());
+                }
+                // Else store the file param
+                else {
+                    files.put(item.getFieldName(), item);
+                }
+            }
+
+            // Now convert them down into the usual map of String->String[]
+            for (Map.Entry<String,List<String>> entry : params.entrySet()) {
+                List<String> values = entry.getValue();
+                this.parameters.put(entry.getKey(), values.toArray(new 
String[values.size()]));
+            }
+        }
+        catch (FileUploadBase.SizeLimitExceededException slee) {
+            throw new FileUploadLimitExceededException(maxPostSize, 
slee.getActualSize());
+        }
+        catch (FileUploadException fue) {
+            IOException ioe = new IOException("Could not parse and cache file 
upload data.");
+            ioe.initCause(fue);
+            throw ioe;
+        }
+
+    }
+
+    /**
+     * Fetches the names of all non-file parameters in the request. Directly 
analogous to the
+     * method of the same name in HttpServletRequest when the request is 
non-multipart.
+     *
+     * @return an Enumeration of all non-file parameter names in the rqequest
+     */
+    public Enumeration<String> getParameterNames() {
+        return new IteratorEnumeration(this.parameters.keySet().iterator());
+    }
+
+    /**
+     * Fetches all values of a specifical parameter in the request. To 
simulate the HTTP request
+     * style, the array should be null for non-present parameters, and values 
in the array should
+     * never be null - the empty String should be used when there is value.
+     *
+     * @param name the name of the request parameter
+     * @return an array of non-null parameters or null
+     */
+    public String[] getParameterValues(String name) {
+        return this.parameters.get(name);
+    }
+
+    /**
+     * Fetches the names of all file parameters in the request. Note that 
these are not the file
+     * names, but the names given to the form fields in which the files are 
specified.
+     *
+     * @return the names of all file parameters in the request.
+     */
+    public Enumeration<String> getFileParameterNames() {
+        return new IteratorEnumeration(this.files.keySet().iterator());
+    }
+
+    /**
+     * Responsible for contructing a FileBean object for the named file 
parameter. If there is no
+     * file parameter with the specified name this method should return null.
+     *
+     * @param name the name of the file parameter
+     * @return a FileBean object wrapping the uploaded file
+     */
+    public FileBean getFileParameterValue(String name) {
+        final FileItem item = this.files.get(name);
+        if (item == null) {
+            return null;
+        }
+        else {
+            // Use an anonymous inner subclass of FileBean that overrides all 
the
+            // methods that rely on having a File present, to use the FileItem
+            // created by commons upload instead.
+            return new FileBean(null, item.getContentType(), item.getName()) {
+                @Override public long getSize() { return item.getSize(); }
+
+                @Override public InputStream getInputStream() throws 
IOException {
+                    return item.getInputStream();
+                }
+
+                @Override public void save(File toFile) throws IOException {
+                    try {
+                        item.write(toFile);
+                        delete();
+                    }
+                    catch (Exception e) {
+                        if (e instanceof IOException) throw (IOException) e;
+                        else {
+                            IOException ioe = new IOException("Problem saving 
uploaded file.");
+                            ioe.initCause(e);
+                            throw ioe;
+                        }
+                    }
+                }
+
+                @Override
+                public void delete() throws IOException { item.delete(); }
+            };
+        }
+    }
+
+    /** Little helper class to create an enumeration as per the interface. */
+    private static class IteratorEnumeration implements Enumeration<String> {
+        Iterator<String> iterator;
+
+        /** Constructs an enumeration that consumes from the underlying 
iterator. */
+        IteratorEnumeration(Iterator<String> iterator) { this.iterator = 
iterator; }
+
+        /** Returns true if more elements can be consumed, false otherwise. */
+        public boolean hasMoreElements() { return this.iterator.hasNext(); }
+
+        /** Gets the next element out of the iterator. */
+        public String nextElement() { return this.iterator.next(); }
+    }
+}
+

Added: 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CosMultipartWrapper.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CosMultipartWrapper.java
                             (rev 0)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/CosMultipartWrapper.java
     2006-08-15 00:18:37 UTC (rev 380)
@@ -0,0 +1,140 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.controller.multipart;
+
+import com.oreilly.servlet.MultipartRequest;
+import net.sourceforge.stripes.action.FileBean;
+import net.sourceforge.stripes.controller.FileUploadLimitExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.File;
+import java.util.Enumeration;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Implementation of MultipartWrapper that uses Jason Hunter's COS 
(com.oreilly.servlet)
+ * multipart parser implementation. This is the default implementation in 
Stripes and is
+ * generally preferred as it is a) free for use and b) has no other 
dependencies! However,
+ * commercial redistribution of the COS library requires licensing from Jason 
Hunter, so
+ * this implemenation may not be applicable for commercial products that are 
distributed/sold
+ * (though it is fine for commercial applications that are simply developed 
and hosted by the
+ * company developing them).
+ *
+ * @author Tim Fennell
+ * @since Stripes 1.4
+ */
+public class CosMultipartWrapper implements MultipartWrapper {
+    /** Pattern used to parse useful info out of the IOException cos throws. */
+    private static Pattern EXCEPTION_PATTERN =
+            Pattern.compile("Posted content length of (\\d*) exceeds limit of 
(\\d*)");
+
+    private MultipartRequest multipart;
+    /**
+     * Pseudo-constructor that allows the class to perform any initialization 
necessary.
+     *
+     * @param request an HttpServletRequest that has a content-type of 
multipart.
+     * @param tempDir a File representing the temporary directory that can be 
used to store
+     *        file parts as they are uploaded if this is desirable
+     * @param maxPostSize the size in bytes beyond which the request should 
not be read, and a
+     *                    FileUploadLimitExceeded exception should be thrown
+     * @throws IOException if a problem occurrs processing the request of 
storing temporary
+     *                     files
+     * @throws FileUploadLimitExceededException if the POST content is longer 
than the
+     *         maxPostSize supplied.
+     */
+    public void build(HttpServletRequest request, File tempDir, long 
maxPostSize)
+            throws IOException, FileUploadLimitExceededException {
+
+        try {
+            this.multipart = new MultipartRequest(request,
+                                                  tempDir.getAbsolutePath(),
+                                                  (int) maxPostSize,
+                                                  
request.getCharacterEncoding());
+        }
+        catch (IOException ioe) {
+            Matcher matcher = EXCEPTION_PATTERN.matcher(ioe.getMessage());
+
+            if (matcher.matches()) {
+                throw new 
FileUploadLimitExceededException(Long.parseLong(matcher.group(2)),
+                                                           
Long.parseLong(matcher.group(1)));
+            }
+            else {
+                throw ioe;
+            }
+        }
+    }
+
+    /**
+     * Fetches the names of all non-file parameters in the request. Directly 
analogous to the method
+     * of the same name in HttpServletRequest when the request is 
non-multipart.
+     *
+     * @return an Enumeration of all non-file parameter names in the rqequest
+     */
+    public Enumeration<String> getParameterNames() {
+        return this.multipart.getParameterNames();
+    }
+
+    /**
+     * Fetches all values of a specifical parameter in the request. To 
simulate the HTTP request
+     * style, the array should be null for non-present parameters, and values 
in the array should
+     * never be null - the empty String should be used when there is value.
+     *
+     * @param name the name of the request parameter
+     * @return an array of non-null parameters or null
+     */
+    public String[] getParameterValues(String name) {
+        String[] values = this.multipart.getParameterValues(name);
+        if (values != null) {
+            for (int i=0; i<values.length; ++i) {
+                if (values[i] == null) {
+                    values[i] = "";
+                }
+            }
+        }
+
+        return values;
+    }
+
+    /**
+     * Fetches the names of all file parameters in the request. Note that 
these are not the file
+     * names, but the names given to the form fields in which the files are 
specified.
+     *
+     * @return the names of all file parameters in the request.
+     */
+    public Enumeration<String> getFileParameterNames() {
+        return this.multipart.getFileNames();
+    }
+
+    /**
+     * Responsible for contructing a FileBean object for the named file 
parameter. If there is no
+     * file parameter with the specified name this method should return null.
+     *
+     * @param name the name of the file parameter
+     * @return a FileBean object wrapping the uploaded file
+     */
+    public FileBean getFileParameterValue(String name) {
+        File file = this.multipart.getFile(name);
+        if (file != null) {
+            return new FileBean(file,
+                                this.multipart.getContentType(name),
+                                this.multipart.getOriginalFileName(name));
+        }
+        else {
+            return null;
+        }
+    }
+}

Added: 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/DefaultMultipartWrapperFactory.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/DefaultMultipartWrapperFactory.java
                          (rev 0)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/DefaultMultipartWrapperFactory.java
  2006-08-15 00:18:37 UTC (rev 380)
@@ -0,0 +1,135 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.controller.multipart;
+
+import net.sourceforge.stripes.controller.FileUploadLimitExceededException;
+import net.sourceforge.stripes.config.Configuration;
+import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.exception.StripesRuntimeException;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.File;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * <p>Default implementation of a factory for MultipartWrappers. Looks up a 
class name in
+ * Configuration under the key specified by [EMAIL PROTECTED] 
#WRAPPER_CLASS_NAME}. If no class
+ * name is configured, defaults to the [EMAIL PROTECTED] CosMultipartWrapper}. 
An additional configuration
+ * parameter is supported to specify the maximum post size allowable.</p>
+ * 
+ * @author Tim Fennell
+ * @since Stripes 1.4
+ */
+public class DefaultMultipartWrapperFactory implements MultipartWrapperFactory 
{
+    /** The configuration key used to lookup the implementation of 
MultipartWrapper. */
+    public static final String WRAPPER_CLASS_NAME = "MultipartWrapper.Class";
+
+    /** The name of the MultipartWrapper class that will be used if no other 
is specified. */
+    public static final String DEFAULT_IMPLEMENTATION =
+            "net.sourceforge.stripes.controller.multipart.CosMultipartWrapper";
+
+    /** Key used to lookup the name of the maximum post size. */
+    public static final String MAX_POST = "FileUpload.MaximumPostSize";
+
+    private static final Log log = 
Log.getInstance(DefaultMultipartWrapperFactory.class);
+
+    // Instance level fields
+    private Class<? extends MultipartWrapper> multipartClass;
+    private long maxPostSizeInBytes = 1024 * 1024 * 10; // Defaults to 10MB
+    private File temporaryDirectory;
+
+    /**
+     * Invoked directly after instantiation to allow the configured component 
to perform one time
+     * initialization.  Components are expected to fail loudly if they are not 
going to be in a
+     * valid state after initialization.
+     *
+     * @param config the Configuration object being used by Stripes
+     * @throws Exception should be thrown if the component cannot be 
configured well enough to use.
+     */
+    public void init(Configuration config) throws Exception {
+        // Determine which class we're using
+        String type = 
config.getBootstrapPropertyResolver().getProperty(WRAPPER_CLASS_NAME);
+        if (type == null) type = DEFAULT_IMPLEMENTATION;
+        try {
+            this.multipartClass = (Class<? extends MultipartWrapper>) 
Class.forName(type);
+        }
+        catch (ClassNotFoundException cnfe) {
+            throw new StripesRuntimeException
+                    ("Could not find configured MultipartWrapper type '" + 
type + "'.", cnfe);
+        }
+
+        // Figure out where the temp directory is, and store that info
+        File tempDir = (File) 
config.getServletContext().getAttribute("javax.servlet.context.tempdir");
+        if (tempDir != null) {
+            this.temporaryDirectory = tempDir;
+        }
+        else {
+            this.temporaryDirectory = new 
File(System.getProperty("java.io.tmpdir")).getAbsoluteFile();
+        }
+
+        // See if a maximum post size was configured
+        String limit = 
config.getBootstrapPropertyResolver().getProperty(MAX_POST);
+        if (limit != null) {
+            Pattern pattern = Pattern.compile("([\\d,]+)([kKmMgG]?).*");
+            Matcher matcher = pattern.matcher(limit);
+            if (!matcher.matches()) {
+                log.error("Did not understand value of configuration parameter 
", MAX_POST,
+                         " You supplied: ", limit, ". Valid values are any 
string of numbers ",
+                         "optionally followed by (case insensitive) 
[k|kb|m|mb|g|gb]. ",
+                         "Default value of ", this.maxPostSizeInBytes, " bytes 
will be used instead.");
+            }
+            else {
+                String digits = matcher.group(1);
+                String suffix = matcher.group(2).toLowerCase();
+                int number = Integer.parseInt(digits);
+
+                if ("k".equals(suffix)) { number = number * 1024; }
+                else if ("m".equals(suffix)) {  number = number * 1024 * 1024; 
}
+                else if ("g".equals(suffix)) { number = number * 1024 * 1024 * 
1024; }
+
+                this.maxPostSizeInBytes = number;
+                log.info("Configured file upload post size limit: ", number, " 
bytes.");
+            }
+        }
+
+
+    }
+
+    /**
+     * Wraps the request in an appropriate implementation of MultipartWrapper 
that is capable of
+     * providing access to request parameters and any file parts contained 
within the request.
+     *
+     * @param request an active HttpServletRequest
+     * @return an implementation of the appropriate wrapper
+     * @throws IOException if encountered when consuming the contents of the 
request
+     * @throws FileUploadLimitExceededException if the post size of the 
request exceeds any
+     *          configured limits
+     */
+    public MultipartWrapper wrap(HttpServletRequest request) throws 
IOException, FileUploadLimitExceededException {
+        try {
+            MultipartWrapper wrapper = this.multipartClass.newInstance();
+            wrapper.build(request, this.temporaryDirectory, 
this.maxPostSizeInBytes);
+            return wrapper;
+        }
+        catch (IOException ioe) { throw ioe; }
+        catch (FileUploadLimitExceededException fulee) { throw fulee; }
+        catch (Exception e) {
+            throw new StripesRuntimeException
+                    ("Could not construct a MultipartWrapper for the " + 
"current request.", e);
+        }
+    }
+}

Added: 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapper.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapper.java
                                (rev 0)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapper.java
        2006-08-15 00:18:37 UTC (rev 380)
@@ -0,0 +1,83 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.controller.multipart;
+
+import net.sourceforge.stripes.controller.FileUploadLimitExceededException;
+import net.sourceforge.stripes.action.FileBean;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * Interface which must be implemented by classes which provide the ability to 
parse
+ * the POST content when it is of type multipart/form-data. Provides a single, 
pluggable
+ * wrapper interface around third party libraries which provide this 
capability.
+ *
+ * @author Tim Fennell
+ * @since Stripes 1.4
+ */
+public interface MultipartWrapper {
+    /**
+     * Pseudo-constructor that allows the class to perform any initialization 
necessary.
+     *
+     * @param request an HttpServletRequest that has a content-type of 
multipart.
+     * @param tempDir a File representing the temporary directory that can be 
used to store
+     *        file parts as they are uploaded if this is desirable
+     * @param maxPostSize the size in bytes beyond which the request should 
not be read,
+     *        and a FileUploadLimitExceeded exception should be thrown
+     * @throws IOException if a problem occurrs processing the request of 
storing temporary files
+     * @throws FileUploadLimitExceededException if the POST content is longer 
than the maxPostSize
+     *         supplied.
+     */
+    void build(HttpServletRequest request, File tempDir, long maxPostSize)
+            throws IOException, FileUploadLimitExceededException;
+
+    /**
+     * Fetches the names of all non-file parameters in the request. Directly 
analogous to the
+     * method of the same name in HttpServletRequest when the request is 
non-multipart.
+     *
+     * @return an Enumeration of all non-file parameter names in the rqequest
+     */
+    Enumeration<String> getParameterNames();
+
+    /**
+     * Fetches all values of a specifical parameter in the request. To 
simulate the HTTP
+     * request style, the array should be null for non-present parameters, and 
values in the
+     * array should never be null - the empty String should be used when there 
is value.
+     *
+     * @param name the name of the request parameter
+     * @return an array of non-null parameters or null
+     */
+    String[] getParameterValues(String name);
+
+    /**
+     * Fetches the names of all file parameters in the request. Note that 
these are not the
+     * file names, but the names given to the form fields in which the files 
are specified.
+     *
+     * @return the names of all file parameters in the request.
+     */
+    Enumeration<String> getFileParameterNames();
+
+    /**
+     * Responsible for contructing a FileBean object for the named file 
parameter. If there is
+     * no file parameter with the specified name this method should return 
null.
+     *
+     * @param name the name of the file parameter
+     * @return a FileBean object wrapping the uploaded file
+     */
+    FileBean getFileParameterValue(String name);
+}

Added: 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapperFactory.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapperFactory.java
                         (rev 0)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/multipart/MultipartWrapperFactory.java
 2006-08-15 00:18:37 UTC (rev 380)
@@ -0,0 +1,44 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.controller.multipart;
+
+import net.sourceforge.stripes.config.ConfigurableComponent;
+import net.sourceforge.stripes.controller.FileUploadLimitExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * Factory for classes that implement [EMAIL PROTECTED] MultipartWrapper}. The 
factory may chose to
+ * always supply the same kind of wrapper, or vary the implementation request 
by request
+ * as it sees fit.
+ *
+ * @author Tim Fennell
+ * @since Stripes 1.4
+ */
+public interface MultipartWrapperFactory extends ConfigurableComponent {
+    /**
+     * Wraps the request in an appropriate implementation of MultipartWrapper 
that is capable
+     * of providing access to request parameters and any file parts contained 
within the
+     * request.
+     *
+     * @param request an active HttpServletRequest
+     * @return an implementation of the appropriate wrapper
+     * @throws IOException if encountered when consuming the contents of the 
request
+     * @throws FileUploadLimitExceededException if the post size of the 
request exceeds
+     *         any configured limits
+     */
+    MultipartWrapper wrap(HttpServletRequest request) throws IOException, 
FileUploadLimitExceededException;
+}

Modified: trunk/stripes/stripes.iml
===================================================================
--- trunk/stripes/stripes.iml   2006-08-14 18:24:12 UTC (rev 379)
+++ trunk/stripes/stripes.iml   2006-08-15 00:18:37 UTC (rev 380)
@@ -77,6 +77,15 @@
         <SOURCES />
       </library>
     </orderEntry>
+    <orderEntry type="module-library" exported="">
+      <library>
+        <CLASSES>
+          <root 
url="jar://$MODULE_DIR$/lib/test/commons-fileupload-1.1.1.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
     <orderEntryProperties />
     <javadoc-paths>
       <root url="file://$MODULE_DIR$/docs" />


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to