From e6eed6ceaf481b5830df4ad4fb13bd2ea6af2ee7 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Fri, 21 Aug 2015 16:27:36 +0200
Subject: [PATCH] OO-1655: remove commons-fileupload and replace it with the
 standard support of Servlet 3.1

---
 pom.xml                                       |  17 +-
 .../org/olat/commons/servlets/RSSServlet.java |   2 -
 .../webdav/servlets/DefaultDispatcher.java    |   4 +-
 .../org/olat/core/gui/UserRequestImpl.java    |  10 +-
 .../components/form/flexible/impl/Form.java   | 179 +++++++-----------
 .../org/olat/core/gui/media/ServletUtil.java  |   4 +-
 .../olat/core/servlets/OpenOLATServlet.java   |   3 +
 .../org/olat/core/servlets/SpyFilter.java     |  21 +-
 .../org/olat/core/servlets/StaticServlet.java |   2 +-
 .../io/HttpServletResponseOutputStream.java   |  16 ++
 .../olat/restapi/support/MultipartReader.java |  72 ++-----
 src/main/webapp-tomcat/WEB-INF/web.xml        |  11 +-
 .../java/org/olat/restapi/CatalogTest.java    |   5 +-
 .../java/org/olat/restapi/CourseDBTest.java   |  32 ++--
 .../org/olat/restapi/NotificationsTest.java   |  20 +-
 .../olat/restapi/RepositoryEntriesTest.java   |   4 +-
 .../org/olat/restapi/UserCoursesTest.java     |   3 -
 .../org/olat/test/OlatJerseyTestCase.java     |   9 +-
 18 files changed, 199 insertions(+), 215 deletions(-)

diff --git a/pom.xml b/pom.xml
index 64bb39c8179..5be52f6bbdb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1878,7 +1878,7 @@
 		<dependency>
 			<groupId>javax.servlet</groupId>
 			<artifactId>javax.servlet-api</artifactId>
-			<version>3.0.1</version>
+			<version>3.1.0</version>
 			<scope>provided</scope>
 		</dependency>
 		<dependency>
@@ -1888,11 +1888,6 @@
 			<scope>test</scope>
 		</dependency>
 		<!-- core dependencies -->
-		<dependency>
-			<groupId>commons-fileupload</groupId>
-			<artifactId>commons-fileupload</artifactId>
-			<version>1.3.1</version>
-		</dependency>
 		<dependency>
 			<groupId>net.sourceforge.nekohtml</groupId>
 			<artifactId>nekohtml</artifactId>
@@ -2172,6 +2167,12 @@
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-web</artifactId>
 			<version>${org.springframework.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>commons-fileupload</groupId>
+					<artifactId>commons-fileupload</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 		<dependency>
 			<groupId>org.springframework</groupId>
@@ -2232,13 +2233,13 @@
 		<dependency>
 		    <groupId>io.undertow</groupId>
 		    <artifactId>undertow-core</artifactId>
-		    <version>1.1.2.Final</version>
+		    <version>1.2.9.Final</version>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
 		    <groupId>io.undertow</groupId>
 		    <artifactId>undertow-servlet</artifactId>
-		    <version>1.1.2.Final</version>
+		    <version>1.2.9.Final</version>
 			<scope>test</scope>
 		</dependency>
     
diff --git a/src/main/java/org/olat/commons/servlets/RSSServlet.java b/src/main/java/org/olat/commons/servlets/RSSServlet.java
index a402786bed0..f1b0a4a6397 100644
--- a/src/main/java/org/olat/commons/servlets/RSSServlet.java
+++ b/src/main/java/org/olat/commons/servlets/RSSServlet.java
@@ -133,8 +133,6 @@ public class RSSServlet extends HttpServlet {
 			if (pubDate != null) {
 				response.setDateHeader("Last-Modified", pubDate.getTime());
 			}
-			// TODO:GW Do we need this?
-			// response.setContentLength(feed.get);
 
 			writer = response.getWriter();
 			SyndFeedOutput output = new SyndFeedOutput();
diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java
index 9be10d0588e..3cef3bff454 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/DefaultDispatcher.java
@@ -482,7 +482,7 @@ public abstract class DefaultDispatcher implements Serializable {
                 // Don't set a content length if something else has already
                 // written to the response.
                 if (contentWritten == 0) {
-                    response.setContentLength((int)contentLength);//TODO tomcat
+                    response.setContentLengthLong(contentLength);
                 }
             }
 
@@ -543,7 +543,7 @@ public abstract class DefaultDispatcher implements Serializable {
                                    + "-" + range.end + "/"
                                    + range.length);
                 long length = range.end - range.start + 1;
-                response.setContentLength((int)length);//TODO tomcat
+                response.setContentLengthLong(length);
 
                 if (contentType != null) {
                     if (debug)
diff --git a/src/main/java/org/olat/core/gui/UserRequestImpl.java b/src/main/java/org/olat/core/gui/UserRequestImpl.java
index e498358c5ff..2a921387ee2 100644
--- a/src/main/java/org/olat/core/gui/UserRequestImpl.java
+++ b/src/main/java/org/olat/core/gui/UserRequestImpl.java
@@ -26,6 +26,7 @@
 
 package org.olat.core.gui;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.Enumeration;
@@ -37,6 +38,7 @@ import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.StringTokenizer;
 
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -253,7 +255,7 @@ public class UserRequestImpl implements UserRequest {
 		String contentType = hreq.getContentType();
 
 		// do not waste inputstream on file uploads
-		if (contentType == null || !contentType.startsWith("multipart/form-data")) {
+		if (contentType == null || !contentType.startsWith("multipart/")) {
 			//if you encouter problems with content in url like german umlauts
 			//make sure you set <Connector port="8080" URIEncoding="utf-8" /> to utf-8 encoding
 			//this will decode content in get requests like request.getParameter(...
@@ -263,6 +265,12 @@ public class UserRequestImpl implements UserRequest {
 				String val = hreq.getParameterValues(key)[0];
 				params.put(key, val);
 			}
+		} else if(contentType.startsWith("multipart/")) {
+			try {
+				hreq.getParts();
+			} catch (IOException | ServletException e) {
+				log.error("", e);
+			}
 		}
 
 		nonParsedUri = decodedUri.substring(uriPrefix.length()); // guaranteed to
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java
index 140c93369f6..b4321cf3337 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java
@@ -27,23 +27,22 @@ package org.olat.core.gui.components.form.flexible.impl;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
-import org.apache.commons.fileupload.FileItemIterator;
-import org.apache.commons.fileupload.FileItemStream;
-import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
-import org.apache.commons.fileupload.MultipartStream.MalformedStreamException;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
-import org.apache.commons.fileupload.util.Streams;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
+
+import org.apache.commons.io.IOUtils;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.ComponentCollection;
@@ -56,10 +55,10 @@ import org.olat.core.gui.control.Event;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.logging.AssertException;
 import org.olat.core.logging.LogDelegator;
-import org.olat.core.logging.OLATRuntimeException;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
 import org.olat.core.util.ArrayHelper;
 import org.olat.core.util.CodeHelper;
-import org.olat.core.util.FileUtils;
 import org.olat.core.util.ValidationStatus;
 import org.olat.core.util.WebappHelper;
 import org.olat.core.util.component.FormComponentTraverser;
@@ -141,6 +140,8 @@ import org.olat.core.util.component.FormComponentVisitor;
  * @author patrickb
  */
 public class Form extends LogDelegator {
+	
+	private static final OLog log = Tracing.createLoggerFor(Form.class);
 	//
 	public final static String FORMCMD = "fid";
 	public final static String FORMID = "ofo_";
@@ -217,7 +218,11 @@ public class Form extends LogDelegator {
 	 */
 	public void evalFormRequest(UserRequest ureq) {
 		// Initialize temporary request parameters
-		doInitRequestParameterAndMulipartData(ureq);
+		if (isMultipartEnabled() && isMultipartContent(ureq.getHttpReq())) {
+			doInitRequestMultipartDataParameter(ureq);
+		} else {
+			doInitRequestParameter(ureq);
+		}
 		
 		String dispatchUri = getRequestParameter("dispatchuri");
 		String dispatchAction = getRequestParameter("dispatchevent");
@@ -312,105 +317,67 @@ public class Form extends LogDelegator {
 		// End of request dispatch: cleanup temp files: ureq requestParams and multipart files
 		doClearRequestParameterAndMultipartData();
 	}
-
+	
+	private void doInitRequestMultipartDataParameter(UserRequest ureq) {
+		HttpServletRequest req = ureq.getHttpReq();
+		try {				
+			for(Part part:req.getParts()) {
+				String contentType = part.getContentType();
+				String name = part.getName();
+
+				if(contentType == null) {
+					String value = IOUtils.toString(part.getInputStream());
+					addRequestParameter(name, value);
+				} else {
+					File tmpFile = new File(WebappHelper.getTmpDir(), "upload-" + CodeHelper.getGlobalForeverUniqueID());
+					part.write(tmpFile.getAbsolutePath());
+					
+					String fileName = part.getSubmittedFileName();
+					
+					// Cleanup IE filenames that are absolute
+					int slashpos = fileName.lastIndexOf("/");
+					if (slashpos != -1) fileName = name.substring(slashpos + 1);
+					slashpos = fileName.lastIndexOf("\\");
+					if (slashpos != -1) fileName = fileName.substring(slashpos + 1);
+					
+					requestMultipartFiles.put(name, tmpFile);
+					requestMultipartFileNames.put(name, fileName);
+					requestMultipartFileMimeTypes.put(name, contentType);
+				}
+				part.delete();
+			}
+		} catch (IOException | ServletException e) {
+			log.error("", e);
+		}
+	}
+	
+	private boolean isMultipartContent(HttpServletRequest request) {
+		if (!"POST".equalsIgnoreCase(request.getMethod())) {
+            return false;
+        }
+		
+		String contentType = request.getContentType();
+		if (contentType == null) {
+            return false;
+        }
+        if (contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")) {
+            return true;
+        }
+		return false;
+	}
+	
 	/**
-	 * Internal helper to initialize the request parameter map an to temporary
-	 * store the uploaded files when a multipart request is used. The files are
-	 * stored to a temporary location and a filehandle is added to the
-	 * requestMultipartFiles map for later retrieval by the responsible FormItem.
-	 * 
+	 * Get parameters the standard way
 	 * @param ureq
 	 */
-	private void doInitRequestParameterAndMulipartData(UserRequest ureq) {
-		// First fill parameter map either from multipart data or standard http request
-		if (isMultipartEnabled() && ServletFileUpload.isMultipartContent(ureq.getHttpReq())) {
-			long uploadSize = -1; // default unlimited
-			// Limit size of complete upload form: upload size limit + 500k for
-			// other input fields
-			if (multipartUploadMaxSizeKB > -1) {
-				uploadSize = (multipartUploadMaxSizeKB * 1024l * 1024l) + 512000l;
-			}
-
-			// Create a new file upload handler, use commons fileupload streaming
-			// API to save files right to the tmp location
-			ServletFileUpload uploadParser = new ServletFileUpload();
-			uploadParser.setSizeMax(uploadSize);
-			// Parse the request
-			try {
-				FileItemIterator iter = uploadParser.getItemIterator(ureq.getHttpReq());
-				while (iter.hasNext()) {
-					FileItemStream item = iter.next();
-					String itemName = item.getFieldName();
-					InputStream itemStream = item.openStream();
-					if (item.isFormField()) {
-						// Normal form item
-						// analog to ureq.getParameter in non-multipart mode
-						String value = Streams.asString(itemStream, "UTF-8");
-						addRequestParameter(itemName, value);
-					} else {
-						// File item, store it to temp location
-						String fileName = item.getName();
-						// Cleanup IE filenames that are absolute
-						int slashpos = fileName.lastIndexOf("/");
-						if (slashpos != -1) fileName = fileName.substring(slashpos + 1);
-						slashpos = fileName.lastIndexOf("\\");
-						if (slashpos != -1) fileName = fileName.substring(slashpos + 1);
-
-						File tmpFile = new File(WebappHelper.getTmpDir() + File.separator + "upload-" + CodeHelper.getGlobalForeverUniqueID());
-						
-						try {
-							FileUtils.save(itemStream, tmpFile);
-							// Only save non-empty file transfers, ignore empty transfers
-							// (e.g. already submitted in a previous form submit, not an error!)
-							
-							// Removing empty file check for now ... was introduced to cope with
-							// browser trouble which probably is  not there any more ...
-							// so empty fileName means nothing selected in the file element
-							
-							//if (tmpFile.length() > 0) {
-							if (fileName.length() > 0) {
-								// a file was selected
-								// Save file and also original file name
-								requestMultipartFiles.put(itemName, tmpFile);
-								requestMultipartFileNames.put(itemName, fileName);
-								requestMultipartFileMimeTypes.put(itemName, item.getContentType());
-							} else {
-								if (tmpFile.exists()) tmpFile.delete();
-							}
-						} catch (OLATRuntimeException e) {
-							// Could not save stream for whatever reason, cleanup temp file and delegate exception
-							if (tmpFile.exists()) tmpFile.delete();
-							
-							if (e.getCause() instanceof MalformedStreamException){
-								logWarn("Could not read uploaded file >"+fileName+"< from stream. Possibly an attempt to upload a directory instead of a file (happens on Mac)",e);
-								return;
-							}
-							
-							throw new OLATRuntimeException("Could not save uploaded file",e);							
-						}
-					}
-				}
-			} catch (SizeLimitExceededException sizeLimitExceededException) {
-				logError("Error while dispatching multipart form: file limit (" + uploadSize + ") exceeded", sizeLimitExceededException);
-				requestError = REQUEST_ERROR_UPLOAD_LIMIT_EXCEEDED;
-			} catch (IOException e) {
-				logWarn("Error while dispatching multipart form: ioexception", e);
-				requestError = REQUEST_ERROR_GENERAL;
-			} catch (Exception e) {
-				logError("Error while dispatching multipart form: general exception", e);
-				requestError = REQUEST_ERROR_GENERAL;
-			}
-		} else {
-			// Get parameters the standard way
-			logDebug("Dispatching non-multipart form", null);
-			Set<String> keys = ureq.getParameterSet();
-			for (String key : keys) {
-				String[] values = ureq.getHttpReq().getParameterValues(key);
-				if (values != null) {
-					requestParams.put(key, values);	
-				} else {
-					addRequestParameter(key, ureq.getParameter(key));					
-				}
+	private void doInitRequestParameter(UserRequest ureq) {
+		Set<String> keys = ureq.getParameterSet();
+		for (String key : keys) {
+			String[] values = ureq.getHttpReq().getParameterValues(key);
+			if (values != null) {
+				requestParams.put(key, values);	
+			} else {
+				addRequestParameter(key, ureq.getParameter(key));					
 			}
 		}
 	}
diff --git a/src/main/java/org/olat/core/gui/media/ServletUtil.java b/src/main/java/org/olat/core/gui/media/ServletUtil.java
index 45fd4d6d695..dd68e23423b 100644
--- a/src/main/java/org/olat/core/gui/media/ServletUtil.java
+++ b/src/main/java/org/olat/core/gui/media/ServletUtil.java
@@ -185,7 +185,7 @@ public class ServletUtil {
 					httpResp.addHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + range.length);
 					long length = range.end - range.start + 1;
 					if (length < Integer.MAX_VALUE) {
-						httpResp.setContentLength((int) length);
+						httpResp.setContentLengthLong(length);
 					} else {
 						// Set the content-length as String to be able to use a long
 						httpResp.setHeader("content-length", "" + length);
@@ -199,7 +199,7 @@ public class ServletUtil {
 					copy(out, in, range);
 				} else {
 					if (size != null) {
-						httpResp.setContentLength(size.intValue());
+						httpResp.setContentLengthLong(size.longValue());
 					}
 					int bufferSize = httpResp.getBufferSize();
 					// buffer input stream
diff --git a/src/main/java/org/olat/core/servlets/OpenOLATServlet.java b/src/main/java/org/olat/core/servlets/OpenOLATServlet.java
index 68acbfa108c..b9d767f92b0 100644
--- a/src/main/java/org/olat/core/servlets/OpenOLATServlet.java
+++ b/src/main/java/org/olat/core/servlets/OpenOLATServlet.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
+import javax.servlet.annotation.MultipartConfig;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -54,6 +55,8 @@ import org.olat.core.util.i18n.I18nManager;
 import org.olat.core.util.threadlog.RequestBasedLogLevelManager;
 import org.olat.core.util.threadlog.UserBasedLogLevelManager;
 
+
+@MultipartConfig(fileSizeThreshold=10240)
 public class OpenOLATServlet extends HttpServlet {
 
 	private static final long serialVersionUID = -2777749229549683775L;
diff --git a/src/main/java/org/olat/core/servlets/SpyFilter.java b/src/main/java/org/olat/core/servlets/SpyFilter.java
index e884c4ffe9d..28beb2886a1 100644
--- a/src/main/java/org/olat/core/servlets/SpyFilter.java
+++ b/src/main/java/org/olat/core/servlets/SpyFilter.java
@@ -32,6 +32,7 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.WriteListener;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -186,6 +187,12 @@ public class SpyFilter implements Filter {
 			response.setContentLength(len);
 		}
 
+		@Override
+		public void setContentLengthLong(long len) {
+			System.out.println("setContentLengthLong: " + len);
+			response.setContentLengthLong(len);
+		}
+
 		public void setContentType(String type) {
 			System.out.println("setContentType: " + type);
 			response.setContentType(type);
@@ -541,5 +548,17 @@ public class SpyFilter implements Filter {
 			System.out.println("close outputStream");
 			delegate.close();
 		}
+
+		@Override
+		public boolean isReady() {
+			System.out.println("isReady");
+			return delegate.isReady();
+		}
+
+		@Override
+		public void setWriteListener(WriteListener writeListener) {
+			System.out.println("setWriteListener");
+			delegate.setWriteListener(writeListener);
+		}
 	}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/servlets/StaticServlet.java b/src/main/java/org/olat/core/servlets/StaticServlet.java
index 48f0a9be40a..b8ad848da96 100644
--- a/src/main/java/org/olat/core/servlets/StaticServlet.java
+++ b/src/main/java/org/olat/core/servlets/StaticServlet.java
@@ -205,7 +205,7 @@ public class StaticServlet extends HttpServlet {
 			
 			String mimeType = WebappHelper.getMimeType(file.getName());
 			response.setContentType(mimeType);
-			response.setContentLength((int)file.length());
+			response.setContentLengthLong(file.length());
 
 			try(InputStream in = new FileInputStream(file)) {
 				FileUtils.cpio(in, response.getOutputStream(), "static");
diff --git a/src/main/java/org/olat/core/util/io/HttpServletResponseOutputStream.java b/src/main/java/org/olat/core/util/io/HttpServletResponseOutputStream.java
index 36a3d3aad81..617812c2242 100644
--- a/src/main/java/org/olat/core/util/io/HttpServletResponseOutputStream.java
+++ b/src/main/java/org/olat/core/util/io/HttpServletResponseOutputStream.java
@@ -27,6 +27,7 @@ import java.util.Collections;
 import java.util.Locale;
 
 import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletResponse;
 
@@ -73,6 +74,11 @@ public class HttpServletResponseOutputStream implements HttpServletResponse {
 	public void setContentLength(int len) {
 		//
 	}
+	
+	@Override
+	public void setContentLengthLong(long len) {
+		//
+	}
 
 	@Override
 	public void setContentType(String type) {
@@ -247,6 +253,16 @@ public class HttpServletResponseOutputStream implements HttpServletResponse {
 			out.write(b, off, len);
 		}
 
+		@Override
+		public boolean isReady() {
+			return true;
+		}
+
+		@Override
+		public void setWriteListener(WriteListener writeListener) {
+			//
+		}
+
 		@Override
 		public void flush() throws IOException {
 			out.flush();
diff --git a/src/main/java/org/olat/restapi/support/MultipartReader.java b/src/main/java/org/olat/restapi/support/MultipartReader.java
index c5ff14874b6..ad260361b75 100644
--- a/src/main/java/org/olat/restapi/support/MultipartReader.java
+++ b/src/main/java/org/olat/restapi/support/MultipartReader.java
@@ -19,22 +19,17 @@
  */
 package org.olat.restapi.support;
 
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
 
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
 
-import org.apache.commons.fileupload.FileItemIterator;
-import org.apache.commons.fileupload.FileItemStream;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
-import org.apache.commons.fileupload.util.Streams;
+import org.apache.commons.io.IOUtils;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
@@ -53,42 +48,34 @@ public class MultipartReader {
 	private Map<String, String> fields = new HashMap<String, String>();
 
 	public MultipartReader(HttpServletRequest request) {
-		long uploadLimit = 500000l;
-		apache(request, uploadLimit);
+		servlet31(request);
 	}
-
-	private final void apache(HttpServletRequest request, long uploadLimit) {
-		ServletFileUpload uploadParser = new ServletFileUpload();
-		uploadParser.setSizeMax((uploadLimit * 1024l) + 512000l);
-		// Parse the request
+	private final void servlet31(HttpServletRequest request) {
 		try {
-			FileItemIterator iter = uploadParser.getItemIterator(request);
-			while (iter.hasNext()) {
-				FileItemStream item = iter.next();
-				String itemName = item.getFieldName();
-				InputStream itemStream = item.openStream();
-				if (item.isFormField()) {
-					String value = Streams.asString(itemStream, "UTF-8");
-					fields.put(itemName, value);
-				} else {
-					// File item, store it to temp location
-					filename = item.getName();
-					contentType = item.getContentType();
-					
+			for(Part part:request.getParts()) {
+				if(part.getContentType() != null) {
+					contentType = part.getContentType();
+					filename = part.getSubmittedFileName();
 					if(filename != null) {
 						filename = UUID.randomUUID().toString().replace("-", "") + "_" + filename;
 					} else {
 						filename = "upload-" + UUID.randomUUID().toString().replace("-", "");
 					}
 					file = new File(WebappHelper.getTmpDir(), filename);
-					try {
-						save(itemStream, file);
-					} catch (Exception e) {
-						log.error("", e);
-					}
+					part.write(file.getAbsolutePath());
+					file = new File(WebappHelper.getTmpDir(), filename);
+				} else {
+					String value = IOUtils.toString(part.getInputStream());
+					fields.put(part.getName(), value);
+				}
+				
+				try {
+					part.delete();
+				} catch (Exception e) {
+					//we try (tomcat doesn't send exception but undertow)
 				}
 			}
-		} catch (Exception e) {
+		} catch (IOException | ServletException e) {
 			log.error("", e);
 		}
 	}
@@ -142,23 +129,6 @@ public class MultipartReader {
 		return file;
 	}
 
-	private void save(InputStream source, File targetFile)
-	throws IOException {
-		InputStream in = new BufferedInputStream(source);
-		OutputStream out = new FileOutputStream(targetFile);
-
-		byte[] buffer = new byte[4096];
-
-		int c;
-		while ((c = in.read(buffer, 0, buffer.length)) != -1) {
-			out.write(buffer, 0, c);
-		}
-
-		out.flush();
-		out.close();
-		in.close();
-	}
-
 	public void close() {
 		if (file != null) {
 			file.delete();
diff --git a/src/main/webapp-tomcat/WEB-INF/web.xml b/src/main/webapp-tomcat/WEB-INF/web.xml
index 32c49a68469..a3acc13e1f8 100644
--- a/src/main/webapp-tomcat/WEB-INF/web.xml
+++ b/src/main/webapp-tomcat/WEB-INF/web.xml
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<web-app version="2.5"
-         xmlns="http://java.sun.com/xml/ns/javaee"
+<web-app xmlns="http://xmlns.jcp.org/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"
-         metadata-complete="true">
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+            http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
 
 	<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   -->
 	<!-- IMPORTANT : DO NOT CHANGE SEQUENCE OF XML TYPES BECAUSE VALIDATION MAY FAIL  -->
@@ -188,6 +188,9 @@
 			<param-name>javax.ws.rs.Application</param-name>
 			<param-value>org.olat.restapi.support.OlatRestApplication</param-value>
 		</init-param>
+		<multipart-config>
+            <file-size-threshold>10240</file-size-threshold>
+        </multipart-config>
 		<load-on-startup>1</load-on-startup>
 	</servlet>
 	
diff --git a/src/test/java/org/olat/restapi/CatalogTest.java b/src/test/java/org/olat/restapi/CatalogTest.java
index d716199a2fa..47db5785c73 100644
--- a/src/test/java/org/olat/restapi/CatalogTest.java
+++ b/src/test/java/org/olat/restapi/CatalogTest.java
@@ -88,7 +88,6 @@ public class CatalogTest extends OlatJerseyTestCase {
 	
 	private Identity admin, id1;
 	private CatalogEntry root1, entry1, entry2, subEntry11, subEntry12;
-	//fxdiff FXOLAT-122: course management
 	private CatalogEntry entryToMove1, entryToMove2, subEntry13move;
 	
 	@Before
@@ -672,7 +671,7 @@ public class CatalogTest extends OlatJerseyTestCase {
 		}
 	}
 	
-	private RepositoryEntry createRepository(String name, final Long resourceableId) {
+	private RepositoryEntry createRepository(String displayName, final Long resourceableId) {
 		OLATResourceable resourceable = new OLATResourceable() {
 			public String getResourceableTypeName() {	return CourseModule.ORES_TYPE_COURSE;}
 			public Long getResourceableId() {return resourceableId;}
@@ -690,7 +689,7 @@ public class CatalogTest extends OlatJerseyTestCase {
 		
 		RepositoryEntry d = RepositoryManager.getInstance().lookupRepositoryEntry(resourceable, false);
 		if(d == null) {
-			d = repositoryService.create("Rei Ayanami", "-", name, "Repo entry", r);
+			d = repositoryService.create("Rei Ayanami", "-", displayName, "Repo entry", r);
 			DBFactory.getInstance().saveObject(d);
 		}
 		DBFactory.getInstance().intermediateCommit();
diff --git a/src/test/java/org/olat/restapi/CourseDBTest.java b/src/test/java/org/olat/restapi/CourseDBTest.java
index a01fa33a1d5..1843e90025c 100644
--- a/src/test/java/org/olat/restapi/CourseDBTest.java
+++ b/src/test/java/org/olat/restapi/CourseDBTest.java
@@ -90,11 +90,11 @@ public class CourseDBTest extends OlatJerseyTestCase {
 		Assert.assertTrue(conn.login(auth.getName(), JunitTestHelper.PWD));
 		
 		String category = createRndCategory();
-		String name = "myKeyName";
+		String key = "myKeyName";
 		String value = "an interessant value";
 		
 		UriBuilder uri = getUriBuilder(course.getResourceableId(), category).path("values")
-				.path(name).queryParam("value", value);
+				.path(key).queryParam("value", value);
 		HttpPut put = conn.createPut(uri.build(), MediaType.APPLICATION_JSON, true);
 	
 		HttpResponse response = conn.execute(put);
@@ -103,9 +103,9 @@ public class CourseDBTest extends OlatJerseyTestCase {
 		
 		conn.shutdown();
 		
-		CourseDBEntry entry = courseDbManager.getValue(course, auth, category, name);
+		CourseDBEntry entry = courseDbManager.getValue(course, auth, category, key);
 		Assert.assertNotNull(entry);
-		Assert.assertEquals(name, entry.getName());
+		Assert.assertEquals(key, entry.getName());
 		Assert.assertEquals(value, entry.getValue());
 		Assert.assertEquals(category, entry.getCategory());
 	}
@@ -119,11 +119,11 @@ public class CourseDBTest extends OlatJerseyTestCase {
 		RepositoryEntry courseRe = repositoryManager.lookupRepositoryEntry(courseOres, true);
 		
 		String category = createRndCategory();
-		String name = "myKeyName";
+		String key = "myKeyName";
 		String value = "an interessant value";
 		
 		UriBuilder uri = getUriBuilder(courseRe.getKey(), category).path("values")
-				.path(name).queryParam("value", value);
+				.path(key).queryParam("value", value);
 		HttpPut put = conn.createPut(uri.build(), MediaType.APPLICATION_JSON, true);
 	
 		HttpResponse response = conn.execute(put);
@@ -132,9 +132,9 @@ public class CourseDBTest extends OlatJerseyTestCase {
 		
 		conn.shutdown();
 		
-		CourseDBEntry entry = courseDbManager.getValue(course, auth, category, name);
+		CourseDBEntry entry = courseDbManager.getValue(course, auth, category, key);
 		Assert.assertNotNull(entry);
-		Assert.assertEquals(name, entry.getName());
+		Assert.assertEquals(key, entry.getName());
 		Assert.assertEquals(value, entry.getValue());
 		Assert.assertEquals(category, entry.getCategory());
 	}
@@ -167,10 +167,10 @@ public class CourseDBTest extends OlatJerseyTestCase {
 		Assert.assertTrue(conn.login(auth.getName(), JunitTestHelper.PWD));
 		
 		String category = createRndCategory();
-		String name = "postit";
+		String key = "postit";
 		String value = "create the value by POST";
 		
-		UriBuilder uri = getUriBuilder(course.getResourceableId(), category).path("values").path(name);
+		UriBuilder uri = getUriBuilder(course.getResourceableId(), category).path("values").path(key);
 		HttpPost put = conn.createPost(uri.build(), MediaType.APPLICATION_JSON);
 		conn.addEntity(put, new BasicNameValuePair("val", value));
 		
@@ -180,9 +180,9 @@ public class CourseDBTest extends OlatJerseyTestCase {
 		
 		conn.shutdown();
 		
-		CourseDBEntry entry = courseDbManager.getValue(course, auth, category, name);
+		CourseDBEntry entry = courseDbManager.getValue(course, auth, category, key);
 		Assert.assertNotNull(entry);
-		Assert.assertEquals(name, entry.getName());
+		Assert.assertEquals(key, entry.getName());
 		Assert.assertEquals(value, entry.getValue());
 		Assert.assertEquals(category, entry.getCategory());
 	}
@@ -190,17 +190,17 @@ public class CourseDBTest extends OlatJerseyTestCase {
 	@Test
 	public void createEntry_get() throws IOException, URISyntaxException {
 		String category = createRndCategory();
-		String name = "getit";
+		String key = "getit";
 		String value = "get a value";
 		
-		CourseDBEntry entry = courseDbManager.setValue(course, auth, category, name, value);
+		CourseDBEntry entry = courseDbManager.setValue(course, auth, category, key, value);
 		dbInstance.commitAndCloseSession();
 		Assert.assertNotNull(entry);
 		
 		RestConnection conn = new RestConnection();
 		Assert.assertTrue(conn.login(auth.getName(), JunitTestHelper.PWD));
 
-		UriBuilder uri = getUriBuilder(course.getResourceableId(), category).path("values").path(name);
+		UriBuilder uri = getUriBuilder(course.getResourceableId(), category).path("values").path(key);
 		HttpGet get = conn.createGet(uri.build(), MediaType.APPLICATION_JSON, true);
 		
 		HttpResponse response = conn.execute(get);
@@ -210,7 +210,7 @@ public class CourseDBTest extends OlatJerseyTestCase {
 		conn.shutdown();
 		
 		Assert.assertNotNull(savedEntry);
-		Assert.assertEquals(name, savedEntry.getKey());
+		Assert.assertEquals(key, savedEntry.getKey());
 		Assert.assertEquals(value, savedEntry.getValue());
 	}
 	
diff --git a/src/test/java/org/olat/restapi/NotificationsTest.java b/src/test/java/org/olat/restapi/NotificationsTest.java
index a6718603b20..dfb507cc753 100644
--- a/src/test/java/org/olat/restapi/NotificationsTest.java
+++ b/src/test/java/org/olat/restapi/NotificationsTest.java
@@ -274,16 +274,16 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		BusinessGroup group = businessGroupService.createBusinessGroup(id, "Notifications 1", "REST forum notifications for group", null, null, false, false, null);
 		CollaborationTools tools = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(group);
 		tools.setToolEnabled(CollaborationTools.TOOL_FORUM, true);
-		Forum forum = tools.getForum();
+		Forum groupForum = tools.getForum();
 		dbInstance.commitAndCloseSession();
 		
 		//publish
 		String businessPath = "[BusinessGroup:" + group.getKey() + "][toolforum:0]";
 		SubscriptionContext forumSubContext = new SubscriptionContext("BusinessGroup", group.getKey(), "toolforum");
 		PublisherData forumPdata =
-				new PublisherData(OresHelper.calculateTypeName(Forum.class), forum.getKey().toString(), businessPath);
+				new PublisherData(OresHelper.calculateTypeName(Forum.class), groupForum.getKey().toString(), businessPath);
 		notificationManager.subscribe(id, forumSubContext, forumPdata);
-		Message message = createMessage(id, forum);
+		Message message = createMessage(id, groupForum);
 		notificationManager.markPublisherNews(forumSubContext, null, true);
 		dbInstance.commitAndCloseSession();
 		
@@ -362,7 +362,7 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		forumNode.setShortTitle("Forum");
 		forumNode.setLearningObjectives("forum objectives");
 		forumNode.setNoAccessExplanation("You don't have access");
-		Forum forum = forumNode.loadOrCreateForum(course.getCourseEnvironment());
+		Forum courseForum = forumNode.loadOrCreateForum(course.getCourseEnvironment());
 		course.getEditorTreeModel().addCourseNode(forumNode, course.getRunStructure().getRootNode());
 		CourseFactory.publishCourse(course, RepositoryEntry.ACC_USERS, false, id, Locale.ENGLISH);
 		dbInstance.intermediateCommit();
@@ -372,9 +372,9 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		String businessPath = "[RepositoryEntry:" + re.getKey() + "][CourseNode:" + forumNode.getIdent() + "]";
 		SubscriptionContext forumSubContext = new SubscriptionContext("CourseModule", course.getResourceableId(), forumNode.getIdent());
 		PublisherData forumPdata =
-				new PublisherData(OresHelper.calculateTypeName(Forum.class), forum.getKey().toString(), businessPath);
+				new PublisherData(OresHelper.calculateTypeName(Forum.class), courseForum.getKey().toString(), businessPath);
 		notificationManager.subscribe(id, forumSubContext, forumPdata);
-		Message message = createMessage(id, forum);
+		Message message = createMessage(id, courseForum);
 		notificationManager.markPublisherNews(forumSubContext, null, true);
 		dbInstance.commitAndCloseSession();
 		
@@ -449,8 +449,8 @@ public class NotificationsTest extends OlatJerseyTestCase {
 	}
 	
 	private String addFile(VFSContainer folder) throws IOException {
-		String name = UUID.randomUUID().toString();
-		VFSLeaf file = folder.createChildLeaf(name + ".jpg");
+		String filename = UUID.randomUUID().toString();
+		VFSLeaf file = folder.createChildLeaf(filename + ".jpg");
 		OutputStream out = file.getOutputStream(true);
 		InputStream in = UserMgmtTest.class.getResourceAsStream("portrait.jpg");
 		IOUtils.copy(in, out);
@@ -459,12 +459,12 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		return file.getName();
 	}
 	
-	private Message createMessage(Identity id, Forum forum) {
+	private Message createMessage(Identity id, Forum fo) {
 		ForumManager fm = ForumManager.getInstance();
 		Message m1 = fm.createMessage();
 		m1.setTitle("Thread-1");
 		m1.setBody("Body of Thread-1");
-		fm.addTopMessage(id, forum, m1);
+		fm.addTopMessage(id, fo, m1);
 		return m1;
 	}
 	
diff --git a/src/test/java/org/olat/restapi/RepositoryEntriesTest.java b/src/test/java/org/olat/restapi/RepositoryEntriesTest.java
index e24a91e071f..c01005efcb9 100644
--- a/src/test/java/org/olat/restapi/RepositoryEntriesTest.java
+++ b/src/test/java/org/olat/restapi/RepositoryEntriesTest.java
@@ -737,7 +737,7 @@ public class RepositoryEntriesTest extends OlatJerseyTestCase {
 		}
 	}
 	
-	private RepositoryEntry createRepository(String name) {
+	private RepositoryEntry createRepository(String displayName) {
 		OLATResourceManager rm = OLATResourceManager.getInstance();
 		// create course and persist as OLATResourceImpl
 		
@@ -745,7 +745,7 @@ public class RepositoryEntriesTest extends OlatJerseyTestCase {
 		DBFactory.getInstance().saveObject(r);
 		DBFactory.getInstance().intermediateCommit();
 
-		RepositoryEntry d = repositoryService.create(name, "-", name, "Repo entry", r);
+		RepositoryEntry d = repositoryService.create(displayName, "-", displayName, "Repo entry", r);
 		DBFactory.getInstance().commit();
 		return d;
 	}
diff --git a/src/test/java/org/olat/restapi/UserCoursesTest.java b/src/test/java/org/olat/restapi/UserCoursesTest.java
index d65e7ca6dec..78728d89a92 100644
--- a/src/test/java/org/olat/restapi/UserCoursesTest.java
+++ b/src/test/java/org/olat/restapi/UserCoursesTest.java
@@ -35,7 +35,6 @@ import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.type.TypeReference;
 import org.junit.Assert;
 import org.junit.Test;
-import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.services.mark.MarkManager;
@@ -67,8 +66,6 @@ public class UserCoursesTest extends OlatJerseyTestCase {
 	@Autowired
 	private MarkManager markManager;
 	@Autowired
-	private BaseSecurity securityManager;
-	@Autowired
 	private RepositoryManager repositoryManager;
 	@Autowired
 	private RepositoryService repositoryService;
diff --git a/src/test/java/org/olat/test/OlatJerseyTestCase.java b/src/test/java/org/olat/test/OlatJerseyTestCase.java
index 43cdd70c57c..b124debe78b 100644
--- a/src/test/java/org/olat/test/OlatJerseyTestCase.java
+++ b/src/test/java/org/olat/test/OlatJerseyTestCase.java
@@ -30,15 +30,13 @@ import static io.undertow.servlet.Servlets.defaultContainer;
 import static io.undertow.servlet.Servlets.deployment;
 import static io.undertow.servlet.Servlets.filter;
 import static io.undertow.servlet.Servlets.servlet;
-import io.undertow.Undertow;
-import io.undertow.servlet.api.DeploymentInfo;
-import io.undertow.servlet.api.DeploymentManager;
 
 import java.io.InputStream;
 import java.net.URI;
 import java.util.List;
 
 import javax.servlet.DispatcherType;
+import javax.servlet.MultipartConfigElement;
 import javax.servlet.ServletException;
 import javax.ws.rs.core.UriBuilder;
 
@@ -56,6 +54,10 @@ import org.olat.restapi.support.vo.FileVO;
 import org.olat.restapi.support.vo.LinkVO;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import io.undertow.Undertow;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.DeploymentManager;
+
 /**
  * 
  * Description:<br>
@@ -105,6 +107,7 @@ public abstract class OlatJerseyTestCase extends OlatTestCase {
 				        .addServlets(
 				                servlet("REST Servlet",  com.sun.jersey.spi.container.servlet.ServletContainer.class)
 		        		        		.addInitParam("javax.ws.rs.Application", OlatRestApplication.class.getName())
+		        		        		.setMultipartConfig(new MultipartConfigElement((String)null))
 				                        .addMapping("/*"))
 				        .addFilters(filter("REST security filter", RestApiLoginFilter.class))
 				        .addFilterUrlMapping("REST security filter", "/*", DispatcherType.REQUEST);
-- 
GitLab