Bug 497271 - [http servlet] complete implementation of the multipart API

Signed-off-by: Raymond Auge <raymond.auge@liferay.com>
Signed-off-by: Raymond Auge <raymond.auge@liferay.com>
Change-Id: Ic2ff6bd6eda8c77e11b990700acffe974475eb09
diff --git a/bundles/org.eclipse.equinox.http.jetty9/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.jetty9/META-INF/MANIFEST.MF
index 99da1ed..76a5c65 100644
--- a/bundles/org.eclipse.equinox.http.jetty9/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.jetty9/META-INF/MANIFEST.MF
@@ -4,7 +4,7 @@
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Bundle-SymbolicName: org.eclipse.equinox.http.jetty
-Bundle-Version: 3.3.100.qualifier
+Bundle-Version: 3.4.0.qualifier
 Bundle-Activator: org.eclipse.equinox.http.jetty.internal.Activator
 Import-Package: javax.servlet;version="[2.6.0,4.0.0)",
  javax.servlet.http;version="[2.6.0,4.0.0)",
@@ -24,7 +24,7 @@
  org.osgi.service.cm;version="1.2.0",
  org.osgi.service.startlevel;version="1.0"
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Export-Package: org.eclipse.equinox.http.jetty;version="1.3.0"
+Export-Package: org.eclipse.equinox.http.jetty;version="1.4.0"
 Comment-Header: Both Eclipse-LazyStart and Bundle-ActivationPolicy are specified for compatibility with 3.2
 Eclipse-LazyStart: true
 Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.equinox.http.jetty9/pom.xml b/bundles/org.eclipse.equinox.http.jetty9/pom.xml
index e1f9f5c..c38ebee 100644
--- a/bundles/org.eclipse.equinox.http.jetty9/pom.xml
+++ b/bundles/org.eclipse.equinox.http.jetty9/pom.xml
@@ -5,7 +5,7 @@
   are made available under the terms of the Eclipse Distribution License v1.0
   which accompanies this distribution, and is available at
   http://www.eclipse.org/org/documents/edl-v10.php
- 
+
   Contributors:
      Igor Fedorenko - initial implementation
      Raymond Augé - bug fixes and enhancements
@@ -21,6 +21,6 @@
   </parent>
   <groupId>org.eclipse.equinox</groupId>
   <artifactId>org.eclipse.equinox.http.jetty</artifactId>
-  <version>3.3.100-SNAPSHOT</version>
+  <version>3.4.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
diff --git a/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/JettyConstants.java b/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/JettyConstants.java
index f4fae3c..e9483bf 100644
--- a/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/JettyConstants.java
+++ b/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/JettyConstants.java
@@ -70,25 +70,25 @@
 	public static final String HTTP_MINTHREADS = "http.minThreads"; //$NON-NLS-1$
 
 	/**
-	 * name="multipart.fileSizeThreshold" type="Integer" (default: 8 -- size threshold after which the file will be written to disk)
+	 * @deprecated
 	 * @since 1.3
 	 */
 	public static final String MULTIPART_FILESIZETHRESHOLD = "multipart.fileSizeThreshold"; //$NON-NLS-1$
 
 	/**
-	 * name="multipart.location" type="String" (default: "" -- directory location where files will be stored)
+	 * @deprecated
 	 * @since 1.3
 	 */
 	public static final String MULTIPART_LOCATION = "multipart.location"; //$NON-NLS-1$
 
 	/**
-	 * name="multipart.maxFileSize" type="Long" (default: -1L -- maximum size allowed for uploaded files)
+	 * @deprecated
 	 * @since 1.3
 	 */
 	public static final String MULTIPART_MAXFILESIZE = "multipart.maxFileSize"; //$NON-NLS-1$
 
 	/**
-	 * name="multipart.maxRequestSize" type="Long" (default: -1L -- maximum size allowed for multipart/form-data requests)
+	 * @deprecated
 	 * @since 1.3
 	 */
 	public static final String MULTIPART_MAXREQUESTSIZE = "multipart.maxRequestSize"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java b/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
index 4170a62..3e9cb6a 100644
--- a/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
+++ b/bundles/org.eclipse.equinox.http.jetty9/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
@@ -23,7 +23,6 @@
 import javax.servlet.http.HttpSessionIdListener;
 import org.eclipse.equinox.http.jetty.JettyConstants;
 import org.eclipse.equinox.http.jetty.JettyCustomizer;
-import org.eclipse.equinox.http.servlet.HttpServiceMultipartServlet;
 import org.eclipse.equinox.http.servlet.HttpServiceServlet;
 import org.eclipse.jetty.server.*;
 import org.eclipse.jetty.server.session.HashSessionManager;
@@ -140,10 +139,6 @@
 		if (null != customizer)
 			httpContext = (ServletContextHandler) customizer.customizeContext(httpContext, dictionary);
 
-		ServletHolder multiPartHolder = createMultipartNamedServlet(dictionary, multipartServletName);
-		// This servlet has no mapping as it's only used from named dispatcher
-		httpContext.getServletHandler().addServlet(multiPartHolder);
-
 		SessionManager sessionManager = httpContext.getSessionHandler().getSessionManager();
 		try {
 			sessionManager.addEventListener((HttpSessionIdListener) holder.getServlet());
@@ -159,24 +154,6 @@
 		servers.put(pid, server);
 	}
 
-	private ServletHolder createMultipartNamedServlet(@SuppressWarnings("rawtypes") Dictionary dictionary, String multipartServletName) {
-		int multipartFileSizeThreshold = Details.getInt(dictionary, JettyConstants.MULTIPART_FILESIZETHRESHOLD, 0);
-		String multipartLocation = Details.getString(dictionary, JettyConstants.MULTIPART_LOCATION, ""); //$NON-NLS-1$
-		long multipartMaxFileSize = Details.getLong(dictionary, JettyConstants.MULTIPART_MAXFILESIZE, -1L);
-		long multipartMaxRequestSize = Details.getLong(dictionary, JettyConstants.MULTIPART_MAXREQUESTSIZE, -1L);
-
-		ServletHolder holder = new ServletHolder(new InternalHttpServiceMultipartServlet());
-		holder.setInitOrder(1);
-		holder.setName(multipartServletName);
-		holder.setInitParameter(Constants.SERVICE_VENDOR, "Eclipse.org"); //$NON-NLS-1$
-		holder.setInitParameter(Constants.SERVICE_DESCRIPTION, multipartServletName); //$NON-NLS-1$
-
-		MultipartConfigElement multipartConfigElement = new MultipartConfigElement(multipartLocation, multipartMaxFileSize, multipartMaxRequestSize, multipartFileSizeThreshold);
-		holder.getRegistration().setMultipartConfig(multipartConfigElement);
-
-		return holder;
-	}
-
 	private ServerConnector createHttpsConnector(@SuppressWarnings("rawtypes") Dictionary dictionary, Server server, HttpConfiguration http_config) {
 		ServerConnector httpsConnector = null;
 		if (Details.getBoolean(dictionary, JettyConstants.HTTPS_ENABLED, false)) {
@@ -339,39 +316,6 @@
 		}
 	}
 
-	public static class InternalHttpServiceMultipartServlet implements Servlet {
-		private Servlet httpServiceServlet = new HttpServiceMultipartServlet();
-		private ClassLoader contextLoader;
-
-		public void init(ServletConfig config) {
-			ServletContext context = config.getServletContext();
-			contextLoader = (ClassLoader) context.getAttribute(INTERNAL_CONTEXT_CLASSLOADER);
-		}
-
-		public void destroy() {
-			contextLoader = null;
-		}
-
-		public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
-			Thread thread = Thread.currentThread();
-			ClassLoader current = thread.getContextClassLoader();
-			thread.setContextClassLoader(contextLoader);
-			try {
-				httpServiceServlet.service(req, res);
-			} finally {
-				thread.setContextClassLoader(current);
-			}
-		}
-
-		public ServletConfig getServletConfig() {
-			return httpServiceServlet.getServletConfig();
-		}
-
-		public String getServletInfo() {
-			return httpServiceServlet.getServletInfo();
-		}
-	}
-
 	// deleteDirectory is a convenience method to recursively delete a directory
 	private static boolean deleteDirectory(File directory) {
 		if (directory.exists() && directory.isDirectory()) {
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml b/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml
index b9e033e..a67b23d 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml
@@ -46,7 +46,7 @@
                 <requirement>
                    <type>eclipse-plugin</type>
                    <id>org.eclipse.equinox.http.jetty</id>
-                   <versionRange>3.3.0</versionRange>
+                   <versionRange>3.4.0</versionRange>
                 </requirement>
              </extraRequirements>
           </dependency-resolution>
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
index 0991304..254440b 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
@@ -17,6 +17,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 
 import java.lang.reflect.InvocationTargetException;
@@ -111,8 +112,8 @@
 public class ServletTest extends BaseTest {
 	@Rule
 	public TestName testName = new TestName();
-	
-	
+
+
 	@Test
 	public void test_ErrorPage1() throws Exception {
 		String expected = "403 ERROR :";
@@ -1910,15 +1911,15 @@
 		Assert.assertEquals("b", requestAdvisor.request("files/help.txt"));
 	}
 
-	private static String getSubmittedFileName(Part part) {
-		for (String cd : part.getHeader("content-disposition").split(";")) {
-			if (cd.trim().startsWith("filename")) {
-				String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
-				return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
-			}
-		}
-		return null;
-	}
+//	private static String getSubmittedFileName(Part part) {
+//		for (String cd : part.getHeader("content-disposition").split(";")) {
+//			if (cd.trim().startsWith("filename")) {
+//				String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
+//				return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
+//			}
+//		}
+//		return null;
+//	}
 
 	/*
 	 * 3.1 file uploads
@@ -1961,14 +1962,38 @@
 		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
 
 		Assert.assertEquals("200", result.get("responseCode").get(0));
-		Assert.assertEquals("resource1.txt|text/plain|1", result.get("responseBody").get(0));
+		Assert.assertEquals("resource1.txt|text/plain|25", result.get("responseBody").get(0));
 	}
 
-	/*
-	 * 3.0 file uploads
-	 */
 	@Test
-	public void test_Servlet17() throws Exception {
+	public void test_Servlet16_notEnabled() throws Exception {
+		Servlet servlet = new HttpServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+				throws IOException, ServletException {
+
+				req.getPart("file");
+			}
+		};
+
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
+		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
+
+		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+
+		map.put("file", Arrays.<Object>asList(getClass().getResource("resource1.txt")));
+
+		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
+
+		Assert.assertEquals("500", result.get("responseCode").get(0));
+	}
+
+	@Test
+	public void test_Servlet16_fileuploadWithLocation() throws Exception {
 		Servlet servlet = new HttpServlet() {
 			private static final long serialVersionUID = 1L;
 
@@ -1979,16 +2004,22 @@
 				Part part = req.getPart("file");
 				Assert.assertNotNull(part);
 
-				String submittedFileName = getSubmittedFileName(part);
+				String submittedFileName = part.getSubmittedFileName();
 				String contentType = part.getContentType();
 				long size = part.getSize();
 
+				File tempDir = (File)getServletContext().getAttribute(ServletContext.TEMPDIR);
+				File location = new File(tempDir, "file-upload-test");
+
+				File[] listFiles = location.listFiles();
+
 				PrintWriter writer = resp.getWriter();
 
 				writer.write(submittedFileName);
 				writer.write("|");
 				writer.write(contentType);
 				writer.write("|" + size);
+				writer.write("|" + listFiles.length);
 			}
 		};
 
@@ -1996,19 +2027,176 @@
 		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
 		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
 		props.put("equinox.http.multipartSupported", Boolean.TRUE);
+		props.put("equinox.http.whiteboard.servlet.multipart.location", "file-upload-test");
 		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
 
 		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
 
-		map.put("file", Arrays.<Object>asList(getClass().getResource("blue.png")));
+		map.put("file", Arrays.<Object>asList(getClass().getResource("resource1.txt")));
 
 		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
 
 		Assert.assertEquals("200", result.get("responseCode").get(0));
-		Assert.assertEquals("blue.png|image/png|292", result.get("responseBody").get(0));
+		Assert.assertEquals("resource1.txt|text/plain|25|0", result.get("responseBody").get(0));
 	}
 
 	@Test
+	public void test_Servlet16_fileuploadWithLocationAndThreshold() throws Exception {
+		Servlet servlet = new HttpServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+				throws IOException, ServletException {
+
+				Part part = req.getPart("file");
+				Assert.assertNotNull(part);
+
+				String submittedFileName = part.getSubmittedFileName();
+				String contentType = part.getContentType();
+				long size = part.getSize();
+
+				File tempDir = (File)getServletContext().getAttribute(ServletContext.TEMPDIR);
+				File location = new File(tempDir, "file-upload-test");
+
+				File[] listFiles = location.listFiles();
+
+				PrintWriter writer = resp.getWriter();
+
+				writer.write(submittedFileName);
+				writer.write("|");
+				writer.write(contentType);
+				writer.write("|" + size);
+				writer.write("|" + listFiles.length);
+			}
+		};
+
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
+		props.put("equinox.http.multipartSupported", Boolean.TRUE);
+		props.put("equinox.http.whiteboard.servlet.multipart.location", "file-upload-test");
+		props.put("equinox.http.whiteboard.servlet.multipart.fileSizeThreshold", 10);
+		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
+
+		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+
+		map.put("file", Arrays.<Object>asList(getClass().getResource("resource1.txt")));
+
+		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
+
+		Assert.assertEquals("200", result.get("responseCode").get(0));
+		Assert.assertEquals("resource1.txt|text/plain|25|1", result.get("responseBody").get(0));
+	}
+
+	@Test
+	public void test_Servlet16_fileuploadWithLocationMaxFileSize() throws Exception {
+		Servlet servlet = new HttpServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+				throws IOException, ServletException {
+
+				req.getPart("file");
+			}
+		};
+
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
+		props.put("equinox.http.multipartSupported", Boolean.TRUE);
+		props.put("equinox.http.whiteboard.servlet.multipart.location", "file-upload-test");
+		// Note the actual uploaded file size is 25bytes
+		props.put("equinox.http.whiteboard.servlet.multipart.maxFileSize", 24L);
+		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
+
+		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+
+		map.put("file", Arrays.<Object>asList(getClass().getResource("resource1.txt")));
+
+		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
+
+		Assert.assertEquals("500", result.get("responseCode").get(0));
+	}
+
+	@Test
+	public void test_Servlet16_fileuploadWithLocationMaxRequestSize() throws Exception {
+		Servlet servlet = new HttpServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+				throws IOException, ServletException {
+
+				req.getPart("file");
+			}
+		};
+
+		Dictionary<String, Object> props = new Hashtable<String, Object>();
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
+		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
+		props.put("equinox.http.multipartSupported", Boolean.TRUE);
+		props.put("equinox.http.whiteboard.servlet.multipart.location", "file-upload-test");
+		// Note the actual uploaded file size is 25bytes, but you also need room for the rest of the headers
+		props.put("equinox.http.whiteboard.servlet.multipart.maxRequestSize", 26L);
+		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
+
+		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+
+		map.put("file", Arrays.<Object>asList(getClass().getResource("resource1.txt")));
+
+		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
+
+		Assert.assertEquals("500", result.get("responseCode").get(0));
+	}
+
+	/*
+	 * 3.0 file uploads
+	 */
+// This is commented due to a bug in commons-fileupload which was subsequently fixed in later versions.
+//	@Test
+//	public void test_Servlet17() throws Exception {
+//		Servlet servlet = new HttpServlet() {
+//			private static final long serialVersionUID = 1L;
+//
+//			@Override
+//			protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+//				throws IOException, ServletException {
+//
+//				Part part = req.getPart("file");
+//				Assert.assertNotNull(part);
+//
+//				String submittedFileName = getSubmittedFileName(part);
+//				String contentType = part.getContentType();
+//				long size = part.getSize();
+//
+//				PrintWriter writer = resp.getWriter();
+//
+//				writer.write(submittedFileName);
+//				writer.write("|");
+//				writer.write(contentType);
+//				writer.write("|" + size);
+//			}
+//		};
+//
+//		Dictionary<String, Object> props = new Hashtable<String, Object>();
+//		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S16");
+//		props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/Servlet16/*");
+//		props.put("equinox.http.multipartSupported", Boolean.TRUE);
+//		registrations.add(getBundleContext().registerService(Servlet.class, servlet, props));
+//
+//		Map<String, List<Object>> map = new HashMap<String, List<Object>>();
+//
+//		map.put("file", Arrays.<Object>asList(getClass().getResource("blue.png")));
+//
+//		Map<String, List<String>> result = requestAdvisor.upload("Servlet16/do", map);
+//
+//		Assert.assertEquals("200", result.get("responseCode").get(0));
+//		Assert.assertEquals("blue.png|image/png|292", result.get("responseBody").get(0));
+//	}
+
+	@Test
 	public void test_commonsFileUpload() throws Exception {
 		Servlet servlet = new HttpServlet() {
 			private static final long serialVersionUID = 1L;
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/resource1.txt b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/resource1.txt
index 2e65efe..d16f7af 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/resource1.txt
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/resource1.txt
@@ -1 +1,3 @@
-a
\ No newline at end of file
+a
+
+Do NOT edit this File!
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
index fd1c5c0..2ec3648 100644
--- a/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
@@ -3,15 +3,19 @@
 Bundle-Name: %bundleName
 Bundle-Vendor: %providerName
 Bundle-SymbolicName: org.eclipse.equinox.http.servlet
-Bundle-Version: 1.3.100.qualifier
+Bundle-Version: 1.4.0.qualifier
 Bundle-Activator: org.eclipse.equinox.http.servlet.internal.Activator
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.6
 Export-Package: org.eclipse.equinox.http.servlet;version="1.2.0",
- org.eclipse.equinox.http.servlet.context; x-internal:=true;version="1.0.0"
-Import-Package: javax.servlet;version="[2.3.0,4.0.0)",
- javax.servlet.annotation;version="2.6.0";resolution:=optional,
- javax.servlet.descriptor;version="2.6.0";resolution:=optional,
+ org.eclipse.equinox.http.servlet.context;version="1.0.0";x-internal:=true,
+ org.eclipse.equinox.http.servlet.dto;version="1.0.0";x-internal:=true
+Import-Package: org.apache.commons.fileupload;version="[1.2.2, 2.0.0)";resolution:=optional,
+ org.apache.commons.fileupload.disk;version="[1.2.2, 2.0.0)";resolution:=optional,
+ org.apache.commons.fileupload.servlet;version="[1.2.2, 2.0.0)";resolution:=optional,
+ javax.servlet;version="[2.3.0,4.0.0)",
+ javax.servlet.annotation;version="[2.6.0,4.0.0)";resolution:=optional,
+ javax.servlet.descriptor;version="[2.6.0,4.0.0)";resolution:=optional,
  javax.servlet.http;version="[2.3.0,4.0.0)",
  org.osgi.dto;version="[1.0.0,2.0)",
  org.osgi.framework;version="[1.3.0,2.0)",
diff --git a/bundles/org.eclipse.equinox.http.servlet/META-INF/services/org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupportFactory b/bundles/org.eclipse.equinox.http.servlet/META-INF/services/org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupportFactory
new file mode 100644
index 0000000..28ed86b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/META-INF/services/org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupportFactory
@@ -0,0 +1 @@
+org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupportFactoryImpl
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/pom.xml b/bundles/org.eclipse.equinox.http.servlet/pom.xml
index ff19811..4ac97a8 100644
--- a/bundles/org.eclipse.equinox.http.servlet/pom.xml
+++ b/bundles/org.eclipse.equinox.http.servlet/pom.xml
@@ -5,7 +5,7 @@
   are made available under the terms of the Eclipse Distribution License v1.0
   which accompanies this distribution, and is available at
   http://www.eclipse.org/org/documents/edl-v10.php
- 
+
   Contributors:
      Igor Fedorenko - initial implementation
      Raymond Augé - bug fixes and enhancements
@@ -20,6 +20,6 @@
   </parent>
   <groupId>org.eclipse.equinox</groupId>
   <artifactId>org.eclipse.equinox.http.servlet</artifactId>
-  <version>1.3.100-SNAPSHOT</version>
+  <version>1.4.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceMultipartServlet.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceMultipartServlet.java
index 67dbd6c..b9a4529 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceMultipartServlet.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/HttpServiceMultipartServlet.java
@@ -10,43 +10,13 @@
  *******************************************************************************/
 package org.eclipse.equinox.http.servlet;
 
-import org.eclipse.equinox.http.servlet.internal.servlet.ProxyMultipartServlet;
+import javax.servlet.http.HttpServlet;
 
 /**
- * The HttpServiceMultipartServlet is the "public" side of a Servlet that when registered (and init() called) in a servlet container
- * will be used by the OSGi Http Service implementation to handle multipart requests. This servlet must be paired with,
- * and initialized after an HttpServiceServlet. The HttpServiceServlet must be told the name of the multipart servlet it is paired with
- * using the init-param "multipart.servlet.name".
- * <p>
- * e.g.<br/>
- * <pre>
- * 	&lt;servlet>
- *		&lt;servlet-name>Equinox Http Service Servlet&lt;/servlet-name>
- *		&lt;servlet-class>org.eclipse.equinox.http.servlet.HttpServiceServlet&lt;/servlet-class>
- *		&lt;init-param>
- *			&lt;param-name>multipart.servlet.name&lt;/param-name>
- *			&lt;param-value>Equinox Http Service Multipart Servlet&lt;/param-value>
- *		&lt;/init-param>
- *		&lt;load-on-startup>0&lt;/load-on-startup>
- *	&lt;/servlet>
- *	&lt;servlet>
- *		&lt;servlet-name>Equinox Http Service Multipart Servlet&lt;/servlet-name>
- *		&lt;servlet-class>org.eclipse.equinox.http.servlet.HttpServiceMultipartServlet&lt;/servlet-class>
- *		&lt;load-on-startup>1&lt;/load-on-startup>
- *		&lt;multipart-config>
- *			&lt;location>&lt;/location>
- *			&lt;max-file-size>-1&lt;/max-file-size>
- *			&lt;max-request-size>-1&lt;/max-request-size>
- *			&lt;file-size-threshold>0&lt;/file-size-threshold>
- *		&lt;/multipart-config>
- *	&lt;/servlet>
- * </pre>
- * </p>
- * This class is not meant for extending or even using directly and is purely meant for registering
- * in a servlet container.
  * @noextend This class is not intended to be subclassed by clients.
+ * @deprecated No longer required.
  * @since 1.3
  */
-public class HttpServiceMultipartServlet extends ProxyMultipartServlet {
+public class HttpServiceMultipartServlet extends HttpServlet {
 	private static final long serialVersionUID = 2281118780429323631L;
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/dto/ExtendedFailedServletDTO.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/dto/ExtendedFailedServletDTO.java
new file mode 100644
index 0000000..1aa4340
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/dto/ExtendedFailedServletDTO.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.dto;
+
+import org.osgi.service.http.runtime.dto.FailedServletDTO;
+
+/**
+ * This type may become irrelevant if the properties appear as part of a
+ * future OSGi Http Whiteboard specification.
+ */
+public class ExtendedFailedServletDTO extends FailedServletDTO {
+
+	/**
+	 * Specifies whether multipart support is enabled.
+	 */
+	public boolean multipartEnabled;
+
+	/**
+	 * Specifies the size threshold after which the file will be written to disk.
+	 */
+	public int multipartFileSizeThreshold;
+
+	/**
+	 * Specifies the location where the files can be stored on disk.
+	 */
+	public String multipartLocation;
+
+	/**
+	 * Specifies the maximum size of a file being uploaded.
+	 */
+	public long multipartMaxFileSize;
+
+	/**
+	 * Specifies the maximum request size.
+	 */
+	public long multipartMaxRequestSize;
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/dto/ExtendedServletDTO.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/dto/ExtendedServletDTO.java
new file mode 100644
index 0000000..982966a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/dto/ExtendedServletDTO.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.dto;
+
+import org.osgi.service.http.runtime.dto.ServletDTO;
+
+/**
+ * This type may become irrelevant if the properties appear as part of a
+ * future OSGi Http Whiteboard specification.
+ */
+public class ExtendedServletDTO extends ServletDTO {
+
+	/**
+	 * Specifies whether multipart support is enabled.
+	 */
+	public boolean multipartEnabled;
+
+	/**
+	 * Specifies the size threshold after which the file will be written to disk.
+	 */
+	public int multipartFileSizeThreshold;
+
+	/**
+	 * Specifies the location where the files can be stored on disk.
+	 */
+	public String multipartLocation;
+
+	/**
+	 * Specifies the maximum size of a file being uploaded.
+	 */
+	public long multipartMaxFileSize;
+
+	/**
+	 * Specifies the maximum request size.
+	 */
+	public long multipartMaxRequestSize;
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
index 78a139f..cfd00e1 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
@@ -21,6 +21,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.eclipse.equinox.http.servlet.ExtendedHttpService;
+import org.eclipse.equinox.http.servlet.internal.util.Throw;
 import org.osgi.framework.Bundle;
 import org.osgi.service.http.*;
 
@@ -99,7 +100,7 @@
 			});
 		}
 		catch (PrivilegedActionException e) {
-			unchecked(e.getException());
+			Throw.unchecked(e.getException());
 		}
 
 	}
@@ -123,7 +124,7 @@
 				}
 			});
 		} catch (PrivilegedActionException e) {
-			unchecked(e.getException());
+			Throw.unchecked(e.getException());
 		}
 
 	}
@@ -149,7 +150,7 @@
 				}
 			});
 		} catch (PrivilegedActionException e) {
-			unchecked(e.getException());
+			Throw.unchecked(e.getException());
 		}
 	}
 
@@ -184,13 +185,4 @@
 				"Service instance is already shutdown"); //$NON-NLS-1$
 		}
 	}
-
-	static <T> T unchecked(Exception exception) {
-		return HttpServiceImpl.<T, RuntimeException> unchecked0(exception);
-	}
-
-	@SuppressWarnings("unchecked")
-	private static <T, E extends Exception> T unchecked0(Exception exception) throws E {
-		throw (E) exception;
-	}
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
index 6e5c7e4..9d39f3e 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
@@ -22,6 +22,7 @@
 import javax.servlet.http.HttpSessionAttributeListener;
 import javax.servlet.http.HttpSessionListener;
 import org.eclipse.equinox.http.servlet.context.ContextPathCustomizer;
+import org.eclipse.equinox.http.servlet.dto.ExtendedFailedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.context.*;
 import org.eclipse.equinox.http.servlet.internal.error.*;
 import org.eclipse.equinox.http.servlet.internal.servlet.Match;
@@ -467,11 +468,11 @@
 	}
 
 	private FailedServletDTO[] getFailedServletDTOs() {
-		Collection<FailedServletDTO> fsDTOs = failedServletDTOs.values();
+		Collection<ExtendedFailedServletDTO> fsDTOs = failedServletDTOs.values();
 
 		List<FailedServletDTO> copies = new ArrayList<FailedServletDTO>();
 
-		for (FailedServletDTO failedServletDTO : fsDTOs) {
+		for (ExtendedFailedServletDTO failedServletDTO : fsDTOs) {
 			copies.add(DTOUtil.clone(failedServletDTO));
 		}
 
@@ -1019,7 +1020,7 @@
 
 	public void recordFailedServletDTO(
 		ServiceReference<Servlet> serviceReference,
-		FailedServletDTO failedServletDTO) {
+		ExtendedFailedServletDTO failedServletDTO) {
 
 		if (failedServletDTOs.containsKey(serviceReference)) {
 			return;
@@ -1092,8 +1093,8 @@
 		new ConcurrentHashMap<ServiceReference<Object>, FailedResourceDTO>();
 	private final ConcurrentMap<ServiceReference<ServletContextHelper>, FailedServletContextDTO> failedServletContextDTOs =
 		new ConcurrentHashMap<ServiceReference<ServletContextHelper>, FailedServletContextDTO>();
-	private final ConcurrentMap<ServiceReference<Servlet>, FailedServletDTO> failedServletDTOs =
-		new ConcurrentHashMap<ServiceReference<Servlet>, FailedServletDTO>();
+	private final ConcurrentMap<ServiceReference<Servlet>, ExtendedFailedServletDTO> failedServletDTOs =
+		new ConcurrentHashMap<ServiceReference<Servlet>, ExtendedFailedServletDTO>();
 
 	private AtomicLong legacyIdGenerator = new AtomicLong(0);
 
@@ -1131,7 +1132,7 @@
 		public void checkForError() {
 			Exception result = error.get();
 			if (result != null) {
-				HttpServiceImpl.unchecked(result);
+				Throw.unchecked(result);
 			}
 		}
 	}
@@ -1169,7 +1170,7 @@
 					error.set(null);
 				} catch (Exception e){
 					error.set(e);
-					HttpServiceImpl.unchecked(e);
+					Throw.unchecked(e);
 				}
 			}
 
@@ -1205,7 +1206,7 @@
 				error.set(null);
 			} catch (Exception e){
 				error.set(e);
-				HttpServiceImpl.unchecked(e);
+				Throw.unchecked(e);
 			}
 		}
 
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
index 8b731bf..9af775c 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
@@ -21,9 +21,9 @@
 import javax.servlet.*;
 import javax.servlet.Filter;
 import javax.servlet.http.*;
+import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl;
 import org.eclipse.equinox.http.servlet.internal.customizer.*;
-import org.eclipse.equinox.http.servlet.internal.dto.ExtendedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.error.*;
 import org.eclipse.equinox.http.servlet.internal.registration.*;
 import org.eclipse.equinox.http.servlet.internal.registration.FilterRegistration;
@@ -424,8 +424,10 @@
 		try {
 			resourceRegistration.init(servletConfig);
 		}
-		catch (ServletException e) {
-			return null;
+		catch (Throwable t) {
+			resourceRegistration.destroy();
+
+			return Throw.unchecked(t);
 		}
 
 		endpointRegistrations.add(resourceRegistration);
@@ -451,6 +453,10 @@
 			}
 		} finally {
 			if (registration == null) {
+				// Always attempt to release here; even though destroy() may have been called
+				// on the registration while failing to add.  There are cases where no 
+				// ServletRegistration may have even been created at all to call destory() on.
+				// Also, addedRegisteredObject may be false which means we never call doAddServletRegistration
 				servletHolder.release();
 				if (addedRegisteredObject) {
 					httpServiceRuntime.getRegisteredObjects().remove(servlet);
@@ -483,8 +489,16 @@
 		String generatedServletName = ServiceProperties.parseName(
 			servletRef.getProperty(
 				HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME), servletHolder.get());
-		boolean multipartSupported = ServiceProperties.parseBoolean(
-			servletRef,	Const.EQUINOX_HTTP_MULTIPARTSUPPORTED);
+		boolean multipartEnabled = ServiceProperties.parseBoolean(
+			servletRef, Const.EQUINOX_HTTP_MULTIPART_ENABLED);
+		Integer multipartFileSizeThreshold = (Integer)servletRef.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_FILESIZETHRESHOLD);
+		String multipartLocation = (String)servletRef.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_LOCATION);
+		Long multipartMaxFileSize = (Long)servletRef.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_MAXFILESIZE);
+		Long multipartMaxRequestSize = (Long)servletRef.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE);
 
 		if (((patterns == null) || (patterns.length == 0)) &&
 			((errorPages == null) || errorPages.length == 0) &&
@@ -512,7 +526,11 @@
 
 		servletDTO.asyncSupported = asyncSupported;
 		servletDTO.initParams = servletInitParams;
-		servletDTO.multipartSupported = multipartSupported;
+		servletDTO.multipartEnabled = multipartEnabled;
+		servletDTO.multipartFileSizeThreshold = (multipartFileSizeThreshold != null ? multipartFileSizeThreshold : 0);
+		servletDTO.multipartLocation = (multipartLocation != null ? multipartLocation : Const.BLANK);
+		servletDTO.multipartMaxFileSize = (multipartMaxFileSize != null ? multipartMaxFileSize : -1L);
+		servletDTO.multipartMaxRequestSize = (multipartMaxRequestSize != null ? multipartMaxRequestSize : -1L);
 		servletDTO.name = generatedServletName;
 		servletDTO.patterns = sort(patterns);
 		servletDTO.serviceId = serviceId;
@@ -572,11 +590,18 @@
 			servletHolder.getBundle(), curServletContextHelper);
 		ServletRegistration servletRegistration = new ServletRegistration(
 			servletHolder, servletDTO, errorPageDTO, curServletContextHelper, this,
-			legacyTCCL);
+			servletContext, legacyTCCL);
 		ServletConfig servletConfig = new ServletConfigImpl(
 			generatedServletName, servletInitParams, servletContext);
 
-		servletRegistration.init(servletConfig);
+		try {
+			servletRegistration.init(servletConfig);
+		}
+		catch (Throwable t) {
+			servletRegistration.destroy();
+
+			return Throw.unchecked(t);
+		}
 
 		endpointRegistrations.add(servletRegistration);
 
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java
index 59e405c..33df57c 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java
@@ -57,8 +57,8 @@
 
 			recordFailedResourceDTO(serviceReference, hwfe.getFailureReason());
 		}
-		catch (Exception e) {
-			httpServiceRuntime.log(e.getMessage(), e);
+		catch (Throwable t) {
+			httpServiceRuntime.log(t.getMessage(), t);
 
 			recordFailedResourceDTO(serviceReference, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
 		}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java
index 388352a..6fb80e2 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java
@@ -13,6 +13,7 @@
 
 import java.util.concurrent.atomic.AtomicReference;
 import javax.servlet.Servlet;
+import org.eclipse.equinox.http.servlet.dto.ExtendedFailedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.error.HttpWhiteboardFailureException;
@@ -20,7 +21,6 @@
 import org.eclipse.equinox.http.servlet.internal.util.*;
 import org.osgi.framework.*;
 import org.osgi.service.http.runtime.dto.DTOConstants;
-import org.osgi.service.http.runtime.dto.FailedServletDTO;
 import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
 /**
@@ -59,8 +59,8 @@
 
 			recordFailedServletDTO(serviceReference, hwfe.getFailureReason());
 		}
-		catch (Exception e) {
-			httpServiceRuntime.log(e.getMessage(), e);
+		catch (Throwable t) {
+			httpServiceRuntime.log(t.getMessage(), t);
 
 			recordFailedServletDTO(serviceReference, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
 		}
@@ -94,13 +94,32 @@
 	private void recordFailedServletDTO(
 		ServiceReference<Servlet> serviceReference, int failureReason) {
 
-		FailedServletDTO failedServletDTO = new FailedServletDTO();
+		ExtendedFailedServletDTO failedServletDTO = new ExtendedFailedServletDTO();
 
 		failedServletDTO.asyncSupported = BooleanPlus.from(
 			serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED), false);
 		failedServletDTO.failureReason = failureReason;
 		failedServletDTO.initParams = ServiceProperties.parseInitParams(
 			serviceReference, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
+		failedServletDTO.multipartEnabled = ServiceProperties.parseBoolean(
+			serviceReference, Const.EQUINOX_HTTP_MULTIPART_ENABLED);
+		Integer multipartFileSizeThreshold = (Integer)serviceReference.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_FILESIZETHRESHOLD);
+		if (multipartFileSizeThreshold != null) {
+			failedServletDTO.multipartFileSizeThreshold = multipartFileSizeThreshold;
+		}
+		failedServletDTO.multipartLocation = (String)serviceReference.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_LOCATION);
+		Long multipartMaxFileSize = (Long)serviceReference.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_MAXFILESIZE);
+		if (multipartMaxFileSize != null) {
+			failedServletDTO.multipartMaxFileSize = multipartMaxFileSize;
+		}
+		Long multipartMaxRequestSize = (Long)serviceReference.getProperty(
+			Const.EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE);
+		if (multipartMaxRequestSize != null) {
+			failedServletDTO.multipartMaxRequestSize = multipartMaxRequestSize;
+		}
 		failedServletDTO.name = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
 		failedServletDTO.patterns = StringPlus.from(
 			serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN)).toArray(new String[0]);
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedServletDTO.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedServletDTO.java
deleted file mode 100644
index 88dccf5..0000000
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedServletDTO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Raymond Augé.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Raymond Augé - Initial implementation
- ******************************************************************************/
-
-package org.eclipse.equinox.http.servlet.internal.dto;
-
-import org.osgi.service.http.runtime.dto.ServletDTO;
-
-public class ExtendedServletDTO extends ServletDTO {
-	public boolean	multipartSupported = false;
-}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupport.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupport.java
new file mode 100644
index 0000000..a45c43a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupport.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal.multipart;
+
+import java.io.IOException;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
+
+public interface MultipartSupport {
+
+	public Map<String, Part> parseRequest(HttpServletRequest request) throws IOException, ServletException;
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportFactory.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportFactory.java
new file mode 100644
index 0000000..60c61c9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportFactory.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal.multipart;
+
+import javax.servlet.ServletContext;
+import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
+
+public interface MultipartSupportFactory {
+
+	MultipartSupport newInstance(ExtendedServletDTO servletDTO, ServletContext servletContext);
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportFactoryImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportFactoryImpl.java
new file mode 100644
index 0000000..bb54ca0
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportFactoryImpl.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal.multipart;
+
+import javax.servlet.ServletContext;
+import org.apache.commons.fileupload.FileUploadException;
+import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
+
+public class MultipartSupportFactoryImpl
+	implements MultipartSupportFactory {
+
+	public static final Class<?> FAIL_EARLY = FileUploadException.class;
+
+	@Override
+	public MultipartSupport newInstance(ExtendedServletDTO servletDTO, ServletContext servletContext) {
+		return new MultipartSupportImpl(servletDTO, servletContext);
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportImpl.java
new file mode 100644
index 0000000..e235758
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportImpl.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal.multipart;
+
+import java.io.*;
+import java.security.AccessControlContext;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.disk.DiskFileItem;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class MultipartSupportImpl implements MultipartSupport {
+
+	public MultipartSupportImpl(ExtendedServletDTO servletDTO, ServletContext servletContext) {
+		this.servletDTO = servletDTO;
+
+		// Must return non-null File. See Servlet 3.1 §4.8.1
+		File baseStorage = (File)servletContext.getAttribute(ServletContext.TEMPDIR);
+
+		if (servletDTO.multipartLocation.length() > 0) {
+			File storage = new File(servletDTO.multipartLocation);
+
+			if (!storage.isAbsolute()) {
+				storage = new File(baseStorage, storage.getPath());
+			}
+
+			baseStorage = storage;
+		}
+
+		checkPermission(baseStorage, servletContext);
+
+		baseStorage.mkdirs();
+
+		DiskFileItemFactory factory = new DiskFileItemFactory();
+
+		factory.setRepository(baseStorage);
+
+		if (servletDTO.multipartFileSizeThreshold > 0) {
+			factory.setSizeThreshold(servletDTO.multipartFileSizeThreshold);
+		}
+
+		upload = new ServletFileUpload(factory);
+
+		if (servletDTO.multipartMaxFileSize > -1L) {
+			upload.setFileSizeMax(servletDTO.multipartMaxFileSize);
+		}
+
+		if (servletDTO.multipartMaxRequestSize > -1L) {
+			upload.setSizeMax(servletDTO.multipartMaxRequestSize);
+		}
+	}
+
+	private void checkPermission(File baseStorage, ServletContext servletContext) {
+		BundleContext bundleContext = (BundleContext)servletContext.getAttribute("osgi-bundlecontext"); //$NON-NLS-1$
+		Bundle bundle = bundleContext.getBundle();
+		AccessControlContext accessControlContext = bundle.adapt(AccessControlContext.class);
+		if (accessControlContext == null) return;
+		accessControlContext.checkPermission(new FilePermission(baseStorage.getAbsolutePath(), "read,write")); //$NON-NLS-1$
+	}
+
+	public Map<String, Part> parseRequest(HttpServletRequest request) throws IOException, ServletException {
+		if (upload == null) {
+			throw new IllegalStateException("Servlet was not configured for multipart!"); //$NON-NLS-1$
+		}
+
+		if (!servletDTO.multipartEnabled) {
+			throw new IllegalStateException("No multipart config on " + servletDTO); //$NON-NLS-1$
+		}
+
+		if (!ServletFileUpload.isMultipartContent(request)) {
+			throw new ServletException("Not a multipart request!"); //$NON-NLS-1$
+		}
+
+		Map<String, Part> parts = new HashMap<String, Part>();
+
+		try {
+			for (Object item : upload.parseRequest(request)) {
+				DiskFileItem diskFileItem = (DiskFileItem)item;
+
+				parts.put(diskFileItem.getFieldName(), new MultipartSupportPart(diskFileItem));
+			}
+		}
+		catch (FileUploadException fnfe) {
+			throw new IOException(fnfe);
+		}
+
+		return parts;
+	}
+
+	private final ExtendedServletDTO servletDTO;
+	private final ServletFileUpload upload;
+
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportPart.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportPart.java
new file mode 100644
index 0000000..1902a8f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/multipart/MultipartSupportPart.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal.multipart;
+
+import java.io.*;
+import java.util.*;
+import javax.servlet.http.Part;
+import org.apache.commons.fileupload.FileItemHeaders;
+import org.apache.commons.fileupload.disk.DiskFileItem;
+
+public class MultipartSupportPart implements Part {
+
+	public MultipartSupportPart(DiskFileItem item) {
+		this.item = item;
+		this.headers = item.getHeaders();
+	}
+
+	@Override
+	public InputStream getInputStream() throws IOException {
+		return item.getInputStream();
+	}
+
+	@Override
+	public String getContentType() {
+		return item.getContentType();
+	}
+
+	@Override
+	public String getName() {
+		return item.getFieldName();
+	}
+
+	@Override
+	public String getSubmittedFileName() {
+		return item.getName();
+	}
+
+	@Override
+	public long getSize() {
+		return item.getSize();
+	}
+
+	@Override
+	public void write(String fileName) throws IOException {
+		try {
+			item.write(new File(item.getStoreLocation(), fileName));
+		}
+		catch (Exception e) {
+			throw new IOException(e);
+		}
+	}
+
+	@Override
+	public void delete() {
+		item.delete();
+	}
+
+	@Override
+	public String getHeader(String name) {
+		if (headers == null) {
+			return null;
+		}
+		return headers.getHeader(name);
+	}
+
+	@Override
+	public Collection<String> getHeaders(String name) {
+		if (headers == null) {
+			return Collections.emptyList();
+		}
+		return new IteratorCollection(headers.getHeaders(name));
+	}
+
+	@Override
+	public Collection<String> getHeaderNames() {
+		if (headers == null) {
+			return Collections.emptyList();
+		}
+		return new IteratorCollection(headers.getHeaderNames());
+	}
+
+	private final DiskFileItem item;
+	private final FileItemHeaders headers;
+
+	private class IteratorCollection extends AbstractList<String> {
+
+		public IteratorCollection(Iterator<String> iterator) {
+			this.collection = new ArrayList<String>();
+		    while (iterator.hasNext()) {
+		        collection.add(iterator.next());
+		    }
+		}
+
+		@Override
+		public String get(int index) {
+			return collection.get(index);
+		}
+
+		@Override
+		public int size() {
+			return collection.size();
+		}
+
+		private List<String> collection;
+
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java
index b16bb74..067a04f 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java
@@ -47,6 +47,7 @@
 		} else {
 			classLoader = servletHolder.getBundle().adapt(BundleWiring.class).getClassLoader();
 		}
+		createContextAttributes();
 	}
 
 	public void destroy() {
@@ -86,19 +87,13 @@
 
 	//Delegate the init call to the actual servlet
 	public void init(ServletConfig servletConfig) throws ServletException {
-		boolean initialized = false;
 		ClassLoader original = Thread.currentThread().getContextClassLoader();
 		try {
 			Thread.currentThread().setContextClassLoader(classLoader);
 
-			createContextAttributes();
 			getT().init(servletConfig);
-			initialized = true;
 		}
 		finally {
-			if (!initialized) {
-				destroyContextAttributes();
-			}
 			Thread.currentThread().setContextClassLoader(original);
 		}
 	}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java
index 127330b..63267f9 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java
@@ -12,10 +12,16 @@
  *******************************************************************************/
 package org.eclipse.equinox.http.servlet.internal.registration;
 
-import javax.servlet.Servlet;
+import java.io.IOException;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
+import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController.ServiceHolder;
-import org.eclipse.equinox.http.servlet.internal.dto.ExtendedServletDTO;
+import org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupport;
+import org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupportFactory;
 import org.eclipse.equinox.http.servlet.internal.servlet.Match;
 import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.service.http.runtime.dto.ErrorPageDTO;
@@ -23,14 +29,43 @@
 //This class wraps the servlet object registered in the HttpService.registerServlet call, to manage the context classloader when handleRequests are being asked.
 public class ServletRegistration extends EndpointRegistration<ExtendedServletDTO> {
 
+	private static MultipartSupportFactory factory;
+
+	static {
+		ServiceLoader<MultipartSupportFactory> loader = ServiceLoader.load(MultipartSupportFactory.class);
+
+		Iterator<MultipartSupportFactory> iterator = loader.iterator();
+
+		while (iterator.hasNext()) {
+			try {
+				factory = iterator.next();
+				break;
+			}
+			catch (Throwable t) {
+				// ignore, it means our optional imports are missing.
+			}
+		}
+	}
+
 	public ServletRegistration(
 		ServiceHolder<Servlet> servletHolder, ExtendedServletDTO servletDTO, ErrorPageDTO errorPageDTO,
 		ServletContextHelper servletContextHelper,
-		ContextController contextController, ClassLoader legacyTCCL) {
+		ContextController contextController, ServletContext servletContext, ClassLoader legacyTCCL) {
 
 		super(servletHolder, servletDTO, servletContextHelper, contextController, legacyTCCL);
 
 		this.errorPageDTO = errorPageDTO;
+
+		if (servletDTO.multipartEnabled) {
+			if (factory == null) {
+				throw new IllegalStateException(
+					"Multipart support not enabled due to missing, optional commons-fileupload dependency!"); //$NON-NLS-1$
+			}
+			multipartSupport = factory.newInstance(servletDTO, servletContext);
+		}
+		else {
+			multipartSupport = null;
+		}
 	}
 
 	public ErrorPageDTO getErrorPageDTO() {
@@ -74,6 +109,15 @@
 		return super.match(name, servletPath, pathInfo, extension, match);
 	}
 
-	private ErrorPageDTO errorPageDTO;
+	public Map<String, Part> parseRequest(HttpServletRequest request) throws IOException, ServletException {
+		if (multipartSupport == null) {
+			throw new IOException("Servlet not configured for multipart!"); //$NON-NLS-1$
+		}
+
+		return multipartSupport.parseRequest(request);
+	}
+
+	private final ErrorPageDTO errorPageDTO;
+	private final MultipartSupport multipartSupport;
 
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java
index 6bcb869..30f03be 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java
@@ -12,11 +12,15 @@
  *******************************************************************************/
 package org.eclipse.equinox.http.servlet.internal.servlet;
 
+import java.io.IOException;
 import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import javax.servlet.*;
 import javax.servlet.http.*;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.context.DispatchTargets;
+import org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration;
 import org.eclipse.equinox.http.servlet.internal.util.Const;
 import org.eclipse.equinox.http.servlet.internal.util.EventListeners;
 import org.osgi.service.http.HttpContext;
@@ -25,6 +29,8 @@
 
 	private final Stack<DispatchTargets> dispatchTargets = new Stack<DispatchTargets>();
 	private final HttpServletRequest request;
+	private Map<String, Part> parts;
+	private final Lock lock = new ReentrantLock();
 
 	private static final String[] dispatcherAttributes = new String[] {
 		RequestDispatcher.ERROR_EXCEPTION,
@@ -400,4 +406,45 @@
 		}
 	}
 
+	@Override
+	public Part getPart(String name) throws IOException, ServletException {
+		return getParts0().get(name);
+	}
+
+	@Override
+	public Collection<Part> getParts() throws IOException, ServletException {
+		return new ArrayList<Part>(getParts0().values());
+	}
+
+	private Map<String, Part> getParts0() throws IOException, ServletException {
+		org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration servletRegistration = getServletRegistration();
+
+		if (servletRegistration == null) {
+			throw new ServletException("Not a servlet request!"); //$NON-NLS-1$
+		}
+
+		lock.lock();
+
+		try {
+			if (parts != null) {
+				return parts;
+			}
+
+			return parts = servletRegistration.parseRequest(this);
+		}
+		finally {
+			lock.unlock();
+		}
+	}
+
+	private org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration getServletRegistration() {
+		EndpointRegistration<?> servletRegistration = dispatchTargets.peek().getServletRegistration();
+
+		if (servletRegistration instanceof org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration) {
+			return (org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration)servletRegistration;
+		}
+
+		return null;
+	}
+
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyMultipartServlet.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyMultipartServlet.java
deleted file mode 100644
index 7ad41de..0000000
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyMultipartServlet.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Raymond Augé and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Raymond Augé - initial implementation
- *******************************************************************************/
-package org.eclipse.equinox.http.servlet.internal.servlet;
-
-import java.io.IOException;
-import javax.servlet.ServletException;
-import javax.servlet.http.*;
-import org.eclipse.equinox.http.servlet.internal.context.DispatchTargets;
-import org.eclipse.equinox.http.servlet.internal.util.Const;
-
-/**
- * The ProxyMultipartServlet is the private side of a Servlet that when registered (and init() called) in a servlet container
- * will handle all multipart requests targeted toward it's associated ProxyServlet.
- * This class is not meant for extending or even using directly and is purely meant for registering
- * in a servlet container.
- */
-public class ProxyMultipartServlet extends HttpServlet {
-
-	private static final long serialVersionUID = -9079427283290998897L;
-
-	protected void service(
-			HttpServletRequest request, HttpServletResponse response)
-		throws ServletException, IOException {
-
-		String alias = HttpServletRequestWrapperImpl.getDispatchPathInfo(request);
-
-		if (alias == null) {
-			alias = Const.SLASH;
-		}
-
-		DispatchTargets dispatchTargets = (DispatchTargets)request.getAttribute(DispatchTargets.class.getName());
-
-		if (dispatchTargets != null) {
-			request.removeAttribute(DispatchTargets.class.getName());
-
-			dispatchTargets.doDispatch(
-				request, response, alias, request.getDispatcherType());
-
-			return;
-		}
-
-		response.sendError(
-			HttpServletResponse.SC_NOT_FOUND, "ProxyMultipartServlet: " + alias); //$NON-NLS-1$
-	}
-
-}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java
index 302dec1..08ec8f1 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java
@@ -18,8 +18,6 @@
 import org.eclipse.equinox.http.servlet.internal.Activator;
 import org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl;
 import org.eclipse.equinox.http.servlet.internal.context.DispatchTargets;
-import org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration;
-import org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration;
 import org.eclipse.equinox.http.servlet.internal.util.Const;
 
 /**
@@ -31,15 +29,11 @@
 public class ProxyServlet extends HttpServlet {
 
 	private static final long serialVersionUID = 4117456123807468871L;
-	protected static final String MULTIPART_SERVLET_NAME_KEY = "multipart.servlet.name"; //$NON-NLS-1$
 	private HttpServiceRuntimeImpl httpServiceRuntimeImpl;
-	private String multipartServletName;
 
 	public void init(ServletConfig config) throws ServletException {
 		super.init(config);
 
-		multipartServletName = getInitParameter(MULTIPART_SERVLET_NAME_KEY);
-
 		Activator.addProxyServlet(this);
 	}
 
@@ -76,26 +70,6 @@
 
 		DispatchTargets dispatchTargets = httpServiceRuntimeImpl.getDispatchTargets(alias, null);
 
-		if ((dispatchTargets != null) && (multipartServletName != null)) {
-			EndpointRegistration<?> endpointRegistration = dispatchTargets.getServletRegistration();
-
-			if (endpointRegistration instanceof ServletRegistration) {
-				ServletRegistration servletRegistration = (ServletRegistration)endpointRegistration;
-
-				if (servletRegistration.getD().multipartSupported) {
-					RequestDispatcher multipartDispatcher = getServletContext().getNamedDispatcher(multipartServletName);
-
-					if (multipartDispatcher != null) {
-						request.setAttribute(DispatchTargets.class.getName(), dispatchTargets);
-
-						multipartDispatcher.forward(request, response);
-
-						return;
-					}
-				}
-			}
-		}
-
 		if (dispatchTargets != null) {
 			dispatchTargets.doDispatch(
 				request, response, alias, request.getDispatcherType());
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
index 651e527..d577c05 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
@@ -32,7 +32,11 @@
 	public static final String SLASH_STAR = "/*"; //$NON-NLS-1$
 	public static final String SLASH_STAR_DOT = "/*."; //$NON-NLS-1$
 	public static final String STAR_DOT = "*."; //$NON-NLS-1$
-	public static final String EQUINOX_HTTP_MULTIPARTSUPPORTED = "equinox.http.multipartSupported"; //$NON-NLS-1$
+	public static final String EQUINOX_HTTP_MULTIPART_ENABLED = "equinox.http.multipartSupported"; //$NON-NLS-1$
+	public static final String EQUINOX_HTTP_MULTIPART_FILESIZETHRESHOLD = "equinox.http.whiteboard.servlet.multipart.fileSizeThreshold"; //$NON-NLS-1$
+	public static final String EQUINOX_HTTP_MULTIPART_LOCATION = "equinox.http.whiteboard.servlet.multipart.location"; //$NON-NLS-1$
+	public static final String EQUINOX_HTTP_MULTIPART_MAXFILESIZE = "equinox.http.whiteboard.servlet.multipart.maxFileSize"; //$NON-NLS-1$
+	public static final String EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE = "equinox.http.whiteboard.servlet.multipart.maxRequestSize"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_TCCL_PROP = "equinox.legacy.tccl"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_CONTEXT_SELECT = "equinox.context.select"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_CONTEXT_HELPER = "equinox.legacy.context.helper"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
index 883d715..5667c9f 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
@@ -13,7 +13,8 @@
 
 import java.lang.reflect.Array;
 import java.util.*;
-import org.eclipse.equinox.http.servlet.internal.dto.ExtendedServletDTO;
+import org.eclipse.equinox.http.servlet.dto.ExtendedFailedServletDTO;
+import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
 import org.osgi.dto.DTO;
 import org.osgi.service.http.runtime.dto.*;
 
@@ -95,12 +96,17 @@
 		return clone;
 	}
 
-	public static FailedServletDTO clone(FailedServletDTO original) {
-		FailedServletDTO clone = new FailedServletDTO();
+	public static ExtendedFailedServletDTO clone(ExtendedFailedServletDTO original) {
+		ExtendedFailedServletDTO clone = new ExtendedFailedServletDTO();
 
 		clone.asyncSupported = copy(original.asyncSupported);
 		clone.failureReason = copy(original.failureReason);
 		clone.initParams = copyStringMap(clone.initParams);
+		clone.multipartEnabled = copy(original.multipartEnabled);
+		clone.multipartFileSizeThreshold = copy(original.multipartFileSizeThreshold);
+		clone.multipartLocation = copy(original.multipartLocation);
+		clone.multipartMaxFileSize = copy(original.multipartMaxFileSize);
+		clone.multipartMaxRequestSize = copy(original.multipartMaxRequestSize);
 		clone.name = copy(original.name);
 		clone.patterns = copy(original.patterns);
 		clone.serviceId = copy(original.serviceId);
@@ -152,7 +158,11 @@
 
 		clone.asyncSupported = copy(original.asyncSupported);
 		clone.initParams = copyStringMap(original.initParams);
-		clone.multipartSupported = copy(original.multipartSupported);
+		clone.multipartEnabled = copy(original.multipartEnabled);
+		clone.multipartFileSizeThreshold = copy(original.multipartFileSizeThreshold);
+		clone.multipartLocation = copy(original.multipartLocation);
+		clone.multipartMaxFileSize = copy(original.multipartMaxFileSize);
+		clone.multipartMaxRequestSize = copy(original.multipartMaxRequestSize);
 		clone.name = copy(original.name);
 		clone.patterns = copy(original.patterns);
 		clone.serviceId = copy(original.serviceId);
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Throw.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Throw.java
new file mode 100644
index 0000000..85206d1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Throw.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 497271
+ *******************************************************************************/
+package org.eclipse.equinox.http.servlet.internal.util;
+
+public class Throw {
+
+	public static <T> T unchecked(Throwable throwable) {
+		return Throw.<T, RuntimeException>unchecked0(throwable);
+	}
+
+	@SuppressWarnings("unchecked")
+	private static <T, E extends Throwable> T unchecked0(Throwable throwable) throws E {
+		throw (E) throwable;
+	}
+
+}
\ No newline at end of file