You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2019/01/25 18:04:03 UTC

[myfaces-tobago] branch tobago-2.x updated: TOBAGO-1976: Backport to 2.x: Using Servlet 3.0 for uploading files with

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch tobago-2.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/tobago-2.x by this push:
     new 4546968  TOBAGO-1976: Backport to 2.x: Using Servlet 3.0 for uploading files with <tc:file>
4546968 is described below

commit 45469686ea6e7dcbb2489cd30fa3fbdaad71c166
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Fri Jan 25 19:03:41 2019 +0100

    TOBAGO-1976: Backport to 2.x: Using Servlet 3.0 for uploading files with <tc:file>
---
 pom.xml                                            |  14 +-
 tobago-assembly/pom.xml                            |  16 +-
 tobago-core/pom.xml                                |   6 +-
 .../tobago/internal/component/AbstractUIFile.java  |   6 +-
 .../tobago/internal/component/AbstractUIPage.java  |  30 --
 .../taglib/component/FileDropTagDeclaration.java   |   8 +-
 .../taglib/component/FileTagDeclaration.java       |   8 +-
 .../tobago/internal/util/HttpPartWrapper.java      | 115 +++++++
 .../myfaces/tobago/internal/util/PartUtils.java    | 331 +++++++++++++++++++++
 .../webapp/TobagoMultipartFormdataRequest.java     | 221 --------------
 .../tobago/validator/FileItemValidator.java        |   4 +-
 .../webapp/TobagoMultipartFormdataFilter.java      |  86 +-----
 .../tobago/internal/util/PartUtilsUnitTest.java    |  89 ++++++
 .../TobagoMultipartFormdataRequestUnitTest.java    | 165 ----------
 tobago-example/pom.xml                             |   2 +-
 .../tobago-example-addressbook-cdi/pom.xml         |   2 +-
 .../tobago/example/addressbook/Picture.java        |   8 +-
 .../tobago/example/addressbook/web/Controller.java |  12 +-
 .../src/main/webapp/WEB-INF/web.xml                |   2 +
 tobago-example/tobago-example-addressbook/pom.xml  |   2 +-
 .../tobago/example/addressbook/Picture.java        |   8 +-
 .../tobago/example/addressbook/web/Controller.java |  12 +-
 .../src/main/webapp/WEB-INF/web.xml                |   9 +-
 tobago-example/tobago-example-assembly/pom.xml     |   2 +-
 tobago-example/tobago-example-blank/pom.xml        |   2 +-
 .../src/main/webapp/WEB-INF/web.xml                |  14 -
 tobago-example/tobago-example-data/pom.xml         |   2 +-
 tobago-example/tobago-example-demo/pom.xml         |   2 +-
 .../apache/myfaces/tobago/example/demo/Upload.java |  76 ++---
 .../myfaces/tobago/example/demo/UploadItem.java    |   6 +-
 .../src/main/webapp/WEB-INF/web.xml                |  35 +--
 .../src/main/webapp/content/40-upload/upload.xhtml |  23 +-
 tobago-example/tobago-example-portlet/pom.xml      |   2 +-
 tobago-example/tobago-example-sandbox/pom.xml      |   2 +-
 .../src/main/webapp/WEB-INF/web.xml                |  23 +-
 tobago-example/tobago-example-security/pom.xml     |   2 +-
 .../src/main/webapp/WEB-INF/web.xml                |   9 +-
 tobago-example/tobago-example-test/pom.xml         |   2 +-
 .../src/main/webapp/WEB-INF/web.xml                |  36 +--
 tobago-extension/pom.xml                           |   2 +-
 tobago-extension/tobago-deprecation/pom.xml        |   2 +-
 tobago-extension/tobago-fileupload/pom.xml         |   2 +-
 .../FileUploadFacesContextFactoryImpl.java         | 108 +------
 tobago-extension/tobago-sandbox/pom.xml            |   2 +-
 tobago-extension/tobago-security/pom.xml           |   2 +-
 tobago-theme/pom.xml                               |   2 +-
 tobago-theme/tobago-theme-charlotteville/pom.xml   |   2 +-
 tobago-theme/tobago-theme-example/pom.xml          |   2 +-
 tobago-theme/tobago-theme-richmond/pom.xml         |   2 +-
 tobago-theme/tobago-theme-scarborough/pom.xml      |   2 +-
 tobago-theme/tobago-theme-speyside/pom.xml         |   2 +-
 tobago-theme/tobago-theme-standard/pom.xml         |   2 +-
 .../html/standard/standard/tag/FileRenderer.java   | 100 ++++---
 tobago-tool/pom.xml                                |   2 +-
 tobago-tool/tobago-theme-plugin/pom.xml            |   2 +-
 tobago-tool/tobago-tool-annotation/pom.xml         |   2 +-
 tobago-tool/tobago-tool-apt/pom.xml                |   2 +-
 57 files changed, 769 insertions(+), 865 deletions(-)

diff --git a/pom.xml b/pom.xml
index 3035c1a..3e9ca16 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
   <artifactId>tobago</artifactId>
   <packaging>pom</packaging>
   <name>Apache Tobago</name>
-  <version>2.3.1-SNAPSHOT</version>
+  <version>2.4.0-SNAPSHOT</version>
   <description>The goal of Tobago is to provide the community with a well designed set of user interface components based on JSF and run on MyFaces.</description>
   <prerequisites>
     <maven>3.0.4</maven>
@@ -207,7 +207,6 @@
             <link>http://download.oracle.com/javaee/5/api/</link>
             <link>http://jakarta.apache.org/commons/collections/api-${commons-collection.version}/</link>
             <link>http://jakarta.apache.org/commons/lang/api-${commons-lang.version}/</link>
-            <link>http://jakarta.apache.org/commons/fileupload/apidocs</link>
             <link>http://jakarta.apache.org/commons/io/api-${commons-io.version}/</link>
             <link>http://jakarta.apache.org/commons/logging/commons-logging-${commons-logging.version}/apidocs/</link>
             <link>http://www.slf4j.org/apidocs</link>
@@ -416,9 +415,9 @@
       </dependency>
       <dependency>
         <groupId>org.apache.geronimo.specs</groupId>
-        <artifactId>geronimo-servlet_2.5_spec</artifactId>
-        <version>1.2</version>
+        <artifactId>geronimo-servlet_3.0_spec</artifactId>
         <scope>provided</scope>
+        <version>1.0</version>
       </dependency>
       <dependency>
         <groupId>org.slf4j</groupId>
@@ -478,11 +477,6 @@
         <scope>provided</scope>
       </dependency>
       <dependency>
-        <groupId>commons-fileupload</groupId>
-        <artifactId>commons-fileupload</artifactId>
-        <version>1.3.3</version>
-      </dependency>
-      <dependency>
         <groupId>commons-codec</groupId>
         <artifactId>commons-codec</artifactId>
         <version>1.4</version>
@@ -574,7 +568,7 @@
 
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
-      <artifactId>geronimo-servlet_2.5_spec</artifactId>
+      <artifactId>geronimo-servlet_3.0_spec</artifactId>
     </dependency>
 
     <dependency>
diff --git a/tobago-assembly/pom.xml b/tobago-assembly/pom.xml
index 7c95ac0..1c0cdf2 100644
--- a/tobago-assembly/pom.xml
+++ b/tobago-assembly/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
 
   <dependencies>
@@ -84,19 +84,13 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>${project.groupId}</groupId>
-      <artifactId>tobago-fileupload</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
       <groupId>javax.servlet.jsp</groupId>
       <artifactId>jsp-api</artifactId>
       <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
-      <artifactId>geronimo-servlet_2.5_spec</artifactId>
-      <scope>provided</scope>
+      <artifactId>geronimo-servlet_3.0_spec</artifactId>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
@@ -269,12 +263,6 @@
                       <version>${project.version}</version>
                       <classifier>sources</classifier>
                     </artifactItem>
-                    <artifactItem>
-                      <groupId>${project.groupId}</groupId>
-                      <artifactId>tobago-fileupload</artifactId>
-                      <version>${project.version}</version>
-                      <classifier>sources</classifier>
-                    </artifactItem>
 
                   </artifactItems>
                   <outputDirectory>${project.build.directory}/src
diff --git a/tobago-core/pom.xml b/tobago-core/pom.xml
index 9d9917b..e847c9f 100644
--- a/tobago-core/pom.xml
+++ b/tobago-core/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-core</artifactId>
   <packaging>jar</packaging>
@@ -163,10 +163,6 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>commons-fileupload</groupId>
-      <artifactId>commons-fileupload</artifactId>
-    </dependency>
-    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java
index d3e69c5..2c03cef 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java
@@ -19,7 +19,6 @@
 
 package org.apache.myfaces.tobago.internal.component;
 
-import org.apache.commons.fileupload.FileItem;
 import org.apache.myfaces.tobago.component.UIFileInput;
 import org.apache.myfaces.tobago.layout.LayoutComponent;
 import org.apache.myfaces.tobago.util.MessageUtils;
@@ -27,13 +26,14 @@ import org.apache.myfaces.tobago.util.MessageUtils;
 import javax.faces.application.FacesMessage;
 import javax.faces.component.UIInput;
 import javax.faces.context.FacesContext;
+import javax.servlet.http.Part;
 
 public abstract class AbstractUIFile extends UIInput implements LayoutComponent, UIFileInput {
 
   public void validate(final FacesContext facesContext) {
     if (isRequired()) {
-      if (getSubmittedValue() instanceof FileItem) {
-        final FileItem file = (FileItem) getSubmittedValue();
+      if (getSubmittedValue() instanceof Part) {
+        final Part file = (Part) getSubmittedValue();
         if (file == null || file.getName().length() == 0) {
           addErrorMessage(facesContext);
           setValid(false);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPage.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPage.java
index e19978e..2c8a8e5 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPage.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPage.java
@@ -31,7 +31,6 @@ import org.apache.myfaces.tobago.internal.ajax.AjaxResponseRenderer;
 import org.apache.myfaces.tobago.internal.layout.LayoutUtils;
 import org.apache.myfaces.tobago.internal.util.Deprecation;
 import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
-import org.apache.myfaces.tobago.internal.webapp.TobagoMultipartFormdataRequest;
 import org.apache.myfaces.tobago.layout.Box;
 import org.apache.myfaces.tobago.layout.LayoutComponent;
 import org.apache.myfaces.tobago.layout.LayoutContainer;
@@ -52,13 +51,10 @@ import org.slf4j.LoggerFactory;
 
 import javax.el.ELContext;
 import javax.el.ValueExpression;
-import javax.faces.application.FacesMessage;
 import javax.faces.component.ContextCallback;
 import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
 import javax.faces.context.FacesContext;
-import javax.servlet.ServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.List;
@@ -125,8 +121,6 @@ public abstract class AbstractUIPage extends AbstractUIForm
 
   private void processDecodes0(final FacesContext facesContext) {
 
-    checkTobagoRequest(facesContext);
-
     decode(facesContext);
 
     markSubmittedForm(facesContext);
@@ -283,30 +277,6 @@ public abstract class AbstractUIPage extends AbstractUIForm
     }
   }
 
-  private void checkTobagoRequest(final FacesContext facesContext) {
-    // multipart/form-data must use TobagoMultipartFormdataRequest
-    final String contentType = facesContext.getExternalContext().getRequestHeaderMap().get("content-type");
-    if (contentType != null && contentType.startsWith("multipart/form-data")) {
-      final Object request = facesContext.getExternalContext().getRequest();
-      boolean okay = false;
-      if (request instanceof TobagoMultipartFormdataRequest) {
-        okay = true;
-      } else if (request instanceof HttpServletRequestWrapper) {
-        final ServletRequest wrappedRequest = ((HttpServletRequestWrapper) request).getRequest();
-        if (wrappedRequest instanceof TobagoMultipartFormdataRequest) {
-          okay = true;
-        }
-      }
-      // TODO PortletRequest ??
-      if (!okay) {
-        LOG.error("Can't process multipart/form-data without TobagoRequest. "
-            + "Please check the web.xml and define a TobagoMultipartFormdataFilter. "
-            + "See documentation for <tc:file>");
-        facesContext.addMessage(null, new FacesMessage("An error has occurred!"));
-      }
-    }
-  }
-
   /**
    * @deprecated PageState is deprecated since 1.5.0
    */
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileDropTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileDropTagDeclaration.java
index 9a60800..f4db455 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileDropTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileDropTagDeclaration.java
@@ -52,10 +52,6 @@ import javax.faces.component.UIInput;
 /**
  * Renders a file drop component.
  * This component always makes a partial (ajax) upload!
- * You need to define an org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataFilter in your web.xml or
- * add the tobago-fileupload.jar to your project.
- * The tobago-fileupload.jar contains a FacesContextFactory that wraps the
- * multipart-formdata request inside the facesContext.
  * <p />
  * For content constraints please use <a href="validateFileItem.html">tc:validateFileItem</a>.
  */
@@ -75,12 +71,12 @@ public interface FileDropTagDeclaration
 
   /**
    * Value binding expression pointing to a
-   * <code>org.apache.commons.fileupload.FileItem</code> property to store the
+   * <code>javax.servlet.http.Part</code> property to store the
    * uploaded file.
    */
   @TagAttribute()
   @UIComponentTagAttribute(
-      type = { "org.apache.commons.fileupload.FileItem", "org.apache.commons.fileupload.FileItem[]" },
+      type = { "javax.servlet.http.Part", "org.apache.commons.fileupload.FileItem[]" },
       expression = DynamicExpression.VALUE_EXPRESSION_REQUIRED)
   void setValue(String value);
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileTagDeclaration.java
index f5b4e16..8a699b2 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/FileTagDeclaration.java
@@ -51,10 +51,6 @@ import javax.faces.component.UIInput;
 
 /**
  * Renders a file input field.
- * You need to define an org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataFilter in your web.xml or
- * add the tobago-fileupload.jar to your project.
- * The tobago-fileupload.jar contains a FacesContextFactory that wraps the
- * multipart-formdata request inside the facesContext.
  * <p />
  * For content constraints please use <a href="validateFileItem.html">tc:validateFileItem</a>.
  */
@@ -78,12 +74,12 @@ public interface FileTagDeclaration
 
   /**
    * Value binding expression pointing to a
-   * <code>org.apache.commons.fileupload.FileItem</code> property to store the
+   * <code>javax.servlet.http.Part</code> property to store the
    * uploaded file.
    */
   @TagAttribute()
   @UIComponentTagAttribute(
-      type = { "org.apache.commons.fileupload.FileItem", "org.apache.commons.fileupload.FileItem[]" },
+      type = { "javax.servlet.http.Part", "javax.servlet.http.Part[]" },
       expression = DynamicExpression.VALUE_EXPRESSION_REQUIRED)
   void setValue(String value);
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/HttpPartWrapper.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/HttpPartWrapper.java
new file mode 100644
index 0000000..f898032
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/HttpPartWrapper.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.myfaces.tobago.internal.util;
+
+import javax.faces.FacesException;
+import javax.faces.FacesWrapper;
+import javax.faces.component.StateHolder;
+import javax.faces.context.FacesContext;
+import javax.servlet.http.Part;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+public class HttpPartWrapper implements Part, FacesWrapper<Part>, StateHolder {
+  private Part delegate;
+
+  public HttpPartWrapper() {
+  }
+
+  public HttpPartWrapper(Part delegate) {
+    this.delegate = delegate;
+  }
+
+  public void delete() throws IOException {
+    getWrapped().delete();
+  }
+
+  public String getContentType() {
+    return getWrapped().getContentType();
+  }
+
+  public String getHeader(String headerName) {
+    return getWrapped().getHeader(headerName);
+  }
+
+  public Collection<String> getHeaderNames() {
+    return getWrapped().getHeaderNames();
+  }
+
+  public Collection<String> getHeaders(String headerName) {
+    return getWrapped().getHeaders(headerName);
+  }
+
+  public InputStream getInputStream() throws IOException {
+    return getWrapped().getInputStream();
+  }
+
+  public String getName() {
+    return getWrapped().getName();
+  }
+
+  public long getSize() {
+    return getWrapped().getSize();
+  }
+
+  public void write(String fileName) throws IOException {
+    getWrapped().write(fileName);
+  }
+
+  public String getSubmittedFileName() {
+    Part wrapped = getWrapped();
+    try {
+      Method m = wrapped.getClass().getMethod("getSubmittedFileName");
+      return (String) m.invoke(wrapped);
+    } catch (NoSuchMethodException ex) {
+      throw new FacesException(ex);
+    } catch (SecurityException ex) {
+      throw new FacesException(ex);
+    } catch (IllegalAccessException ex) {
+      throw new FacesException(ex);
+    } catch (IllegalArgumentException ex) {
+      throw new FacesException(ex);
+    } catch (InvocationTargetException ex) {
+      throw new FacesException(ex);
+    }
+  }
+
+  public Object saveState(FacesContext context) {
+    return null;
+  }
+
+  public void restoreState(FacesContext context, Object state) {
+  }
+
+  public boolean isTransient() {
+    return true;
+  }
+
+  public void setTransient(boolean newTransientValue) {
+  }
+
+  public Part getWrapped() {
+    return delegate;
+  }
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/PartUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/PartUtils.java
new file mode 100644
index 0000000..04c90d8
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/PartUtils.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.myfaces.tobago.internal.util;
+
+import org.apache.commons.beanutils.PropertyUtils;
+
+import javax.servlet.http.Part;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Only needed for Servlet 3.0. Not needed for Servlet 3.1 or higher.
+ *
+ * Basically taken from Apache Tomcat 8
+ */
+public final class PartUtils {
+
+  private PartUtils() {
+  }
+
+  /**
+   * This is a helper method, to get the original file name of the upload.
+   * If you have at least Servlet 3.1, you wouldn't need this function.
+   *
+   * @since Tobago 3.0.0
+   */
+  public static String getSubmittedFileName(Part part) {
+
+    try { // try to call the Servlet 3.1 function
+      return (String) PropertyUtils.getProperty(part, "submittedFileName");
+    } catch (Exception e) {
+      // ignore
+    }
+
+    String fileName = null;
+    String cd = part.getHeader("Content-Disposition");
+    if (cd != null) {
+      String cdl = cd.toLowerCase(Locale.ENGLISH);
+      if (cdl.startsWith("form-data") || cdl.startsWith("attachment")) {
+        ParameterParser paramParser = new ParameterParser();
+        paramParser.setLowerCaseNames(true);
+        // Parameter parser can handle null input
+        Map<String, String> params =
+            paramParser.parse(cd, ';');
+        if (params.containsKey("filename")) {
+          fileName = params.get("filename");
+          if (fileName != null) {
+            fileName = fileName.trim();
+            // XXX seems to be wrong in the code?
+            fileName = fileName.replaceAll("\\\\\\\"", "\""); // replaces \" with "
+          } else {
+            // Even if there is no value, the parameter is present,
+            // so we return an empty file name rather than no file
+            // name.
+            fileName = "";
+          }
+        }
+      }
+    }
+    return fileName;
+  }
+
+  private static class ParameterParser {
+
+    /**
+     * String to be parsed.
+     */
+    private char[] chars = null;
+
+    /**
+     * Current position in the string.
+     */
+    private int pos = 0;
+
+    /**
+     * Maximum position in the string.
+     */
+    private int len = 0;
+
+    /**
+     * Start of a token.
+     */
+    private int i1 = 0;
+
+    /**
+     * End of a token.
+     */
+    private int i2 = 0;
+
+    /**
+     * Whether names stored in the map should be converted to lower case.
+     */
+    private boolean lowerCaseNames = false;
+
+    /**
+     * Default ParameterParser constructor.
+     */
+    public ParameterParser() {
+      super();
+    }
+
+    /**
+     * Are there any characters left to parse?
+     *
+     * @return {@code true} if there are unparsed characters,
+     * {@code false} otherwise.
+     */
+    private boolean hasChar() {
+      return this.pos < this.len;
+    }
+
+    /**
+     * A helper method to process the parsed token. This method removes
+     * leading and trailing blanks as well as enclosing quotation marks,
+     * when necessary.
+     *
+     * @param quoted {@code true} if quotation marks are expected,
+     *               {@code false} otherwise.
+     * @return the token
+     */
+    private String getToken(boolean quoted) {
+      // Trim leading white spaces
+      while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) {
+        i1++;
+      }
+      // Trim trailing white spaces
+      while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) {
+        i2--;
+      }
+      // Strip away quotation marks if necessary
+      if (quoted
+          && ((i2 - i1) >= 2)
+          && (chars[i1] == '"')
+          && (chars[i2 - 1] == '"')) {
+        i1++;
+        i2--;
+      }
+      String result = null;
+      if (i2 > i1) {
+        result = new String(chars, i1, i2 - i1);
+      }
+      return result;
+    }
+
+    /**
+     * Tests if the given character is present in the array of characters.
+     *
+     * @param ch      the character to test for presense in the array of characters
+     * @param charray the array of characters to test against
+     * @return {@code true} if the character is present in the array of
+     * characters, {@code false} otherwise.
+     */
+    private boolean isOneOf(char ch, final char[] charray) {
+      boolean result = false;
+      for (char element : charray) {
+        if (ch == element) {
+          result = true;
+          break;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Parses out a token until any of the given terminators
+     * is encountered.
+     *
+     * @param terminators the array of terminating characters. Any of these
+     *                    characters when encountered signify the end of the token
+     * @return the token
+     */
+    private String parseToken(final char[] terminators) {
+      char ch;
+      i1 = pos;
+      i2 = pos;
+      while (hasChar()) {
+        ch = chars[pos];
+        if (isOneOf(ch, terminators)) {
+          break;
+        }
+        i2++;
+        pos++;
+      }
+      return getToken(false);
+    }
+
+    /**
+     * Parses out a token until any of the given terminators
+     * is encountered outside the quotation marks.
+     *
+     * @param terminators the array of terminating characters. Any of these
+     *                    characters when encountered outside the quotation marks signify the end
+     *                    of the token
+     * @return the token
+     */
+    private String parseQuotedToken(final char[] terminators) {
+      char ch;
+      i1 = pos;
+      i2 = pos;
+      boolean quoted = false;
+      boolean charEscaped = false;
+      while (hasChar()) {
+        ch = chars[pos];
+        if (!quoted && isOneOf(ch, terminators)) {
+          break;
+        }
+        if (!charEscaped && ch == '"') {
+          quoted = !quoted;
+        }
+        charEscaped = (!charEscaped && ch == '\\');
+        i2++;
+        pos++;
+
+      }
+      return getToken(true);
+    }
+
+    /**
+     * Sets the flag if parameter names are to be converted to lower case when
+     * name/value pairs are parsed.
+     *
+     * @param b {@code true} if parameter names are to be
+     *          converted to lower case when name/value pairs are parsed.
+     *          {@code false} otherwise.
+     */
+    public void setLowerCaseNames(boolean b) {
+      this.lowerCaseNames = b;
+    }
+
+    /**
+     * Extracts a map of name/value pairs from the given string. Names are
+     * expected to be unique.
+     *
+     * @param str       the string that contains a sequence of name/value pairs
+     * @param separator the name/value pairs separator
+     * @return a map of name/value pairs
+     */
+    public Map<String, String> parse(final String str, char separator) {
+      if (str == null) {
+        return new HashMap<String, String>();
+      }
+      return parse(str.toCharArray(), separator);
+    }
+
+    /**
+     * Extracts a map of name/value pairs from the given array of
+     * characters. Names are expected to be unique.
+     *
+     * @param charArray the array of characters that contains a sequence of
+     *                  name/value pairs
+     * @param separator the name/value pairs separator
+     * @return a map of name/value pairs
+     */
+    public Map<String, String> parse(final char[] charArray, char separator) {
+      if (charArray == null) {
+        return new HashMap<String, String>();
+      }
+      return parse(charArray, 0, charArray.length, separator);
+    }
+
+    /**
+     * Extracts a map of name/value pairs from the given array of
+     * characters. Names are expected to be unique.
+     *
+     * @param charArray the array of characters that contains a sequence of
+     *                  name/value pairs
+     * @param offset    - the initial offset.
+     * @param length    - the length.
+     * @param separator the name/value pairs separator
+     * @return a map of name/value pairs
+     */
+    public Map<String, String> parse(
+        final char[] charArray,
+        int offset,
+        int length,
+        char separator) {
+
+      if (charArray == null) {
+        return new HashMap<String, String>();
+      }
+      HashMap<String, String> params = new HashMap<String, String>();
+      this.chars = charArray;
+      this.pos = offset;
+      this.len = length;
+
+      String paramName;
+      String paramValue;
+      while (hasChar()) {
+        paramName = parseToken(new char[]{
+            '=', separator
+        });
+        paramValue = null;
+        if (hasChar() && (charArray[pos] == '=')) {
+          pos++; // skip '='
+          paramValue = parseQuotedToken(new char[]{
+              separator
+          });
+        }
+        if (hasChar() && (charArray[pos] == separator)) {
+          pos++; // skip separator
+        }
+        if ((paramName != null) && (paramName.length() > 0)) {
+          if (this.lowerCaseNames) {
+            paramName = paramName.toLowerCase(Locale.ENGLISH);
+          }
+
+          params.put(paramName, paramValue);
+        }
+      }
+      return params;
+    }
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoMultipartFormdataRequest.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoMultipartFormdataRequest.java
deleted file mode 100644
index da5a254..0000000
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoMultipartFormdataRequest.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.myfaces.tobago.internal.webapp;
-
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileUploadException;
-import org.apache.commons.fileupload.disk.DiskFileItemFactory;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
-import org.apache.myfaces.tobago.internal.component.AbstractUIPage;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.faces.FacesException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-public class TobagoMultipartFormdataRequest extends HttpServletRequestWrapper {
-
-  private static final Logger LOG = LoggerFactory.getLogger(TobagoMultipartFormdataRequest.class);
-
-  public static final long ONE_KB = 1024;
-  public static final long ONE_MB = ONE_KB * ONE_KB;
-  public static final long ONE_GB = ONE_KB * ONE_MB;
-
-  private Map<String, String[]> parameters;
-
-  private Map<String, List<FileItem>> fileItems;
-
-  public TobagoMultipartFormdataRequest(final HttpServletRequest request) {
-    this(request, System.getProperty("java.io.tmpdir"), ONE_MB);
-  }
-
-  public TobagoMultipartFormdataRequest(
-      final HttpServletRequest request, final String repositoryPath, final long maxSize) {
-    super(request);
-    init(request, repositoryPath, maxSize);
-  }
-
-  private void init(final HttpServletRequest request, final String repositoryPath, final long maxSize) {
-    if (!ServletFileUpload.isMultipartContent(request)) {
-      final String errorText = "contentType is not multipart/form-data but '" + request.getContentType() + "'";
-      LOG.error(errorText);
-      throw new FacesException(errorText);
-    } else {
-      parameters = new HashMap<String, String[]>();
-      fileItems = new HashMap<String, List<FileItem>>();
-      final DiskFileItemFactory factory = new DiskFileItemFactory();
-
-      factory.setRepository(new File(repositoryPath));
-
-      final ServletFileUpload upload = new ServletFileUpload(factory);
-
-      upload.setSizeMax(maxSize);
-
-      if (upload.getHeaderEncoding() != null) {
-        // TODO: enable configuration of  'accept-charset'
-        upload.setHeaderEncoding(AbstractUIPage.FORM_ACCEPT_CHARSET);
-      }
-      final List<FileItem> itemList;
-      try {
-        itemList = (List<FileItem>) upload.parseRequest(request);
-      } catch (final FileUploadException e) {
-        //LOG.error(e);
-        throw new FacesException(e);
-      }
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("parametercount = " + itemList.size() + " + " + request.getParameterMap().size());
-      }
-      for (final FileItem item : itemList) {
-        final String key = item.getFieldName();
-        if (LOG.isDebugEnabled()) {
-          String value = item.getString();
-          if (value.length() > 100) {
-            value = value.substring(0, 100) + " [...]";
-          }
-          LOG.debug("Parameter: '" + key + "'='" + value + "' isFormField=" + item.isFormField()
-              + " contentType='" + item.getContentType() + "'");
-        }
-        if (item.isFormField()) {
-          String newValue;
-          try {
-            // TODO: enable configuration of 'accept-charset'
-            newValue = item.getString(AbstractUIPage.FORM_ACCEPT_CHARSET);
-          } catch (final UnsupportedEncodingException e) {
-            LOG.error("Caught: " + e.getMessage(), e);
-            newValue = item.getString();
-          }
-
-          addParameter(key, newValue);
-        } else {
-          List<FileItem> help = this.fileItems.get(key);
-          if (help == null) {
-            help = new ArrayList<FileItem>();
-            this.fileItems.put(key, help);
-          }
-          help.add(item);
-        }
-      }
-
-      // merging the GET parameters:
-      final Enumeration e = request.getParameterNames();
-      while(e.hasMoreElements()) {
-        final String name = (String) e.nextElement();
-        final String[] newValues = request.getParameterValues(name);
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Parameter: '" + name + "'='" + Arrays.toString(newValues) + "' (GET)");
-        }
-        for (final String newValue : newValues) {
-          addParameter(name, newValue);
-        }
-      }
-    }
-  }
-
-  private void addParameter(final String key, final String newValue) {
-    final String[] inStock = parameters.get(key);
-    final String[] values;
-    if (inStock == null) {
-      values = new String[]{newValue};
-    } else {
-      values = new String[inStock.length + 1];
-      System.arraycopy(inStock, 0, values, 0, inStock.length);
-      values[inStock.length] = newValue;
-    }
-    parameters.put(key, values);
-  }
-
-  public FileItem getFileItem(final String key) {
-    if (fileItems != null) {
-      final List<FileItem> fileItemsForKey = this.fileItems.get(key);
-      if (fileItemsForKey != null && fileItemsForKey.size() > 0) {
-        return fileItemsForKey.get(0);
-      } else {
-        return null;
-      }
-    }
-    return null;
-  }
-
-  public FileItem[] getFileItems(final String key) {
-    if (fileItems != null) {
-      final List<FileItem> fileItemsForKey = this.fileItems.get(key);
-      if (fileItemsForKey != null) {
-        return fileItemsForKey.toArray(new FileItem[fileItemsForKey.size()]);
-      }
-    }
-    return null;
-  }
-
-  public String getParameter(final String key) {
-    String parameter = null;
-    final String[] values = (String[]) parameters.get(key);
-    if (values != null) {
-      parameter = values[0];
-    }
-    return parameter;
-  }
-
-  public Enumeration getParameterNames() {
-    return Collections.enumeration(parameters.keySet());
-  }
-
-  public String[] getParameterValues(final String key) {
-    return (String[]) parameters.get(key);
-  }
-
-  public Map getParameterMap() {
-    return parameters;
-  }
-
-  public static long getMaxSize(final String param) {
-    if (param != null) {
-      String number = param.toLowerCase(Locale.ENGLISH);
-      long factor = 1;
-      if (number.endsWith("g")) {
-        factor = ONE_GB;
-        number = number.substring(0, number.length() - 1);
-      } else if (number.endsWith("m")) {
-        factor = ONE_MB;
-        number = number.substring(0, number.length() - 1);
-      } else if (number.endsWith("k")) {
-        factor = ONE_KB;
-        number = number.substring(0, number.length() - 1);
-      }
-      try {
-        return Long.parseLong(number.trim()) * factor;
-      } catch (final NumberFormatException e) {
-        LOG.error("Given max file size for "
-            + TobagoMultipartFormdataRequest.class.getName() + " " + param + " couldn't parsed to a number");
-      }
-    }
-    return ONE_MB;
-  }
-}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/validator/FileItemValidator.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/validator/FileItemValidator.java
index 30864cd..14c648b 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/validator/FileItemValidator.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/validator/FileItemValidator.java
@@ -19,7 +19,6 @@
 
 package org.apache.myfaces.tobago.validator;
 
-import org.apache.commons.fileupload.FileItem;
 import org.apache.myfaces.tobago.internal.component.AbstractUIFile;
 import org.apache.myfaces.tobago.internal.util.ContentType;
 import org.apache.myfaces.tobago.util.MessageUtils;
@@ -30,6 +29,7 @@ import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 import javax.faces.validator.Validator;
 import javax.faces.validator.ValidatorException;
+import javax.servlet.http.Part;
 import java.util.Arrays;
 
 /**
@@ -54,7 +54,7 @@ public class FileItemValidator implements Validator, StateHolder {
   public void validate(final FacesContext facesContext, final UIComponent component, final Object value)
       throws ValidatorException {
     if (value != null && component instanceof AbstractUIFile) {
-      final FileItem file = (FileItem) value;
+      final Part file = (Part) value;
       if (maxSize != null && file.getSize() > maxSize) {
         final FacesMessage facesMessage = MessageUtils.getMessage(
             facesContext, facesContext.getViewRoot().getLocale(), FacesMessage.SEVERITY_ERROR,
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoMultipartFormdataFilter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoMultipartFormdataFilter.java
index 38ed8e8..17aa873 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoMultipartFormdataFilter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoMultipartFormdataFilter.java
@@ -21,7 +21,6 @@ package org.apache.myfaces.tobago.webapp;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.myfaces.tobago.internal.webapp.TobagoMultipartFormdataRequest;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
@@ -29,99 +28,24 @@ import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import java.io.File;
 import java.io.IOException;
-import java.util.Locale;
-
 
 /**
- * This filter handles multipart request. It must be enabled in the web.xml of your web application.
- * Usage:
- * <p/>
- * <p><blockquote><pre>
- * &lt;filter&gt;
- * &lt;filter-name&gt;multipartFormdataFilter&lt;/filter-name&gt;
- * &lt;filter-class&gt;org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataFilter&lt;/filter-class&gt;
- * &lt;init-param&gt;
- * &lt;description&gt;Set the size limit for uploaded files. Default value is 1 MB.
- * Format: 10 = 10 bytes
- * 10k = 10 KB
- * 10m = 10 MB
- * 1g = 1 GB
- * &lt;/description&gt;
- * &lt;param-name&gt;uploadMaxFileSize&lt;/param-name&gt;
- * &lt;param-value&gt;20m&lt;/param-value&gt;
- * &lt;/init-param&gt;
- * &lt;init-param&gt;
- * &lt;description&gt;Set the upload repository path for uploaded files.
- * Default value is java.io.tmpdir.&lt;/description&gt;
- * &lt;param-name&gt;uploadRepositoryPath&lt;/param-name&gt;
- * &lt;param-value&gt;/tmp&lt;/param-value&gt;
- * &lt;/init-param&gt;
- * &lt;/filter&gt;
- * &lt;filter-mapping&gt;
- * &lt;filter-name&gt;multipartFormdataFilter&lt;/filter-name&gt;
- * &lt;url-pattern&gt;/faces/*&lt;/url-pattern&gt;
- * &lt;/filter-mapping&gt;
- * </pre></blockquote><p>
+ * @deprecated since 2.4.0
  */
+@Deprecated
 public class TobagoMultipartFormdataFilter implements Filter {
 
   private static final Logger LOG = LoggerFactory.getLogger(TobagoMultipartFormdataFilter.class);
 
-  private String repositoryPath = System.getProperty("java.io.tmpdir");
-  private long maxSize = TobagoMultipartFormdataRequest.ONE_MB;
-
   public void init(final FilterConfig filterConfig) throws ServletException {
-    final String repositoryPath = filterConfig.getInitParameter("uploadRepositoryPath");
-    if (repositoryPath != null) {
-      final File file = new File(repositoryPath);
-      if (!file.exists()) {
-        LOG.error("Given repository Path for " + getClass().getName() + " " + repositoryPath + " doesn't exists");
-      } else if (!file.isDirectory()) {
-        LOG.error("Given repository Path for " + getClass().getName() + " " + repositoryPath + " is not a directory");
-      } else {
-        this.repositoryPath = repositoryPath;
-      }
-    }
-
-
-    maxSize = TobagoMultipartFormdataRequest.getMaxSize(filterConfig.getInitParameter("uploadMaxFileSize"));
-    if (LOG.isInfoEnabled()) {
-      LOG.info("Configure uploadRepositoryPath for " + getClass().getName() + " to " + this.repositoryPath);
-      LOG.info("Configure uploadMaxFileSize for " + getClass().getName() + " to " + this.maxSize);
-    }
-
+    LOG.error("This class is no longer needed! Please remove from web.xml. "
+        + "See the release notes for Tobago 2.4.0.");
   }
 
   public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
       throws IOException, ServletException {
-    final ServletRequest wrapper;
-    if (request instanceof HttpServletRequest) {
-      if (request instanceof TobagoMultipartFormdataRequest) {
-        wrapper = request;
-      } else {
-        final String contentType = request.getContentType();
-        if (contentType != null
-            && contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/form-data")) {
-          if (LOG.isDebugEnabled()) {
-            LOG.debug("Wrapping " + request.getClass().getName()
-                + " with ContentType=\"" + contentType + "\" "
-                + "into TobagoMultipartFormdataRequest");
-          }
-          wrapper = new TobagoMultipartFormdataRequest(
-              (HttpServletRequest) request, repositoryPath, maxSize);
-        } else {
-          wrapper = request;
-        }
-      }
-    } else {
-      LOG.error("Not implemented for non HttpServletRequest");
-      wrapper = request;
-    }
-
-    chain.doFilter(wrapper, response);
+    chain.doFilter(request, response);
   }
 
   public void destroy() {
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/PartUtilsUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/PartUtilsUnitTest.java
new file mode 100644
index 0000000..298f295
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/PartUtilsUnitTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.myfaces.tobago.internal.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.servlet.http.Part;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+
+public class PartUtilsUnitTest {
+
+  @Test
+  public void testGetSubmittedFileName() throws Exception {
+
+    final String dc0 = "form-data; name=\"page:file\"; filename=\"foo.jpg\"";
+    Assert.assertEquals("foo.jpg", PartUtils.getSubmittedFileName(new PartMock(dc0)));
+
+    final String dc1 = "form-data; name=\"page:file\"; filename=\"foo;bar=\\\"boo\\\"-bar.jpg\"";
+    Assert.assertEquals("foo;bar=\"boo\"-bar.jpg", PartUtils.getSubmittedFileName(new PartMock(dc1)));
+  }
+
+  private static final class PartMock implements Part {
+
+    private String contentDisposition;
+
+    public PartMock(String contentDisposition) {
+      this.contentDisposition = contentDisposition;
+    }
+
+    public void delete() throws IOException {
+    }
+
+    public String getContentType() {
+      return null;
+    }
+
+    public String getHeader(String headerName) {
+      if (headerName.equals("Content-Disposition")) {
+        return contentDisposition;
+      } else {
+        return null;
+      }
+    }
+
+    public Collection<String> getHeaderNames() {
+      return null;
+    }
+
+    public Collection<String> getHeaders(String headerName) {
+      return null;
+    }
+
+    public InputStream getInputStream() throws IOException {
+      return null;
+    }
+
+    public String getName() {
+      return null;
+    }
+
+    public long getSize() {
+      return 0;
+    }
+
+    public void write(String fileName) throws IOException {
+
+    }
+  }
+}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/webapp/TobagoMultipartFormdataRequestUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/webapp/TobagoMultipartFormdataRequestUnitTest.java
deleted file mode 100644
index d4c6908..0000000
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/webapp/TobagoMultipartFormdataRequestUnitTest.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 org.apache.myfaces.tobago.webapp;
-
-import org.apache.commons.fileupload.FileItem;
-import org.apache.myfaces.tobago.internal.mock.servlet.MockHttpServletRequest;
-import org.apache.myfaces.tobago.internal.webapp.TobagoMultipartFormdataRequest;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class TobagoMultipartFormdataRequestUnitTest {
-
-  private static final String SNIP = "--";
-
-  private static final String BOUNDARY = "xxx";
-
-  private static final String NEWLINE = "\r\n";
-
-  private TobagoMultipartFormdataRequest request;
-
-  public TobagoMultipartFormdataRequestUnitTest() throws UnsupportedEncodingException {
-    final String body
-        = SNIP + BOUNDARY + NEWLINE
-        + parameter("color", "red")
-        + parameter("city", "Amsterdam")
-        + parameter("city", "Bonn")
-        + parameter("city", "Pisa")
-        + parameter("color", "green")
-        + fileItem("file", "hello.txt", "Hello World!")
-        + parameter("color", "blue")
-        + parameter("color", "yellow")
-        + parameter("country", "Trinidad & Tobago")
-        + SNIP + BOUNDARY + SNIP + NEWLINE;
-
-    final MockHttpServletRequest mockRequest
-        = new MockHttpServletRequest(body.getBytes("UTF-8"));
-    mockRequest.setMethod("post");
-
-    request = new TobagoMultipartFormdataRequest(mockRequest, System.getProperty("java.io.tmpdir"), 1024 * 1024);
-  }
-
-  private String parameter(final String key, final String value) {
-    return SNIP + BOUNDARY + NEWLINE
-        + "Content-Disposition: form-data; name=\"" + key + "\"" + NEWLINE
-        + NEWLINE
-        + value + NEWLINE;
-  }
-
-  private String fileItem(final String key, final String filename, final String value) {
-    return SNIP + BOUNDARY + NEWLINE
-        + "Content-Disposition: form-data; name=\""
-        + key + "\"; filename=\"" + filename + "\"" + NEWLINE
-        + "Content-Type: text/plain" + NEWLINE
-        + NEWLINE
-        + value + NEWLINE;
-  }
-
-  @Test
-  public void testGetFileItem() {
-
-    final FileItem item = request.getFileItem("file");
-    Assert.assertNotNull(item);
-    Assert.assertEquals("filename", "hello.txt", item.getName());
-    Assert.assertEquals("content", "Hello World!", item.getString());
-  }
-
-  @Test
-  public void testGetParameter() {
-
-    Assert.assertEquals("red", request.getParameter("color"));
-    Assert.assertEquals("Amsterdam", request.getParameter("city"));
-    Assert.assertEquals("Trinidad & Tobago", request.getParameter("country"));
-    Assert.assertEquals(null, request.getParameter("empty"));
-  }
-
-  @Test
-  public void testGetParameterValues() {
-
-    Set<String> expectedSet;
-    Set<String> actualSet;
-
-    expectedSet = new HashSet<String>(
-        Arrays.asList("red", "green", "blue", "yellow"));
-    actualSet
-        = new HashSet<String>(Arrays.asList(request.getParameterValues("color")));
-    Assert.assertEquals("color", expectedSet, actualSet);
-
-    expectedSet = new HashSet<String>(
-        Arrays.asList("Amsterdam", "Bonn", "Pisa"));
-    actualSet = new HashSet<String>(Arrays.asList(request.getParameterValues("city")));
-    Assert.assertEquals("city", expectedSet, actualSet);
-
-    expectedSet = new HashSet<String>(
-        Arrays.asList("Trinidad & Tobago"));
-    actualSet
-        = new HashSet<String>(Arrays.asList(request.getParameterValues("country")));
-    Assert.assertEquals("country", expectedSet, actualSet);
-
-    Assert.assertEquals("empty", null, request.getParameterValues("empty"));
-  }
-
-  @Test
-  public void testGetParameterNames() {
-
-    final Set<Object> actual = new HashSet<Object>();
-    final Enumeration e = request.getParameterNames();
-    while (e.hasMoreElements()) {
-      actual.add(e.nextElement());
-    }
-
-    final Set<String> expected = new HashSet<String>(
-        Arrays.asList("color", "city", "country"));
-
-    Assert.assertEquals(expected, actual);
-  }
-
-  @Test
-  public void testGetParameterMap() {
-
-    final Map actual = request.getParameterMap();
-
-    final Map<String, String[]> expected = new HashMap<String, String[]>();
-    expected.put("color", new String[]{"red", "green", "blue", "yellow"});
-    expected.put("city", new String[]{"Amsterdam", "Bonn", "Pisa"});
-    expected.put("country", new String[]{"Trinidad & Tobago"});
-
-    Assert.assertEquals(expected.keySet(), actual.keySet());
-
-    final Set keys = actual.keySet();
-    for (final Object key1 : keys) {
-      final String key = (String) key1;
-      final String[] expectedStrings = expected.get(key);
-      final String[] actualStrings = (String[]) actual.get(key);
-      Assert.assertEquals(expectedStrings.length, actualStrings.length);
-      final Set<String> expectedSet = new HashSet<String>(Arrays.asList(expectedStrings));
-      final Set<String> actualSet = new HashSet<String>(Arrays.asList(actualStrings));
-      Assert.assertEquals(expectedSet, actualSet);
-    }
-  }
-}
diff --git a/tobago-example/pom.xml b/tobago-example/pom.xml
index a903709..bcd5822 100644
--- a/tobago-example/pom.xml
+++ b/tobago-example/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <name>Tobago Examples</name>
diff --git a/tobago-example/tobago-example-addressbook-cdi/pom.xml b/tobago-example/tobago-example-addressbook-cdi/pom.xml
index ed38692..c108d3b 100644
--- a/tobago-example/tobago-example-addressbook-cdi/pom.xml
+++ b/tobago-example/tobago-example-addressbook-cdi/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-addressbook-cdi</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java b/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java
index 26adc96..7f60763 100644
--- a/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java
+++ b/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java
@@ -19,6 +19,8 @@
 
 package org.apache.myfaces.tobago.example.addressbook;
 
+import org.apache.commons.io.IOUtils;
+
 import javax.persistence.Basic;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
@@ -26,6 +28,8 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Lob;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.Serializable;
 
 @Entity
@@ -43,9 +47,9 @@ public class Picture implements Serializable {
   public Picture() {
   }
 
-  public Picture(final String contentType, final byte[] content) {
+  public Picture(final String contentType, final InputStream inputStream) throws IOException {
     this.contentType = contentType;
-    this.content = content;
+    this.content = IOUtils.toByteArray(inputStream);
   }
 
   public Integer getId() {
diff --git a/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java b/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java
index 1c52dda..d2bc460 100644
--- a/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java
+++ b/tobago-example/tobago-example-addressbook-cdi/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java
@@ -19,7 +19,6 @@
 
 package org.apache.myfaces.tobago.example.addressbook.web;
 
-import org.apache.commons.fileupload.FileItem;
 import org.apache.deltaspike.core.api.scope.WindowScoped;
 import org.apache.myfaces.tobago.component.UIColumn;
 import org.apache.myfaces.tobago.component.UISheet;
@@ -45,6 +44,7 @@ import javax.faces.validator.ValidatorException;
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
 import java.io.IOException;
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -93,7 +93,7 @@ public class Controller implements Serializable {
   @Inject
   private AddressDao addressDao;
 
-  private FileItem uploadedFile;
+  private Part uploadedFile;
   private boolean renderFileUploadPopup;
 
   static {
@@ -282,9 +282,9 @@ public class Controller implements Serializable {
     return OUTCOME_LIST;
   }
 
-  public String okFileUpload() {
+  public String okFileUpload() throws IOException {
     setRenderFileUploadPopup(false);
-    final Picture picture = new Picture(uploadedFile.getContentType(), uploadedFile.get());
+    final Picture picture = new Picture(uploadedFile.getContentType(), uploadedFile.getInputStream());
     currentAddress.setPicture(picture);
     return null;
   }
@@ -380,11 +380,11 @@ public class Controller implements Serializable {
     this.renderDayOfBirth = renderDayOfBirth;
   }
 
-  public FileItem getUploadedFile() {
+  public Part getUploadedFile() {
     return uploadedFile;
   }
 
-  public void setUploadedFile(final FileItem uploadedFile) {
+  public void setUploadedFile(final Part uploadedFile) {
     this.uploadedFile = uploadedFile;
   }
 
diff --git a/tobago-example/tobago-example-addressbook-cdi/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-addressbook-cdi/src/main/webapp/WEB-INF/web.xml
index de330e1..008e9a1 100644
--- a/tobago-example/tobago-example-addressbook-cdi/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-addressbook-cdi/src/main/webapp/WEB-INF/web.xml
@@ -55,6 +55,8 @@
     <servlet-name>FacesServlet</servlet-name>
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
+    <!-- This is needed, for file upload e.g. <tc:file> with JSF 2.0 or 2.1, JSF 2.2 doesn't need it. -->
+    <multipart-config/>
   </servlet>
 
   <servlet>
diff --git a/tobago-example/tobago-example-addressbook/pom.xml b/tobago-example/tobago-example-addressbook/pom.xml
index 13bfd49..aa0c1a6 100644
--- a/tobago-example/tobago-example-addressbook/pom.xml
+++ b/tobago-example/tobago-example-addressbook/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-addressbook</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java b/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java
index 26adc96..7f60763 100644
--- a/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java
+++ b/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/Picture.java
@@ -19,6 +19,8 @@
 
 package org.apache.myfaces.tobago.example.addressbook;
 
+import org.apache.commons.io.IOUtils;
+
 import javax.persistence.Basic;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
@@ -26,6 +28,8 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Lob;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.Serializable;
 
 @Entity
@@ -43,9 +47,9 @@ public class Picture implements Serializable {
   public Picture() {
   }
 
-  public Picture(final String contentType, final byte[] content) {
+  public Picture(final String contentType, final InputStream inputStream) throws IOException {
     this.contentType = contentType;
-    this.content = content;
+    this.content = IOUtils.toByteArray(inputStream);
   }
 
   public Integer getId() {
diff --git a/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java b/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java
index cfc753a..f296d02 100644
--- a/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java
+++ b/tobago-example/tobago-example-addressbook/src/main/java/org/apache/myfaces/tobago/example/addressbook/web/Controller.java
@@ -19,7 +19,6 @@
 
 package org.apache.myfaces.tobago.example.addressbook.web;
 
-import org.apache.commons.fileupload.FileItem;
 import org.apache.myfaces.tobago.component.UIColumn;
 import org.apache.myfaces.tobago.component.UISheet;
 import org.apache.myfaces.tobago.config.TobagoConfig;
@@ -45,6 +44,7 @@ import javax.faces.context.FacesContext;
 import javax.faces.event.ActionEvent;
 import javax.faces.validator.ValidatorException;
 import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -91,7 +91,7 @@ public class Controller {
   @Resource(name = "addressDao")
   private AddressDao addressDao;
 
-  private FileItem uploadedFile;
+  private Part uploadedFile;
   private boolean renderFileUploadPopup;
 
   static {
@@ -280,9 +280,9 @@ public class Controller {
     return OUTCOME_LIST;
   }
 
-  public String okFileUpload() {
+  public String okFileUpload() throws IOException {
     setRenderFileUploadPopup(false);
-    final Picture picture = new Picture(uploadedFile.getContentType(), uploadedFile.get());
+    final Picture picture = new Picture(uploadedFile.getContentType(), uploadedFile.getInputStream());
     currentAddress.setPicture(picture);
     return null;
   }
@@ -383,11 +383,11 @@ public class Controller {
     this.renderDayOfBirth = renderDayOfBirth;
   }
 
-  public FileItem getUploadedFile() {
+  public Part getUploadedFile() {
     return uploadedFile;
   }
 
-  public void setUploadedFile(final FileItem uploadedFile) {
+  public void setUploadedFile(final Part uploadedFile) {
     this.uploadedFile = uploadedFile;
   }
 
diff --git a/tobago-example/tobago-example-addressbook/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-addressbook/src/main/webapp/WEB-INF/web.xml
index 6fe65a9..a89f329 100644
--- a/tobago-example/tobago-example-addressbook/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-addressbook/src/main/webapp/WEB-INF/web.xml
@@ -17,10 +17,11 @@
  * limitations under the License.
 -->
 
-<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+<web-app
+    xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
-    version="2.4">
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+    version="3.0">
 
   <display-name>A simple addressbook demo with Tobago</display-name>
 
@@ -75,6 +76,8 @@
     <servlet-name>FacesServlet</servlet-name>
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
+    <!-- This is needed, for file upload e.g. <tc:file> with JSF 2.0 or 2.1, JSF 2.2 doesn't need it. -->
+    <multipart-config/>
   </servlet>
 
   <servlet>
diff --git a/tobago-example/tobago-example-assembly/pom.xml b/tobago-example/tobago-example-assembly/pom.xml
index 575d574..7cc7518 100644
--- a/tobago-example/tobago-example-assembly/pom.xml
+++ b/tobago-example/tobago-example-assembly/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
 
   <profiles>
diff --git a/tobago-example/tobago-example-blank/pom.xml b/tobago-example/tobago-example-blank/pom.xml
index 12a28dd..ff873df 100644
--- a/tobago-example/tobago-example-blank/pom.xml
+++ b/tobago-example/tobago-example-blank/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-blank</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-blank/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-blank/src/main/webapp/WEB-INF/web.xml
index dea7873..85ac5c1 100644
--- a/tobago-example/tobago-example-blank/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-blank/src/main/webapp/WEB-INF/web.xml
@@ -24,20 +24,6 @@
 
   <display-name>Blank Tobago Application</display-name>
 
-  <filter>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <filter-class>org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataFilter</filter-class>
-  </filter>
-  <filter-mapping>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <url-pattern>/faces/*</url-pattern>
-  </filter-mapping>
-
-<!--  workaround (e.g. for Oracle AS 10.1.2.0.0)-->
-  <listener>
-    <listener-class>org.apache.myfaces.tobago.webapp.TobagoServletContextListener</listener-class>
-  </listener>
-
   <!-- servlet -->
   <servlet>
     <servlet-name>FacesServlet</servlet-name>
diff --git a/tobago-example/tobago-example-data/pom.xml b/tobago-example/tobago-example-data/pom.xml
index 8a30946..2fc48d9 100644
--- a/tobago-example/tobago-example-data/pom.xml
+++ b/tobago-example/tobago-example-data/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-data</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-example/tobago-example-demo/pom.xml b/tobago-example/tobago-example-demo/pom.xml
index c7fcda6..92b2b56 100644
--- a/tobago-example/tobago-example-demo/pom.xml
+++ b/tobago-example/tobago-example-demo/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-demo</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Upload.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Upload.java
index db3c84a..f2ae483 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Upload.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Upload.java
@@ -19,10 +19,11 @@
 
 package org.apache.myfaces.tobago.example.demo;
 
-import org.apache.commons.fileupload.FileItem;
+import org.apache.myfaces.tobago.internal.util.PartUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.servlet.http.Part;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -30,86 +31,85 @@ public class Upload {
 
   private static final Logger LOG = LoggerFactory.getLogger(Upload.class);
 
-  private FileItem file1;
-  private FileItem file2;
-  private FileItem[] fileMulti;
-  private FileItem[] fileAjax;
-  private FileItem[] fileDnd;
+  private Part file1;
+  private Part file2;
+  private Part[] fileMulti;
+  private Part[] fileAjax;
+  private Part[] fileDnd;
 
   private List<UploadItem> list = new ArrayList<UploadItem>();
 
   public String upload() {
-   upload(file1);
-   upload(file2);
-   upload(fileMulti);
-   upload(fileAjax);
-   upload(fileDnd);
-      return null;
-    }
+    upload(file1);
+    upload(file2);
+    upload(fileMulti);
+    upload(fileAjax);
+    upload(fileDnd);
+    file1 = null;
+    file2 = null;
+    fileMulti = null;
+    fileAjax = null;
+    fileDnd = null;
+    return null;
+  }
 
-  public void upload(FileItem[] files) {
+  public void upload(Part[] files) {
     if (files != null) {
-      for (FileItem file : files) {
+      for (Part file : files) {
         upload(file);
       }
     }
   }
 
-  public void upload(FileItem file) {
+  public void upload(Part part) {
     LOG.info("checking file item");
-    if (file == null || file.get().length == 0) {
+    if (part == null || part.getSize() == 0) {
       return;
     }
-    LOG.info("type=" + file.getContentType());
-    LOG.info("size=" + file.get().length);
-    String name = file.getName();
-    final int pos = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\'));
-    if (pos >= 0) {
-      // some old browsers send the name with path.
-      // modern browsers doesn't because of security reasons.
-      name = name.substring(pos + 1);
-    }
-    LOG.info("name=" + name);
-    list.add(new UploadItem(name, file.get().length, file.getContentType()));
+    LOG.info("type='{}'", part.getContentType());
+    LOG.info("size={}", part.getSize());
+    final String submittedFileName = PartUtils.getSubmittedFileName(part);
+    LOG.info("name=" + submittedFileName);
+    list.add(new UploadItem(submittedFileName, part.getSize(), part.getContentType()));
   }
 
-  public FileItem getFile1() {
+  public Part getFile1() {
     return file1;
   }
 
-  public void setFile1(FileItem file1) {
+  public void setFile1(Part file1) {
     this.file1 = file1;
   }
 
-  public FileItem getFile2() {
+  public Part getFile2() {
     return file2;
   }
 
-  public void setFile2(FileItem file2) {
+  public void setFile2(Part file2) {
     this.file2 = file2;
   }
 
-  public FileItem[] getFileMulti() {
+  public Part[] getFileMulti() {
     return fileMulti;
   }
 
-  public void setFileMulti(FileItem[] fileMulti) {
+  public void setFileMulti(Part[] fileMulti) {
     this.fileMulti = fileMulti;
   }
 
-  public FileItem[] getFileAjax() {
+  public Part[] getFileAjax() {
     return fileAjax;
   }
 
-  public void setFileAjax(FileItem[] fileAjax) {
+  public void setFileAjax(Part[] fileAjax) {
     this.fileAjax = fileAjax;
   }
 
-  public FileItem[] getFileDnd() {
+  public Part[] getFileDnd() {
     return fileDnd;
   }
 
-  public void setFileDnd(FileItem[] fileDnd) {
+  public void setFileDnd(Part[] fileDnd) {
     this.fileDnd = fileDnd;
   }
 
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/UploadItem.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/UploadItem.java
index 2968d61..9a2e790 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/UploadItem.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/UploadItem.java
@@ -27,10 +27,10 @@ public class UploadItem {
   private static final Logger LOG = LoggerFactory.getLogger(UploadItem.class);
 
   private String name;
-  private int size;
+  private long size;
   private String type;
 
-  public UploadItem(final String name, final int size, final String type) {
+  public UploadItem(final String name, final long size, final String type) {
     this.name = name;
     this.size = size;
     this.type = type;
@@ -44,7 +44,7 @@ public class UploadItem {
     this.name = name;
   }
 
-  public int getSize() {
+  public long getSize() {
     return size;
   }
 
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
index 52d3f54..1676138 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/WEB-INF/web.xml
@@ -17,10 +17,11 @@
  * limitations under the License.
 -->
 
-<web-app xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
-         version="2.5">
+<web-app
+    xmlns="http://java.sun.com/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+    version="3.0">
 
   <display-name>Tobago Demo Application</display-name>
 
@@ -83,30 +84,6 @@
     <param-value>true</param-value>
   </context-param>
 
-  <filter>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <filter-class>org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataFilter</filter-class>
-    <init-param>
-      <description>Set the size limit for uploaded files. Default value is 1 MB.
-        Format: 10 = 10 bytes
-        10k = 10 KB
-        10m = 10 MB
-        1g = 1 GB
-      </description>
-      <param-name>uploadMaxFileSize</param-name>
-      <param-value>20m</param-value>
-    </init-param>
-    <!--<init-param>
-      <description>Set the upload repository path for uploaded files. Default value is java.io.tmpdir.</description>
-      <param-name>uploadRepositoryPath</param-name>
-      <param-value>/tmp</param-value>
-    </init-param>-->
-  </filter>
-  <filter-mapping>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <url-pattern>/faces/*</url-pattern>
-  </filter-mapping>
-
   <listener>
     <listener-class>org.apache.myfaces.tobago.example.demo.info.ActivitySessionListener</listener-class>
   </listener>
@@ -156,6 +133,8 @@
   <servlet>
     <servlet-name>FacesServlet</servlet-name>
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
+    <!-- This is needed, for file upload e.g. <tc:file> with JSF 2.0 or 2.1, JSF 2.2 doesn't need it. -->
+    <multipart-config/>
   </servlet>
 
   <servlet-mapping>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-upload/upload.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/40-upload/upload.xhtml
index d573127..712bdce 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/40-upload/upload.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/40-upload/upload.xhtml
@@ -25,10 +25,31 @@
   <ui:param name="title" value="File Upload"/>
   <tc:panel id="uploadPanel">
     <f:facet name="layout">
-      <tc:gridLayout rows="auto;*"/>
+      <tc:gridLayout rows="200px;auto;*"/>
     </f:facet>
 
     <tc:panel>
+      <p>
+        To load up files to the server for JSF 2.0 and 2.1 you will need to
+        add an multipart-config entry to the FacesServlet in the <code>web.xml</code> file.
+        Since JSF 2.2 this is not required.
+      </p>
+
+      <p>
+        Here you can configure some more general information.
+      </p>
+
+      <pre><code class="language-markup">
+      &lt;multipart-config>
+        &lt;location>/tmp&lt;/location>
+        &lt;max-file-size>20848820&lt;/max-file-size>
+        &lt;max-request-size>418018841&lt;/max-request-size>
+        &lt;file-size-threshold>1048576&lt;/file-size-threshold>
+      &lt;/multipart-config>
+    </code></pre>
+    </tc:panel>
+
+    <tc:panel>
       <f:facet name="layout">
         <tc:gridLayout columns="*;auto"/>
       </f:facet>
diff --git a/tobago-example/tobago-example-portlet/pom.xml b/tobago-example/tobago-example-portlet/pom.xml
index 44dd40b..0370675 100644
--- a/tobago-example/tobago-example-portlet/pom.xml
+++ b/tobago-example/tobago-example-portlet/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-portlet</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-sandbox/pom.xml b/tobago-example/tobago-example-sandbox/pom.xml
index 8daf44b..44fe8fe 100644
--- a/tobago-example/tobago-example-sandbox/pom.xml
+++ b/tobago-example/tobago-example-sandbox/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-sandbox</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-sandbox/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-sandbox/src/main/webapp/WEB-INF/web.xml
index ba1f716..740a2fa 100644
--- a/tobago-example/tobago-example-sandbox/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-sandbox/src/main/webapp/WEB-INF/web.xml
@@ -17,10 +17,11 @@
  * limitations under the License.
 -->
 
-<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+<web-app
+    xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
-    version="2.4">
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+    version="3.0">
 
   <display-name>Tobago Sandbox Application</display-name>
 
@@ -44,25 +45,13 @@
     <param-value>.xml</param-value>
   </context-param>
 
-  <filter>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <filter-class>org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataFilter</filter-class>
-  </filter>
-  <filter-mapping>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <url-pattern>/faces/*</url-pattern>
-  </filter-mapping>
-
-<!--  workaround (e.g. for Oracle AS 10.1.2.0.0)-->
-  <listener>
-    <listener-class>org.apache.myfaces.tobago.webapp.TobagoServletContextListener</listener-class>
-  </listener>
-
   <!-- servlet -->
   <servlet>
     <servlet-name>FacesServlet</servlet-name>
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
     <load-on-startup>3</load-on-startup>
+    <!-- This is needed, for file upload e.g. <tc:file> with JSF 2.0 or 2.1, JSF 2.2 doesn't need it. -->
+    <multipart-config/>
   </servlet>
 
   <servlet>
diff --git a/tobago-example/tobago-example-security/pom.xml b/tobago-example/tobago-example-security/pom.xml
index 38b3977..fdc2d6b 100644
--- a/tobago-example/tobago-example-security/pom.xml
+++ b/tobago-example/tobago-example-security/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-security</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-security/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-security/src/main/webapp/WEB-INF/web.xml
index 3ec1857..88a87c3 100644
--- a/tobago-example/tobago-example-security/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-security/src/main/webapp/WEB-INF/web.xml
@@ -17,10 +17,11 @@
  * limitations under the License.
 -->
 
-<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+<web-app
+    xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
-    version="2.4">
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+    version="3.0">
 
   <display-name>A simple security demo with Tobago</display-name>
 
@@ -35,6 +36,8 @@
     <servlet-name>FacesServlet</servlet-name>
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
+    <!-- This is needed, for file upload e.g. <tc:file> with JSF 2.0 or 2.1, JSF 2.2 doesn't need it. -->
+    <multipart-config/>
   </servlet>
 
   <servlet>
diff --git a/tobago-example/tobago-example-test/pom.xml b/tobago-example/tobago-example-test/pom.xml
index 8f45608..e3d6994 100644
--- a/tobago-example/tobago-example-test/pom.xml
+++ b/tobago-example/tobago-example-test/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-example</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-example-test</artifactId>
   <packaging>war</packaging>
diff --git a/tobago-example/tobago-example-test/src/main/webapp/WEB-INF/web.xml b/tobago-example/tobago-example-test/src/main/webapp/WEB-INF/web.xml
index fdfcfd2..3b71c0d 100644
--- a/tobago-example/tobago-example-test/src/main/webapp/WEB-INF/web.xml
+++ b/tobago-example/tobago-example-test/src/main/webapp/WEB-INF/web.xml
@@ -17,10 +17,11 @@
  * limitations under the License.
 -->
 
-<web-app xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
-         version="2.5">
+<web-app
+    xmlns="http://java.sun.com/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+    version="3.0">
 
   <display-name>Tobago Application for Quality Assurance</display-name>
   <description>Will be used for automated tests in the build process.</description>
@@ -55,6 +56,8 @@
     <servlet-name>FacesServlet</servlet-name>
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
     <load-on-startup>3</load-on-startup>
+    <!-- This is needed, for file upload e.g. <tc:file> with JSF 2.0 or 2.1, JSF 2.2 doesn't need it. -->
+    <multipart-config/>
   </servlet>
   <servlet-mapping>
     <servlet-name>FacesServlet</servlet-name>
@@ -153,29 +156,4 @@
       <url-pattern>/image/wait/*</url-pattern>
   </filter-mapping>
 
-  <filter>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <filter-class>org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataFilter</filter-class>
-    <init-param>
-      <description>Set the size limit for uploaded files. Default value is 1 MB.
-        Format: 10 = 10 bytes
-        10k = 10 KB
-        10m = 10 MB
-        1g = 1 GB
-      </description>
-      <param-name>uploadMaxFileSize</param-name>
-      <param-value>20m</param-value>
-    </init-param>
-    <!--<init-param>
-      <description>Set the upload repository path for uploaded files. Default value is java.io.tmpdir.</description>
-      <param-name>uploadRepositoryPath</param-name>
-      <param-value>/tmp</param-value>
-    </init-param>-->
-  </filter>
-  <filter-mapping>
-    <filter-name>multipartFormdataFilter</filter-name>
-    <url-pattern>/faces/*</url-pattern>
-  </filter-mapping>
-
-
 </web-app>
diff --git a/tobago-extension/pom.xml b/tobago-extension/pom.xml
index edb7e46..f3b7b43 100644
--- a/tobago-extension/pom.xml
+++ b/tobago-extension/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <name>Tobago Extensions</name>
diff --git a/tobago-extension/tobago-deprecation/pom.xml b/tobago-extension/tobago-deprecation/pom.xml
index 96759c8..868a303 100644
--- a/tobago-extension/tobago-deprecation/pom.xml
+++ b/tobago-extension/tobago-deprecation/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-deprecation</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-extension/tobago-fileupload/pom.xml b/tobago-extension/tobago-fileupload/pom.xml
index b398cf3..b740c9a 100644
--- a/tobago-extension/tobago-fileupload/pom.xml
+++ b/tobago-extension/tobago-fileupload/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <packaging>jar</packaging>
   <name>Tobago Fileupload</name>
diff --git a/tobago-extension/tobago-fileupload/src/main/java/org/apache/myfaces/tobago/fileupload/FileUploadFacesContextFactoryImpl.java b/tobago-extension/tobago-fileupload/src/main/java/org/apache/myfaces/tobago/fileupload/FileUploadFacesContextFactoryImpl.java
index 2c05b7b..a3dcddc 100644
--- a/tobago-extension/tobago-fileupload/src/main/java/org/apache/myfaces/tobago/fileupload/FileUploadFacesContextFactoryImpl.java
+++ b/tobago-extension/tobago-fileupload/src/main/java/org/apache/myfaces/tobago/fileupload/FileUploadFacesContextFactoryImpl.java
@@ -21,126 +21,32 @@ package org.apache.myfaces.tobago.fileupload;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.myfaces.tobago.internal.util.JndiUtils;
-import org.apache.myfaces.tobago.internal.webapp.TobagoMultipartFormdataRequest;
 
 import javax.faces.FacesException;
-import javax.faces.application.FacesMessage;
 import javax.faces.context.FacesContext;
 import javax.faces.context.FacesContextFactory;
 import javax.faces.lifecycle.Lifecycle;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.servlet.http.HttpServletRequest;
-import java.io.File;
 
 /**
- * This FacesContextFactory handles multipart request. Add the tobago-fileupload.jar to your web application.
- * Configuration:
- *
- * <p><blockquote><pre>
-    &lt;env-entry&gt;
-      &lt;description&gt;Set the size limit for uploaded files. Default value is 1 MB.
-        Format: 10 = 10 bytes
-        10k = 10 KB
-        10m = 10 MB
-        1g = 1 GB
-      &lt;/description&gt;
-      &lt;env-entry-name&gt;uploadMaxFileSize&lt;/env-entry-name&gt;
-      &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
-      &lt;env-entry-value&gt;20m&lt;/env-entry-value&gt;
-    &lt;/env-entry&gt;
-    &lt;env-entry&gt;
-      &lt;description&gt;Set the upload repository path for uploaded files.
-             Default value is java.io.tmpdir.&lt;/description&gt;
-      &lt;env-entry-name&gt;uploadRepositoryPath&lt;/env-entry-name&gt;
-      &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
-      &lt;env-entry-value&gt;/tmp&lt;/env-entry-value&gt;
-    &lt;/env-entry&gt;
- </pre></blockquote><p>
-
- *
+ * @deprecated Since 2.4.0
  */
+@Deprecated
 public class FileUploadFacesContextFactoryImpl extends FacesContextFactory {
+
   private static final Logger LOG = LoggerFactory.getLogger(FileUploadFacesContextFactoryImpl.class);
+
   private FacesContextFactory facesContextFactory;
-  private String repositoryPath = System.getProperty("java.io.tmpdir");
-  private long maxSize = TobagoMultipartFormdataRequest.ONE_MB;
 
   public FileUploadFacesContextFactoryImpl(final FacesContextFactory facesContextFactory) {
-    // TODO get Configuration from env entries in the web.xml or context-param
-    this.facesContextFactory = facesContextFactory;
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Wrap FacesContext for file upload");
-    }
-    InitialContext ic = null;
-    try {
-      ic = new InitialContext();
 
-      try {
-        final String repositoryPath = (String) JndiUtils.getJndiProperty(ic, "uploadRepositoryPath");
-        if (repositoryPath != null) {
-          final File file = new File(repositoryPath);
-          if (!file.exists()) {
-            LOG.error("Given repository Path for "
-                + getClass().getName() + " " + repositoryPath + " doesn't exists");
-          } else if (!file.isDirectory()) {
-            LOG.error("Given repository Path for "
-                + getClass().getName() + " " + repositoryPath + " is not a directory");
-          } else {
-            this.repositoryPath = repositoryPath;
-          }
-        }
-      } catch (final NamingException ne) {
-        // ignore
-      }
-
-      try {
-        final String size = (String) JndiUtils.getJndiProperty(ic, "uploadMaxFileSize");
-        maxSize = TobagoMultipartFormdataRequest.getMaxSize(size);
-      } catch (final NamingException ne) {
-        // ignore
-      }
-    } catch (final NamingException e) {
-      // ignore no naming available
-    } finally {
-      if (ic != null) {
-        try {
-          ic.close();
-        } catch (final NamingException e) {
-          // ignore
-        }
-      }
-    }
-    if (LOG.isInfoEnabled()) {
-      LOG.info("Configure uploadMaxFileSize for "+ getClass().getName() + " to "+ this.maxSize);
-      LOG.info("Configure uploadRepositoryPath for "+ getClass().getName() + " to "+ this.repositoryPath);
-    }
+    this.facesContextFactory = facesContextFactory;
+    LOG.error("This class is no longer needed! Please remove from web.xml. "
+        + "See the release notes for Tobago 2.4.0.");
   }
 
   public FacesContext getFacesContext(
       final Object context, Object request, final Object response, final Lifecycle lifecycle)
       throws FacesException {
-    if (request instanceof HttpServletRequest && !(request instanceof TobagoMultipartFormdataRequest)) {
-      final String contentType = ((HttpServletRequest) request).getContentType();
-      if (contentType != null && contentType.toLowerCase().startsWith("multipart/form-data")) {
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Wrap HttpServletRequest for file upload");
-        }
-        try {
-          request = new TobagoMultipartFormdataRequest((HttpServletRequest) request, repositoryPath, maxSize);
-        } catch (final FacesException e) {
-          LOG.error("", e);
-          final FacesContext facesContext = facesContextFactory.getFacesContext(context, request, response, lifecycle);
-          // TODO  better Message i18n Message?
-          final FacesMessage facesMessage
-              = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getCause().getMessage(), null);
-          facesContext.addMessage(null, facesMessage);
-          facesContext.renderResponse();
-          return facesContext;
-        }
-      }
-    }
     return facesContextFactory.getFacesContext(context, request, response, lifecycle);
   }
 }
diff --git a/tobago-extension/tobago-sandbox/pom.xml b/tobago-extension/tobago-sandbox/pom.xml
index 4d46a36..721af6e 100644
--- a/tobago-extension/tobago-sandbox/pom.xml
+++ b/tobago-extension/tobago-sandbox/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-sandbox</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-extension/tobago-security/pom.xml b/tobago-extension/tobago-security/pom.xml
index 6ebeeaa..c0ab349 100644
--- a/tobago-extension/tobago-security/pom.xml
+++ b/tobago-extension/tobago-security/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-extension</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <packaging>jar</packaging>
   <name>Tobago Security</name>
diff --git a/tobago-theme/pom.xml b/tobago-theme/pom.xml
index a245397..618a9c7 100644
--- a/tobago-theme/pom.xml
+++ b/tobago-theme/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <name>Tobago Themes</name>
diff --git a/tobago-theme/tobago-theme-charlotteville/pom.xml b/tobago-theme/tobago-theme-charlotteville/pom.xml
index 75e5152..7d11016 100644
--- a/tobago-theme/tobago-theme-charlotteville/pom.xml
+++ b/tobago-theme/tobago-theme-charlotteville/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-charlotteville</artifactId>
   <name>Tobago Theme Charlotteville</name>
diff --git a/tobago-theme/tobago-theme-example/pom.xml b/tobago-theme/tobago-theme-example/pom.xml
index 59eb8be..c4b2519 100644
--- a/tobago-theme/tobago-theme-example/pom.xml
+++ b/tobago-theme/tobago-theme-example/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-example</artifactId>
   <name>Tobago Theme Example</name>
diff --git a/tobago-theme/tobago-theme-richmond/pom.xml b/tobago-theme/tobago-theme-richmond/pom.xml
index 92e5f63..38d38eb 100644
--- a/tobago-theme/tobago-theme-richmond/pom.xml
+++ b/tobago-theme/tobago-theme-richmond/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-richmond</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-theme/tobago-theme-scarborough/pom.xml b/tobago-theme/tobago-theme-scarborough/pom.xml
index 14b16ce..1639e51 100644
--- a/tobago-theme/tobago-theme-scarborough/pom.xml
+++ b/tobago-theme/tobago-theme-scarborough/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-scarborough</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-theme/tobago-theme-speyside/pom.xml b/tobago-theme/tobago-theme-speyside/pom.xml
index a4fe088..4f63f0d 100644
--- a/tobago-theme/tobago-theme-speyside/pom.xml
+++ b/tobago-theme/tobago-theme-speyside/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-speyside</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-theme/tobago-theme-standard/pom.xml b/tobago-theme/tobago-theme-standard/pom.xml
index 013c602..158d64b 100644
--- a/tobago-theme/tobago-theme-standard/pom.xml
+++ b/tobago-theme/tobago-theme-standard/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-theme</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-standard</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/FileRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/FileRenderer.java
index 4cb2642..1d1faf2 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/FileRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/FileRenderer.java
@@ -19,12 +19,11 @@
 
 package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
 
-import org.apache.commons.fileupload.FileItem;
-
+import org.apache.myfaces.tobago.internal.util.HttpPartWrapper;
 import org.apache.myfaces.tobago.context.ResourceManagerUtils;
 import org.apache.myfaces.tobago.internal.component.AbstractUIFile;
 import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
-import org.apache.myfaces.tobago.internal.webapp.TobagoMultipartFormdataRequest;
+import org.apache.myfaces.tobago.internal.util.PartUtils;
 import org.apache.myfaces.tobago.layout.Measure;
 import org.apache.myfaces.tobago.renderkit.InputRendererBase;
 import org.apache.myfaces.tobago.renderkit.css.Classes;
@@ -34,15 +33,19 @@ import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
 import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.util.ComponentUtils;
+import org.apache.myfaces.tobago.validator.FileItemValidator;
 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
-import javax.servlet.ServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
+import javax.faces.validator.Validator;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 public class FileRenderer extends InputRendererBase {
 
@@ -62,46 +65,39 @@ public class FileRenderer extends InputRendererBase {
       return;
     }
 
-    final AbstractUIFile input = (AbstractUIFile) component;
-
-    TobagoMultipartFormdataRequest request = null;
-    final Object requestObject = facesContext.getExternalContext().getRequest();
-    if (requestObject instanceof TobagoMultipartFormdataRequest) {
-      request = (TobagoMultipartFormdataRequest) requestObject;
-    } else if (requestObject instanceof HttpServletRequestWrapper) {
-      final ServletRequest wrappedRequest
-          = ((HttpServletRequestWrapper) requestObject).getRequest();
-      if (wrappedRequest instanceof TobagoMultipartFormdataRequest) {
-        request = (TobagoMultipartFormdataRequest) wrappedRequest;
-      }
-    }
-    // TODO PortletRequest ??
-    if (request == null) {
-      // should not be possible, because of the check in UIPage
-      LOG.error("Can't process multipart/form-data without TobagoRequest. "
-          + "Please check the web.xml and define a TobagoMultipartFormdataFilter. "
-          + "See documentation for <tc:file>");
-    } else {
-
-      if (input.isMultiple()) {
-        final FileItem[] items = request.getFileItems(input.getClientId(facesContext));
-        if (LOG.isDebugEnabled()) {
-          for (FileItem item : items) {
-            LOG.debug("Uploaded file name : \"" + item.getName()
-                + "\"  size = " + item.getSize());
+    final AbstractUIFile file = (AbstractUIFile) component;
+    final boolean multiple = file.isMultiple() && !file.isRequired();
+    final Object request = facesContext.getExternalContext().getRequest();
+    if (request instanceof HttpServletRequest) {
+      try {
+        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        if (multiple) {
+          final List<Part> parts = new ArrayList<Part>();
+          for (final Part part : httpServletRequest.getParts()) {
+            if (file.getClientId(facesContext).equals(part.getName())) {
+              LOG.debug("Uploaded file '{}', size={}, type='{}'",
+                  PartUtils.getSubmittedFileName(part), part.getSize(), part.getContentType());
+              parts.add(new HttpPartWrapper(part));
+            }
+            file.setSubmittedValue(parts.toArray(new Part[0]));
+          }
+        } else {
+          final Part part = httpServletRequest.getPart(file.getClientId(facesContext));
+          final String submittedFileName = PartUtils.getSubmittedFileName(part);
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Uploaded file '{}', size={}, type='{}'",
+                submittedFileName, part.getSize(), part.getContentType());
+          }
+          if (submittedFileName.length() > 0) {
+            file.setSubmittedValue(new HttpPartWrapper(part));
           }
         }
-        input.setSubmittedValue(items);
-      } else {
-        final FileItem item = request.getFileItem(input.getClientId(facesContext));
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Uploaded file name : \"" + item.getName()
-              + "\"  size = " + item.getSize());
-        }
-        input.setSubmittedValue(item);
+      } catch (final Exception e) {
+        LOG.error("", e);
+        file.setValid(false);
       }
-      //TODO remove this
-      input.setValid(true);
+    } else { // todo: PortletRequest
+      LOG.warn("Unsupported request type: " + request.getClass().getName());
     }
   }
 
@@ -110,6 +106,7 @@ public class FileRenderer extends InputRendererBase {
     final AbstractUIFile file = (AbstractUIFile) component;
     final String clientId = file.getClientId(facesContext);
     final Style style = new Style(facesContext, file);
+    final String accept = createAcceptFromValidators(file);
 
     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
 
@@ -127,6 +124,7 @@ public class FileRenderer extends InputRendererBase {
     writer.writeAttribute(HtmlAttributes.MULTIPLE, file.isMultiple());
     writer.writeIdAttribute(clientId + ComponentUtils.SUB_SEPARATOR + "real");
     writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.FILE, false);
+    writer.writeAttribute(HtmlAttributes.ACCEPT, accept, true);
     writer.writeClassAttribute(getCssClasses(file, "real"));
     writer.writeNameAttribute(clientId);
     String multiFormat = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "tobago.file.multiFormat");
@@ -150,6 +148,24 @@ public class FileRenderer extends InputRendererBase {
     writer.endElement(HtmlElements.DIV);
   }
 
+  private String createAcceptFromValidators(final AbstractUIFile file) {
+    final StringBuilder builder = new StringBuilder();
+    for (Validator validator : file.getValidators()) {
+      if (validator instanceof FileItemValidator) {
+        final FileItemValidator fileItemValidator = (FileItemValidator) validator;
+        for (final String contentType : fileItemValidator.getContentType()) {
+          builder.append(",");
+          builder.append(contentType);
+        }
+      }
+    }
+    if (builder.length() > 0) {
+      return builder.substring(1);
+    } else {
+      return null;
+    }
+  }
+
   protected Classes getCssClasses(UIComponent component, String sub) {
     return Classes.create(component, sub);
   }
diff --git a/tobago-tool/pom.xml b/tobago-tool/pom.xml
index 376a125..0c4105c 100644
--- a/tobago-tool/pom.xml
+++ b/tobago-tool/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <name>Tobago Tool</name>
diff --git a/tobago-tool/tobago-theme-plugin/pom.xml b/tobago-tool/tobago-theme-plugin/pom.xml
index 4d5ed9c..2362e27 100644
--- a/tobago-tool/tobago-theme-plugin/pom.xml
+++ b/tobago-tool/tobago-theme-plugin/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-tool</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-theme-plugin</artifactId>
   <packaging>maven-plugin</packaging>
diff --git a/tobago-tool/tobago-tool-annotation/pom.xml b/tobago-tool/tobago-tool-annotation/pom.xml
index ab4e507..1998e71 100644
--- a/tobago-tool/tobago-tool-annotation/pom.xml
+++ b/tobago-tool/tobago-tool-annotation/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-tool</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-tool-annotation</artifactId>
   <packaging>jar</packaging>
diff --git a/tobago-tool/tobago-tool-apt/pom.xml b/tobago-tool/tobago-tool-apt/pom.xml
index 90fd188..42c97f3 100644
--- a/tobago-tool/tobago-tool-apt/pom.xml
+++ b/tobago-tool/tobago-tool-apt/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.apache.myfaces.tobago</groupId>
     <artifactId>tobago-tool</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
   </parent>
   <artifactId>tobago-tool-apt</artifactId>
   <packaging>jar</packaging>