You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by gc...@apache.org on 2012/05/18 17:47:27 UTC

svn commit: r1340139 - in /myfaces/trinidad/branches/1.2.12.7.0-branch: trinidad-api/src/main/java/org/apache/myfaces/trinidad/context/ trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/ trinidad-api/src/test/java/org/apache/myfaces/trinida...

Author: gcrawford
Date: Fri May 18 15:47:26 2012
New Revision: 1340139

URL: http://svn.apache.org/viewvc?rev=1340139&view=rev
Log:
TRINIDAD-2258 Add Chunked File Upload support to the Trinidad Upload Framwork 

Thanks to Kentaro

Modified:
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/context/RequestContext.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/context/MockRequestContext.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextBean.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextImpl.java
    myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/context/RequestContext.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/context/RequestContext.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/context/RequestContext.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/context/RequestContext.java Fri May 18 15:47:26 2012
@@ -616,6 +616,10 @@ abstract public class RequestContext
 
   public abstract Long getUploadedFileMaxDiskSpace();
 
+  public abstract Long getUploadedFileMaxFileSize();
+  
+  public abstract Long getUploadedFileMaxChunkSize();
+
   public abstract String getUploadedFileTempDir();
 
   /**

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java Fri May 18 15:47:26 2012
@@ -41,8 +41,12 @@ import org.apache.myfaces.trinidad.model
  * <li>org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE: the maximum amount of
  *  disk space that can be used in a single request to store
  *  uploaded files.  (Default of 2000K)
+ * <li>org.apache.myfaces.trinidad.UPLOAD_MAX_FILE_SIZE: the maximum
+ *  file size that can be uploaded.  (Default of 2000K)
  * <li>org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR: the name of a directory
  *   to store temporary files.  (Defaults to the user's temporary directory)
+ * <li>org.apache.myfaces.trinidad.UPLOAD_MAX_CHUNK_SIZE: the maximum
+ *  chunk size that large files will be split into during upload.  (Default of 2000M)
  * </ul>
  * 
  * @see org.apache.myfaces.trinidad.model.UploadedFile
@@ -80,6 +84,47 @@ public interface UploadedFileProcessor
   public static final String TEMP_DIR_PARAM_NAME = "org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR";
 
   /**
+   * Initialization parameter for the default
+   * <code>UploadedFileProcessor</code> that configures the maximum
+   * file size that can be uploaded. The default is -1 (unlimited).  Any requests that
+   * exceed this size will result in an EOFException being thrown
+   * on that request.
+   */
+  public static final String MAX_FILE_SIZE_PARAM_NAME = "org.apache.myfaces.trinidad.UPLOAD_MAX_FILE_SIZE";
+  
+  /**
+   * Initialization parameter for the default
+   * <code>UploadedFileProcessor</code> that configures the maximum
+   * chunk size that large files will be split into during upload. 
+   * The default is 2000M and is also the maximum allowed value.
+   */
+  public static final String MAX_CHUNK_SIZE_PARAM_NAME = "org.apache.myfaces.trinidad.UPLOAD_MAX_CHUNK_SIZE";
+  
+  /**
+   * Initialization parameter default value of 100 kilobytes for the default
+   * <code>UploadedFileProcessor</code> parameter MAX_MEMORY_PARAM_NAME.
+   */
+  public static final long DEFAULT_MAX_MEMORY = 102400L;
+  
+  /**
+   * Initialization parameter default value of 2000 kilobytes for the default
+   * <code>UploadedFileProcessor</code> parameter MAX_DISK_SPACE_PARAM_NAME.
+   */
+  public static final long DEFAULT_MAX_DISK_SPACE = 2048000L;
+  
+  /**
+   * Initialization parameter default value of 2000 kilobytes for the default
+   * <code>UploadedFileProcessor</code> parameter MAX_FILE_SIZE_PARAM_NAME.
+   */
+  public static final long DEFAULT_MAX_FILE_SIZE = -1L;
+  
+  /**
+   * Initialization parameter default value of 2000 megabytes for the default
+   * <code>UploadedFileProcessor</code> parameter MAX_CHUNK_PARAM_NAME.
+   */
+  public static final long DEFAULT_MAX_CHUNK_SIZE = 2000000000L;
+  
+  /**
    * Initialize the UploadedFileProcessor with access to the current
    * web application context. 
    * 

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/context/MockRequestContext.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/context/MockRequestContext.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/context/MockRequestContext.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-api/src/test/java/org/apache/myfaces/trinidad/context/MockRequestContext.java Fri May 18 15:47:26 2012
@@ -299,6 +299,28 @@ public class MockRequestContext extends 
     return _maxDiskSpace;
   }
 
+  public void setUploadedFileMaxFileSize(Long maxFileSize)
+  {
+    _maxFileSize = maxFileSize;
+  }
+
+  @Override
+  public Long getUploadedFileMaxFileSize()
+  {
+    return _maxFileSize;
+  }
+
+  public void setUploadedFileMaxChunkSize(Long maxChunkSize)
+  {
+    _maxChunkSize = maxChunkSize;
+  }
+
+  @Override
+  public Long getUploadedFileMaxChunkSize()
+  {
+    return _maxChunkSize;
+  }
+
   public void setUploadedFileTempDir(String tempDir)
   {
     _tempDir= tempDir;
@@ -459,5 +481,7 @@ public class MockRequestContext extends 
   private Locale _formattingLocale;
   private Long _maxMemory;
   private Long _maxDiskSpace;
+  private Long _maxFileSize;
+  private Long _maxChunkSize;
   private String _tempDir;
 }

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java Fri May 18 15:47:26 2012
@@ -110,7 +110,7 @@ public class CompositeUploadedFileProces
         catch (IOException ioe)
         {
           _LOG.severe(ioe);
-          original = new ErrorFile(ioe.getLocalizedMessage());
+          original = new ErrorFile(tempFile.getFilename(), ioe.getLocalizedMessage());
           // The chain breaks if one of the chained processor throws an IOException, if the intent
           //  is to allow rest of the processors in chain to process, they could return custom 
           //  UploadedFile instance with length -1 and opaqueData having the failure details.
@@ -228,12 +228,12 @@ public class CompositeUploadedFileProces
         }
         catch (NumberFormatException nfe)
         {
-          _maxMemory = _DEFAULT_MAX_MEMORY;
+          _maxMemory = DEFAULT_MAX_MEMORY;
         }
       }
       else
       {
-        _maxMemory = _DEFAULT_MAX_MEMORY;
+        _maxMemory = DEFAULT_MAX_MEMORY;
       }
     }
 
@@ -248,12 +248,12 @@ public class CompositeUploadedFileProces
         }
         catch (NumberFormatException nfe)
         {
-          _maxDiskSpace = _DEFAULT_MAX_DISK_SPACE;
+          _maxDiskSpace = DEFAULT_MAX_DISK_SPACE;
         }
       }
       else
       {
-        _maxDiskSpace = _DEFAULT_MAX_DISK_SPACE;
+        _maxDiskSpace = DEFAULT_MAX_DISK_SPACE;
       }
     }
 
@@ -270,6 +270,26 @@ public class CompositeUploadedFileProces
           _tempDir = tempDirFile.getAbsolutePath();
       }
     }
+    
+    if (_maxFileSize == -1)
+    {
+      String maxFileSize = info.initParams.get(MAX_FILE_SIZE_PARAM_NAME);
+      if (maxFileSize != null)
+      {
+        try
+        {
+          _maxFileSize = Long.parseLong(maxFileSize);
+        }
+        catch (NumberFormatException nfe)
+        {
+          _maxFileSize = DEFAULT_MAX_FILE_SIZE;
+        }
+      }
+      else
+      {
+        _maxFileSize = DEFAULT_MAX_FILE_SIZE;
+      }
+    }
   }
 
   private UploadedFile _processFile(
@@ -287,7 +307,8 @@ public class CompositeUploadedFileProces
     Long maxMemory = (Long)requestMap.get(MAX_MEMORY_PARAM_NAME);
     Long maxDiskSpace = (Long)requestMap.get(MAX_DISK_SPACE_PARAM_NAME);
     String tempDir = (String)requestMap.get(TEMP_DIR_PARAM_NAME);
-
+    Long maxFileSizeSpace = (Long)requestMap.get(MAX_FILE_SIZE_PARAM_NAME);
+    
     if (maxMemory != null)
     {
       _maxMemory = maxMemory;
@@ -301,9 +322,19 @@ public class CompositeUploadedFileProces
     if (tempDir != null)
       _tempDir = tempDir;
 
+    if (maxFileSizeSpace != null)
+    {
+      _maxFileSize = maxFileSizeSpace;
+    }
+    
     if(contentLength>_maxDiskSpace)
     {
-      return new ErrorFile(_LOG.getMessage("UPLOADED_FILE_LARGE"));
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
+    }
+    // If the file is too large throw error
+    if(_maxFileSize > 0 && contentLength>_maxFileSize)
+    {
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
     }
     // Process one new file, loading only as much as can fit
     // in the remaining memory and disk space.
@@ -318,7 +349,7 @@ public class CompositeUploadedFileProces
     catch(IOException ioe)
     {
       _LOG.severe(ioe);
-      return new ErrorFile(ioe.getLocalizedMessage());
+      return new ErrorFile(tempFile.getFilename(), ioe.getLocalizedMessage());
     }
 
     // Keep a tally of how much we've stored in memory and on disk.
@@ -448,11 +479,9 @@ public class CompositeUploadedFileProces
 
   private long   _maxMemory = -1;
   private long   _maxDiskSpace = -1;
+  private long   _maxFileSize = -1;
   private String _tempDir = null;
 
-  private static final long _DEFAULT_MAX_MEMORY = 102400;
-  private static final long _DEFAULT_MAX_DISK_SPACE = 2048000;
-
   private static final String _REQUEST_INFO_KEY = CompositeUploadedFileProcessorImpl.class.getName()+
     ".UploadedFilesInfo";
 

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java Fri May 18 15:47:26 2012
@@ -32,6 +32,12 @@ public class ErrorFile implements Upload
     _errorMessage = errorMessage;
   }
 
+  public ErrorFile(String filename, String errorMessage)
+  {
+    _filename = filename;
+    _errorMessage = errorMessage;
+  }
+  
   public void dispose()
   {
   }
@@ -43,7 +49,7 @@ public class ErrorFile implements Upload
 
   public String getFilename()
   {
-    return null;
+    return _filename;
   }
 
   public InputStream getInputStream() throws IOException
@@ -62,6 +68,7 @@ public class ErrorFile implements Upload
   }
   
   private String _errorMessage = null;
+  private String _filename = null;
 
   private static final long serialVersionUID = 2L;
-}
\ No newline at end of file
+}

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java Fri May 18 15:47:26 2012
@@ -19,12 +19,21 @@
 
 package org.apache.myfaces.trinidadinternal.config.upload;
 
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
+import java.io.SequenceInputStream;
+
 import java.lang.reflect.Proxy;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import javax.faces.context.ExternalContext;
@@ -121,7 +130,7 @@ public class FileUploadConfiguratorImpl 
 
         final HashMap<String, String[]> parameters = new HashMap<String, String[]>();
         MultipartFormItem item;
-        final UploadedFiles files = new UploadedFiles(externalContext);
+        UploadedFiles files = new UploadedFiles(externalContext);
         while ((item = mfh.getNextPart()) != null)
         {
           final String name = item.getName();
@@ -144,12 +153,74 @@ public class FileUploadConfiguratorImpl 
               parameters.put(name, newArray);
             }
           }
-          // Upload a file
           else if (item.getFilename().length() > 0)
           {
+            // Upload a file
             _doUploadFile(RequestContext.getCurrentInstance(), externalContext, files, item);
           }
         }
+        if (parameters.containsKey(_MULTIPLE_UPLOAD_PARAM))
+        {
+          String uploadType = parameters.get(_MULTIPLE_UPLOAD_PARAM)[0];
+          if (uploadType != null)
+          {
+            UploadedFiles sessionFiles = UploadedFiles.getSessionUploadedFiles(externalContext);
+            if (uploadType.equals("multipleAdd"))
+            {
+              Map<String, List<UploadedFile>> uploadedMapFile = files.getUploadedFileMap();
+              Iterator iterator = uploadedMapFile.keySet().iterator();
+              while (iterator.hasNext())
+              {
+                String name = (String) iterator.next();
+                List<UploadedFile> fileList = uploadedMapFile.get(name);
+                for (UploadedFile file: fileList)
+                {
+                  sessionFiles.__put(name, file);
+                }
+              }
+              uploadedMapFile.clear();
+            }
+            else if (uploadType.equals("multipleDelete"))
+            {
+              String itemName = parameters.get("itemName")[0];
+              String fileName = parameters.get("fileName")[0];
+              List<UploadedFile> uploadedFiles = sessionFiles.getUploadedFileList(itemName);
+              if (uploadedFiles != null)
+              {
+                for (UploadedFile uploadedFile: uploadedFiles)
+                {
+                  if (uploadedFile.getFilename().equals(fileName))
+                  {
+                    uploadedFiles.remove(uploadedFile);
+                    break;
+                  }
+                }
+              }
+            }
+            else if (uploadType.equals("multipleAddChunk"))
+            {
+              String itemName = parameters.get("itemName")[0];
+              String fileName = externalContext.getRequestHeaderMap().get(_MULTIPLE_UPLOAD_CHUNK_FILENAME_PARAM);
+              Long chunkNum = Long.parseLong(externalContext.getRequestHeaderMap().get(_MULTIPLE_UPLOAD_CHUNK_NUM_PARAM));
+              Long chunkCount = Long.parseLong(externalContext.getRequestHeaderMap().get(_MULTIPLE_UPLOAD_CHUNK_COUNT_PARAM));
+              UploadedFile file = files.getUploadedFile(itemName);
+              List<UploadedFile> chunkList = (List<UploadedFile>) externalContext.getSessionMap().get(_UPLOADED_CHUNK_FILES_LIST_KEY);
+              if (chunkList == null)
+              {
+                chunkList = new ArrayList<UploadedFile>();
+                externalContext.getSessionMap().put(_UPLOADED_CHUNK_FILES_LIST_KEY, chunkList);
+              }
+              chunkList.add(file);
+              if (chunkNum == chunkCount - 1)
+              {
+                UploadedFile combinedFile = new ChunkedUploadedFile(fileName, file.getContentType(), chunkList);
+                sessionFiles.__put(itemName, combinedFile);
+                externalContext.getSessionMap().remove(_UPLOADED_CHUNK_FILES_LIST_KEY);
+              }
+              files.getUploadedFileMap().clear();
+            }
+          }
+        }
         externalContext.getRequestMap().put(_PARAMS, parameters);
       }
       catch (Throwable t)
@@ -210,14 +281,27 @@ public class FileUploadConfiguratorImpl 
       final UploadedFiles     files,
       final MultipartFormItem item) throws IOException
   {
-    final UploadedFile temp = new TempUploadedFile(item);
+    String filename = item.getFilename();              
+    String chunkFilename = externalContext.getRequestHeaderMap().get(_MULTIPLE_UPLOAD_CHUNK_FILENAME_PARAM);
+    if (chunkFilename != null)
+    {
+      // We store the filename in a special header when sending chunked data. The reason is that
+      // browsers do not have an API for setting the filename on Blob objects. Also, append the chunk num
+      // so it's easier to keep track of.
+      String chunkNum = externalContext.getRequestHeaderMap().get(_MULTIPLE_UPLOAD_CHUNK_NUM_PARAM);
+      chunkFilename = chunkFilename + ".part" + chunkNum;
+      filename = chunkFilename;
+    }
+    final UploadedFile temp = new TempUploadedFile(filename, item);
     Map<String, Object> sessionMap = externalContext.getSessionMap();
     Map<String, Object> requestMap = externalContext.getRequestMap();
     
     _copyParamsFromSessionToRequestMap(sessionMap, requestMap,
       UploadedFileProcessor.MAX_MEMORY_PARAM_NAME,
       UploadedFileProcessor.MAX_DISK_SPACE_PARAM_NAME,
-      UploadedFileProcessor.TEMP_DIR_PARAM_NAME);
+      UploadedFileProcessor.TEMP_DIR_PARAM_NAME,
+      UploadedFileProcessor.MAX_FILE_SIZE_PARAM_NAME,
+      UploadedFileProcessor.MAX_CHUNK_SIZE_PARAM_NAME);
     
     final UploadedFile file =
       context.getUploadedFileProcessor().processFile(externalContext.getRequest(), temp);
@@ -320,15 +404,16 @@ public class FileUploadConfiguratorImpl 
 
   static private class TempUploadedFile implements UploadedFile
   {
-    public TempUploadedFile(MultipartFormItem item)
+    public TempUploadedFile(String filename, MultipartFormItem item)
     {
+      _filename = filename;
       _item = item;
       assert(item.getValue() == null);
     }
 
     public String getFilename()
     {
-      return _item.getFilename();
+      return _filename != null ? _filename : _item.getFilename();
     }
 
     public String getContentType()
@@ -358,11 +443,92 @@ public class FileUploadConfiguratorImpl 
     }
 
     private MultipartFormItem _item;
+    private String _filename = null;
+  }
+  static private class ChunkedUploadedFile implements UploadedFile
+  {
+    private List<UploadedFile> _uploadedFileChunkList = null;
+    private String _filename = null;
+    private String _contentType = null;
+    
+    public ChunkedUploadedFile(String filename, String contentType, List<UploadedFile> uploadedFileChunkList)
+    {
+      _filename = filename;
+      _contentType = contentType;
+      _uploadedFileChunkList = uploadedFileChunkList;
+    }
+    
+    public String getFilename()
+    {
+      return _filename;
+    }
+
+    public String getContentType()
+    {
+      return _contentType;
+    }
+    
+    public long getLength()
+    {
+      Long totalLength = 0L;
+      for (UploadedFile file : _uploadedFileChunkList)
+      {
+        if (file.getLength() == -1L)
+        {
+          // there was an error so return -1
+          return -1L;
+        }
+        totalLength = totalLength + file.getLength();
+      }
+      return totalLength;
+    }
+
+    public Object getOpaqueData()
+    {
+      for (UploadedFile file : _uploadedFileChunkList)
+      {
+        if (file.getLength() == -1L)
+        {
+          // there was an error so return the data
+          return file.getOpaqueData();
+        }
+      }
+      return null;
+    }
+
+    public InputStream getInputStream() throws IOException
+    {
+      List<InputStream> inputSteamList = new ArrayList<InputStream>(_uploadedFileChunkList.size());
+      for (UploadedFile uploadedFileChunk : _uploadedFileChunkList)
+        inputSteamList.add(uploadedFileChunk.getInputStream());
+      
+      return new SequenceInputStream(Collections.enumeration(inputSteamList));
+    }
+
+    public void dispose()
+    {
+      for (UploadedFile uploadedFileChunk : _uploadedFileChunkList)
+      {
+        try
+        {
+          uploadedFileChunk.dispose();
+        }
+        catch (Exception e)
+        {
+          // Just keep trying to dispose of the rest of the chunks
+        }
+      }
+    }
   }
   static private final String _APPLIED = FileUploadConfiguratorImpl.class.getName()+".APPLIED";
   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(FileUploadConfiguratorImpl.class);
   static private final String _PARAMS = FileUploadConfiguratorImpl.class.getName()+".PARAMS";
   static private final boolean _ENHANCED_PORTLET_SUPPORTED = ExternalContextUtils.isRequestTypeSupported(RequestType.RESOURCE);
+  static private final String _MULTIPLE_UPLOAD_PARAM = "org.apache.myfaces.trinidad.UploadedFiles";
+  static private final String _MULTIPLE_UPLOAD_CHUNK_NUM_PARAM = "X-Trinidad-chunkNum";
+  static private final String _MULTIPLE_UPLOAD_CHUNK_COUNT_PARAM = "X-Trinidad-chunkCount";
+  static private final String _MULTIPLE_UPLOAD_CHUNK_FILENAME_PARAM = "X-Trinidad-chunkFilename";
+  static private final String _UPLOADED_CHUNK_FILES_LIST_KEY = "org.apache.myfaces.trinidadinternal.webapp.UploadedFiles.ChunkList";
   
   private long _maxAllowedBytes = 1L << 27;
 }

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java Fri May 18 15:47:26 2012
@@ -78,12 +78,12 @@ public class UploadedFileProcessorImpl i
         }
         catch (NumberFormatException nfe)
         {
-          _maxMemory = _DEFAULT_MAX_MEMORY;
+          _maxMemory = DEFAULT_MAX_MEMORY;
         }
       }
       else
       {
-        _maxMemory = _DEFAULT_MAX_MEMORY;
+        _maxMemory = DEFAULT_MAX_MEMORY;
       }
     }
 
@@ -98,12 +98,12 @@ public class UploadedFileProcessorImpl i
         }
         catch (NumberFormatException nfe)
         {
-          _maxDiskSpace = _DEFAULT_MAX_DISK_SPACE;
+          _maxDiskSpace = DEFAULT_MAX_DISK_SPACE;
         }
       }
       else
       {
-        _maxDiskSpace = _DEFAULT_MAX_DISK_SPACE;
+        _maxDiskSpace = DEFAULT_MAX_DISK_SPACE;
       }
     }
 
@@ -120,6 +120,26 @@ public class UploadedFileProcessorImpl i
           _tempDir = tempDirFile.getAbsolutePath();
       }
     }
+    
+    if (_maxFileSize == -1)
+    {
+      String maxFileSize = info.initParams.get(MAX_FILE_SIZE_PARAM_NAME);
+      if (maxFileSize != null)
+      {
+        try
+        {
+          _maxFileSize = Long.parseLong(maxFileSize);
+        }
+        catch (NumberFormatException nfe)
+        {
+          _maxFileSize = DEFAULT_MAX_FILE_SIZE;
+        }
+      }
+      else
+      {
+        _maxFileSize = DEFAULT_MAX_FILE_SIZE;
+      }
+    }
   }
 
   public UploadedFile processFile(
@@ -153,7 +173,12 @@ public class UploadedFileProcessorImpl i
     
     if(contentLength>_maxDiskSpace)
     {
-      return new ErrorFile(_LOG.getMessage("UPLOADED_FILE_LARGE"));
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
+    }
+    // If the file is too large throw error
+    if(_maxFileSize > 0 && contentLength>_maxFileSize)
+    {
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
     }
     // Process one new file, loading only as much as can fit
     // in the remaining memory and disk space.
@@ -168,7 +193,7 @@ public class UploadedFileProcessorImpl i
     catch(IOException ioe)
     {
       _LOG.severe(ioe);
-      return new ErrorFile(ioe.getLocalizedMessage());
+      return new ErrorFile(tempFile.getFilename(), ioe.getLocalizedMessage());
     }
 
     // Keep a tally of how much we've stored in memory and on disk.
@@ -298,11 +323,9 @@ public class UploadedFileProcessorImpl i
 
   private long   _maxMemory = -1;
   private long   _maxDiskSpace = -1;
+  private long   _maxFileSize = -1;
   private String _tempDir = null;
 
-  private static final long _DEFAULT_MAX_MEMORY = 102400;
-  private static final long _DEFAULT_MAX_DISK_SPACE = 2048000;
-
   private static final String _REQUEST_INFO_KEY = UploadedFileProcessorImpl.class.getName()+
     ".UploadedFilesInfo";
 

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java Fri May 18 15:47:26 2012
@@ -18,24 +18,28 @@
  */
 package org.apache.myfaces.trinidadinternal.config.upload;
 
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
+
 import javax.portlet.PortletRequest;
+
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.myfaces.trinidad.model.UploadedFile;
-
 import org.apache.myfaces.trinidadinternal.share.util.CaboHttpUtils;
 
+
 /**
  * UploadedFiles defines the set of files that have been uploaded
  * to the server.
@@ -52,17 +56,41 @@ final public class UploadedFiles
   {
     return getUploadedFiles(context.getExternalContext());
   }
-
+  
   /**
    * Returns the map of uploaded files for the current request.
    */
   @SuppressWarnings("unchecked")
   static public UploadedFiles getUploadedFiles(ExternalContext context)
   {
-    Map<String, Object> requestMap =
-      context.getRequestMap();
+    Map<String, Object> requestMap = context.getRequestMap();
     return (UploadedFiles) requestMap.get(_UPLOADED_FILES_KEY);
   }
+  
+  /**
+   * Returns the map of uploaded files for the current request.
+   */
+  @SuppressWarnings("unchecked")
+  static public UploadedFiles getSessionUploadedFiles(FacesContext context)
+  {
+    return getSessionUploadedFiles(context.getExternalContext());
+  }
+  
+  /**
+   * Returns the map of uploaded files for the current session.
+   */
+  @SuppressWarnings("unchecked")
+  static public UploadedFiles getSessionUploadedFiles(ExternalContext context)
+  {
+    Map<String, Object> sessionMap = context.getSessionMap();
+    UploadedFiles files = (UploadedFiles) sessionMap.get(_UPLOADED_FILES_KEY);
+    if (files == null)
+    {
+      files = new UploadedFiles();
+      sessionMap.put(_UPLOADED_FILES_KEY, files);
+    }
+    return files;
+  }
 
   /**
    * Store the character encoding for the current request.
@@ -71,8 +99,7 @@ final public class UploadedFiles
     ExternalContext externalContext,
     String         encoding)
   {
-    UploadedFiles files = (UploadedFiles)
-      externalContext.getRequestMap().get(_UPLOADED_FILES_KEY);
+    UploadedFiles files = getUploadedFiles(externalContext);
     _setCharacterEncoding(files, encoding);
   }
 
@@ -93,7 +120,7 @@ final public class UploadedFiles
       req.getAttribute(_UPLOADED_FILES_KEY);
     _setCharacterEncoding(files, encoding);
   }
-
+  
   static private void _setCharacterEncoding(UploadedFiles files, String encoding)
   {
     if(files != null)
@@ -109,13 +136,63 @@ final public class UploadedFiles
    */
   public UploadedFile getUploadedFile(String name)
   {
-    UploadedFile file = _map.get(name);
-    if (file == null)
+    List<UploadedFile> files = getUploadedFileList(name);
+    if (files == null || files.isEmpty())
       return null;
 
-    return new FixFilename(file, _characterEncoding);
+    for (UploadedFile file : files)
+    {
+      if (file != null)
+      {
+        return file;
+      }
+    }
+    
+    return null;
   }
 
+  /**
+   * Returns a list of uploaded files.
+   * @param name the name under which the files are stored.  In HTML forms,
+   *   this will be derived from the "name" set on the &lt;input&gt; tag.
+   */
+  public List<UploadedFile> getUploadedFileList(String name)
+  {
+    List<UploadedFile> files = _map.get(name);
+    if (files == null || files.isEmpty())
+      return null;
+    
+    return files;
+  }
+
+  /**
+   * Retrieves files uploaded during a multiple file upload and copies those
+   * files to the requestMap so they get processed and cleaned up as usual
+   * @param name the name under which the files are stored.  In HTML forms,
+   *   this will be derived from the "name" set on the &lt;input&gt; tag.
+   */
+  public static void retrieveSessionUploadedFiles(ExternalContext context, String name)
+  {
+    UploadedFiles sessionFiles = (UploadedFiles) context.getSessionMap().get(_UPLOADED_FILES_KEY);
+    if (sessionFiles != null)
+    {
+      List<UploadedFile> sessionFileList = sessionFiles.getUploadedFileList(name);
+      if (sessionFileList != null && !sessionFileList.isEmpty())
+      {
+        UploadedFiles requestFiles = (UploadedFiles) context.getRequestMap().get(_UPLOADED_FILES_KEY);
+        if (requestFiles == null)
+        {
+          requestFiles = new UploadedFiles(context);
+        }
+        for (UploadedFile sessionFile: sessionFileList)
+        {
+          requestFiles.__put(name, sessionFile);
+        }
+        // clear it in the sessionMap
+        sessionFiles.getUploadedFileMap().remove(name);
+      }
+    }
+  }
 
   /**
    * Returns an Iterator of the names of all uploaded files.
@@ -124,6 +201,14 @@ final public class UploadedFiles
   {
     return _map.keySet().iterator();
   }
+  
+  /**
+   * Returns an Map of all of the uploaded files and names
+   */
+  public Map<String, List<UploadedFile>> getUploadedFileMap()
+  {
+    return _map;
+  }
 
   /**
    * Dispose of all UploadedFiles.  This will happen automatically
@@ -132,12 +217,21 @@ final public class UploadedFiles
    * processing files, this will free up resources earlier.
    */
   public void dispose()
-  {
-    Iterator<UploadedFile> iterator = _map.values().iterator();
+  {    
+    Iterator<List<UploadedFile>> iterator = _map.values().iterator();
     while (iterator.hasNext())
     {
-      UploadedFile file = iterator.next();
-      file.dispose();
+      List<UploadedFile> files = iterator.next();
+      if (files != null)
+      {
+        for (UploadedFile file: files)
+        {
+          if (file != null)
+          {
+            file.dispose();
+          }
+        }
+      }
     }
 
     _map.clear();
@@ -145,26 +239,39 @@ final public class UploadedFiles
     _totalMemory    = 0;
     _totalDiskSpace = 0;
   }
-
+  
   /**
    * Creates an UploadedFiles.
    */
   @SuppressWarnings("unchecked")
   UploadedFiles(ExternalContext externalContext)
   {
+    _map = new HashMap<String, List<UploadedFile>>();
     externalContext.getRequestMap().put(_UPLOADED_FILES_KEY, this);
-    _map = new HashMap<String, UploadedFile>();
   }
-
+  
+  /**
+   * Creates an UploadedFiles.
+   */
+  UploadedFiles()
+  {
+    _map = new HashMap<String, List<UploadedFile>>();
+  }
+  
   /**
    * Store a single UploadedFile.
    */
   void __put(String name, UploadedFile file)
   {
-    _map.put(name, file);
+    List<UploadedFile> files = _map.get(name);
+    if (files == null)
+    {
+      files = new ArrayList<UploadedFile>();
+    }
+    files.add(new FixFilename(file, _characterEncoding));
+    _map.put(name, files);
   }
 
-
   /**
    * Return the tally of total memory used.
    */
@@ -182,12 +289,10 @@ final public class UploadedFiles
     return _totalDiskSpace;
   }
 
-
-
   private long   _totalMemory;
   private long   _totalDiskSpace;
   private String _characterEncoding;
-  private final Map<String, UploadedFile> _map;
+  private final Map<String, List<UploadedFile>> _map;
 
   private static final String _UPLOADED_FILES_KEY =
     "org.apache.myfaces.trinidadinternal.webapp.UploadedFiles";

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextBean.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextBean.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextBean.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextBean.java Fri May 18 15:47:26 2012
@@ -78,6 +78,10 @@ public class RequestContextBean extends 
     TYPE.registerKey("uploaded-file-max-memory", Long.class);
   static public final PropertyKey UPLOADED_FILE_MAX_DISK_SPACE_KEY = 
     TYPE.registerKey("uploaded-file-max-disk-space", Long.class);
+  static public final PropertyKey UPLOADED_FILE_MAX_FILE_SIZE_KEY = 
+    TYPE.registerKey("uploaded-file-max-file-size", Long.class);
+  static public final PropertyKey UPLOADED_FILE_MAX_CHUNK_SIZE_KEY = 
+    TYPE.registerKey("uploaded-file-max-chunk-size", Long.class);
   static public final PropertyKey UPLOADED_FILE_TEMP_DIR_KEY = 
     TYPE.registerKey("uploaded-file-temp-dir");
   static public final PropertyKey REMOTE_DEVICE_REPOSITORY_URI =

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextImpl.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextImpl.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/context/RequestContextImpl.java Fri May 18 15:47:26 2012
@@ -178,6 +178,18 @@ public class RequestContextImpl extends 
   }
 
   @Override
+  public Long getUploadedFileMaxFileSize()
+  {
+    return (Long) _bean.getProperty(RequestContextBean.UPLOADED_FILE_MAX_FILE_SIZE_KEY);
+  }
+  
+  @Override
+  public Long getUploadedFileMaxChunkSize()
+  {
+    return (Long) _bean.getProperty(RequestContextBean.UPLOADED_FILE_MAX_CHUNK_SIZE_KEY);
+  }
+
+  @Override
   public String getUploadedFileTempDir()
   {
     return (String) _bean.getProperty(RequestContextBean.UPLOADED_FILE_TEMP_DIR_KEY);

Modified: myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java?rev=1340139&r1=1340138&r2=1340139&view=diff
==============================================================================
--- myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java (original)
+++ myfaces/trinidad/branches/1.2.12.7.0-branch/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/MRequestContext.java Fri May 18 15:47:26 2012
@@ -296,6 +296,28 @@ public class MRequestContext extends Req
     return _maxDiskSpace;
   }
 
+  public void setUploadedFileMaxFileSize(Long maxFileSize)
+  {
+    _maxFileSize = maxFileSize;
+  }
+  
+  @Override
+  public Long getUploadedFileMaxFileSize()
+  {
+    return _maxFileSize;
+  }
+  
+  public void setUploadedFileMaxChunkSize(Long maxChunkSize)
+  {
+    _maxChunkSize = maxChunkSize;
+  }
+
+  @Override
+  public Long getUploadedFileMaxChunkSize()
+  {
+    return _maxChunkSize;
+  }
+
   public void setUploadedFileTempDir(String tempDir)
   {
     _tempDir= tempDir;
@@ -457,6 +479,8 @@ public class MRequestContext extends Req
   private String _skin;
   private Long _maxMemory;
   private Long _maxDiskSpace;
+  private Long _maxFileSize;
+  private Long _maxChunkSize;
   private String _tempDir;
   private Accessibility _accMode;
   private AccessibilityProfile _accProfile;