diff --git a/src/org/json/Json4Uws.java b/src/org/json/Json4Uws.java
index 41311372d73742ee76093e36d7e92e8ade48da1d..9e84863d5585d672a5166d1422d4473b2c3287a2 100644
--- a/src/org/json/Json4Uws.java
+++ b/src/org/json/Json4Uws.java
@@ -35,7 +35,7 @@ import uws.service.UWSUrl;
  * Useful conversion functions from UWS to JSON.
  * 
  * @author Grégory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (12/2014)
  */
 public final class Json4Uws {
 
@@ -153,8 +153,23 @@ public final class Json4Uws {
 	public final static JSONObject getJobParamsJson(final UWSJob job) throws JSONException{
 		JSONObject json = new JSONObject();
 		if (job != null){
-			for(String name : job.getAdditionalParameters())
-				json.put(name, job.getAdditionalParameterValue(name));
+			Object val;
+			for(String name : job.getAdditionalParameters()){
+				// get the raw value:
+				val = job.getAdditionalParameterValue(name);
+				// if an array, build a JSON array of strings:
+				if (val != null && val.getClass().isArray()){
+					JSONArray array = new JSONArray();
+					for(Object o : (Object[])val){
+						if (o != null)
+							array.put(o.toString());
+					}
+					json.put(name, array);
+				}
+				// otherwise, just put the value:
+				else
+					json.put(name, val);
+			}
 		}
 		return json;
 	}
diff --git a/src/tap/ADQLExecutor.java b/src/tap/ADQLExecutor.java
index 43b18fbfd1f271d06c5abcfc40643602a1bc2643..9143dca9dac68a46f85d0e07543bb8c9296fff47 100644
--- a/src/tap/ADQLExecutor.java
+++ b/src/tap/ADQLExecutor.java
@@ -33,8 +33,8 @@ import tap.formatter.OutputFormat;
 import tap.log.TAPLog;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
+import tap.parameters.DALIUpload;
 import tap.parameters.TAPParameters;
-import tap.upload.TableLoader;
 import tap.upload.Uploader;
 import uws.UWSException;
 import uws.job.JobThread;
@@ -104,7 +104,7 @@ import adql.query.ADQLQuery;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (12/2014)
  */
 public class ADQLExecutor {
 
@@ -299,7 +299,7 @@ public class ADQLExecutor {
 			dbConn = service.getFactory().getConnection(report.jobID);
 
 			// 1. UPLOAD TABLES, if there is any:
-			if (tapParams.getTableLoaders() != null && tapParams.getTableLoaders().length > 0){
+			if (tapParams.getUploadedTables() != null && tapParams.getUploadedTables().length > 0){
 				startStep(ExecutionProgression.UPLOADING);
 				uploadTables();
 				endStep();
@@ -444,16 +444,12 @@ public class ADQLExecutor {
 	 */
 	private final void uploadTables() throws TAPException{
 		// Fetch the tables to upload:
-		TableLoader[] tables = tapParams.getTableLoaders();
+		DALIUpload[] tables = tapParams.getUploadedTables();
 
 		// Upload them, if needed:
 		if (tables.length > 0){
 			logger.logTAP(LogLevel.INFO, report, "UPLOADING", "Loading uploaded tables (" + tables.length + ")", null);
-			try{
-				uploadSchema = service.getFactory().createUploader(dbConn).upload(tables);
-			}finally{
-				TAPParameters.deleteUploadedTables(tables);
-			}
+			uploadSchema = service.getFactory().createUploader(dbConn).upload(tables);
 		}
 	}
 
diff --git a/src/tap/AbstractTAPFactory.java b/src/tap/AbstractTAPFactory.java
index 203a2021ed9750d944f160b05b90a83931368dad..359b3cbde1ce780131d4e098f6299a4393388574 100644
--- a/src/tap/AbstractTAPFactory.java
+++ b/src/tap/AbstractTAPFactory.java
@@ -53,7 +53,7 @@ import adql.query.ADQLQuery;
  * Only the functions related with the database connection stay abstract.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (11/2014)
+ * @version 2.0 (12/2014)
  */
 public abstract class AbstractTAPFactory extends TAPFactory {
 
@@ -213,9 +213,13 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 	 */
 	@Override
 	public UWSService createUWS() throws TAPException{
-		UWSService uws = new UWSService(this, this.service.getFileManager(), this.service.getLogger());
-		uws.setErrorWriter(errorWriter);
-		return uws;
+		try{
+			UWSService uws = new UWSService(this, this.service.getFileManager(), this.service.getLogger());
+			uws.setErrorWriter(errorWriter);
+			return uws;
+		}catch(UWSException ue){
+			throw new TAPException("Can not create a UWS service (asynchronous resource of TAP)!", ue, UWSException.INTERNAL_SERVER_ERROR);
+		}
 	}
 
 	/**
@@ -243,7 +247,7 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 			TAPParameters tapParams = createTAPParameters(request);
 			return new TAPJob(owner, tapParams);
 		}catch(TAPException te){
-			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, te, "Can not create a TAP asynchronous job !");
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, te, "Can not create a TAP asynchronous job!");
 		}
 	}
 
@@ -276,11 +280,7 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 	 */
 	@Override
 	public TAPParameters createTAPParameters(final HttpServletRequest request) throws TAPException{
-		try{
-			return new TAPParameters(request, service);
-		}catch(UWSException ue){
-			throw new TAPException(ue);
-		}
+		return new TAPParameters(request, service);
 	}
 
 	/**
@@ -295,11 +295,7 @@ public abstract class AbstractTAPFactory extends TAPFactory {
 	 */
 	@Override
 	public TAPParameters createTAPParameters(final Map<String,Object> params) throws TAPException{
-		try{
-			return new TAPParameters(service, params);
-		}catch(UWSException ue){
-			throw new TAPException(ue);
-		}
+		return new TAPParameters(service, params);
 	}
 
 }
diff --git a/src/tap/ServiceConnection.java b/src/tap/ServiceConnection.java
index f849d8283bee98b6ec763afb837c75049ba882b5..444df9d8a6edf73a1b01bf2f7ad95c69de2c4614 100644
--- a/src/tap/ServiceConnection.java
+++ b/src/tap/ServiceConnection.java
@@ -23,13 +23,13 @@ package tap;
 import java.util.Collection;
 import java.util.Iterator;
 
-import tap.file.LocalTAPFileManager;
-import tap.file.TAPFileManager;
 import tap.formatter.OutputFormat;
 import tap.log.DefaultTAPLog;
 import tap.log.TAPLog;
 import tap.metadata.TAPMetadata;
 import uws.service.UserIdentifier;
+import uws.service.file.LocalUWSFileManager;
+import uws.service.file.UWSFileManager;
 import adql.db.FunctionDef;
 
 /**
@@ -42,7 +42,7 @@ import adql.db.FunctionDef;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (10/2014)
+ * @version 2.0 (12/2014)
  */
 public interface ServiceConnection {
 
@@ -494,13 +494,13 @@ public interface ServiceConnection {
 	 * </b></p>
 	 * 
 	 * <p><i>Piece of advice:
-	 * 	The library provides a default implementation of the interface {@link TAPFileManager}:
-	 * 	{@link LocalTAPFileManager}, which stores all files on the local file-system.
+	 * 	The library provides a default implementation of the interface {@link UWSFileManager}:
+	 * 	{@link LocalUWSFileManager}, which stores all files on the local file-system.
 	 * </i></p>
 	 * 
-	 * @return	An instance of {@link TAPFileManager}.
+	 * @return	An instance of {@link UWSFileManager}.
 	 */
-	public TAPFileManager getFileManager();
+	public UWSFileManager getFileManager();
 
 	/**
 	 * <i><b>[MANDATORY]</b></i>
diff --git a/src/tap/TAPFactory.java b/src/tap/TAPFactory.java
index f0ba06fdb83ef58d24d1a5cd285483dc2a5b5fb1..49d3421ec3b963411faacbfd5d2bfaf1b0fe09fd 100644
--- a/src/tap/TAPFactory.java
+++ b/src/tap/TAPFactory.java
@@ -41,6 +41,8 @@ import uws.service.UWSFactory;
 import uws.service.UWSService;
 import uws.service.backup.UWSBackupManager;
 import uws.service.error.ServiceErrorWriter;
+import uws.service.file.UWSFileManager;
+import uws.service.request.RequestParser;
 import adql.parser.ADQLQueryFactory;
 import adql.parser.QueryChecker;
 import adql.query.ADQLQuery;
@@ -448,4 +450,9 @@ public abstract class TAPFactory implements UWSFactory {
 	 */
 	public abstract TAPParameters createTAPParameters(final Map<String,Object> params) throws TAPException;
 
+	@Override
+	public RequestParser createRequestParser(final UWSFileManager fileManager) throws UWSException{
+		return new TAPRequestParser(fileManager);
+	}
+
 }
diff --git a/src/tap/TAPJob.java b/src/tap/TAPJob.java
index 05c7f519bbbbb0a4a1ee10b5dbdf4c091949646a..ab95375bb749cff6dc22704fcfb6ef0604921d09 100644
--- a/src/tap/TAPJob.java
+++ b/src/tap/TAPJob.java
@@ -22,56 +22,105 @@ package tap;
 
 import java.util.List;
 
+import tap.parameters.DALIUpload;
 import tap.parameters.TAPParameters;
-import tap.upload.TableLoader;
 import uws.UWSException;
 import uws.job.ErrorSummary;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.parameters.UWSParameters;
 import uws.job.user.JobOwner;
 
 /**
- * TODO JAVADOC OF THE WHOLE CLASS!
+ * <p>Description of a TAP job. This class is used for asynchronous but also synchronous queries.</p>
+ * 
+ * <p>
+ * 	On the contrary to {@link UWSJob}, it is loading parameters from {@link TAPParameters} instances rather than {@link UWSParameters}.
+ * 	However, {@link TAPParameters} is an extension of {@link UWSParameters}. That's what allow the UWS library to use both {@link TAPJob} and {@link TAPParameters}.
+ * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (10/2014)
+ * @version 2.0 (12/2014)
  */
 public class TAPJob extends UWSJob {
-
 	private static final long serialVersionUID = 1L;
 
+	/** Name of the standard TAP parameter which specifies the type of request to execute: "REQUEST". */
 	public static final String PARAM_REQUEST = "request";
+	/** REQUEST value meaning an ADQL query must be executed: "doQuery". */
 	public static final String REQUEST_DO_QUERY = "doQuery";
+	/** REQUEST value meaning VO service capabilities must be returned: "getCapabilities". */
 	public static final String REQUEST_GET_CAPABILITIES = "getCapabilities";
 
+	/** Name of the standard TAP parameter which specifies the query language: "LANG". <i>(only the ADQL language is supported by default in this version of the library)</i> */
 	public static final String PARAM_LANGUAGE = "lang";
+	/** LANG value meaning ADQL language: "ADQL". */
 	public static final String LANG_ADQL = "ADQL";
+	/** LANG value meaning PQL language: "PQL". <i>(this language is not supported in this version of the library)</i> */
 	public static final String LANG_PQL = "PQL";
 
+	/** Name of the standard TAP parameter which specifies the version of the TAP protocol that must be used: "VERSION". <i>(only the version 1.0 is supported in this version of the library)</i> */
 	public static final String PARAM_VERSION = "version";
+	/** VERSION value meaning the version 1.0 of TAP: "1.0". */
 	public static final String VERSION_1_0 = "1.0";
 
+	/** Name of the standard TAP parameter which specifies the output format (format of a query result): "FORMAT". */
 	public static final String PARAM_FORMAT = "format";
+	/** FORMAT value meaning the VOTable format: "votable". */
 	public static final String FORMAT_VOTABLE = "votable";
 
+	/** Name of the standard TAP parameter which specifies the maximum number of rows that must be returned in the query result: "MAXREC". */
 	public static final String PARAM_MAX_REC = "maxRec";
+	/** Special MAXREC value meaning the number of output rows is not limited. */
 	public static final int UNLIMITED_MAX_REC = -1;
 
+	/** Name of the standard TAP parameter which specifies the query to execute: "QUERY". */
 	public static final String PARAM_QUERY = "query";
+
+	/** Name of the standard TAP parameter which defines the tables to upload in the database for the query execution: "UPLOAD". */
 	public static final String PARAM_UPLOAD = "upload";
 
+	/** Name of the library parameter which informs about a query execution progression: "PROGRESSION". <i>(this parameter is removed once the execution is finished)</i> */
 	public static final String PARAM_PROGRESSION = "progression";
 
-	protected TAPExecutionReport execReport;
+	/** Internal query execution report. */
+	protected TAPExecutionReport execReport = null;
 
+	/** Parameters of this job for its execution. */
 	protected final TAPParameters tapParams;
 
+	/**
+	 * <p>Build a pending TAP job with the given parameters.</p>
+	 * 
+	 * <p><i><u>Note:</u> if the parameter {@link #PARAM_PHASE} (</i>phase<i>) is given with the value {@link #PHASE_RUN}
+	 * the job execution starts immediately after the job has been added to a job list or after {@link #applyPhaseParam(JobOwner)} is called.</i></p>
+	 * 
+	 * @param owner		User who owns this job. <i>MAY BE NULL</i>
+	 * @param tapParams	Set of parameters.
+	 * 
+	 * @throws TAPException	If one of the given parameters has a forbidden or wrong value.
+	 */
 	public TAPJob(final JobOwner owner, final TAPParameters tapParams) throws TAPException{
 		super(owner, tapParams);
 		this.tapParams = tapParams;
 		tapParams.check();
 	}
 
+	/**
+	 * <p>Restore a job in a state defined by the given parameters.
+	 * The phase must be set separately with {@link #setPhase(uws.job.ExecutionPhase, boolean)}, where the second parameter is true.</p>
+	 * 
+	 * @param jobID		ID of the job.
+	 * @param owner		User who owns this job.
+	 * @param params	Set of not-standard UWS parameters (i.e. what is called by {@link UWSJob} as additional parameters ; they includes all TAP parameters).
+	 * @param quote		Quote of this job.
+	 * @param startTime	Date/Time at which this job started. <i>(if not null, it means the job execution was finished, so a endTime should be provided)</i>
+	 * @param endTime	Date/Time at which this job finished.
+	 * @param results	List of results. <i>NULL if the job has not been executed, has been aborted or finished with an error.</i>
+	 * @param error		Error with which this job ends.
+	 * 
+	 * @throws TAPException	If one of the given parameters has a forbidden or wrong value.
+	 */
 	public TAPJob(final String jobID, final JobOwner owner, final TAPParameters params, final long quote, final long startTime, final long endTime, final List<Result> results, final ErrorSummary error) throws TAPException{
 		super(jobID, owner, params, quote, startTime, endTime, results, error);
 		this.tapParams = params;
@@ -79,45 +128,107 @@ public class TAPJob extends UWSJob {
 	}
 
 	/**
-	 * @return The tapParams.
+	 * Get the object storing and managing the set of all (UWS and TAP) parameters.
+	 * 
+	 * @return The object managing all job parameters.
 	 */
 	public final TAPParameters getTapParams(){
 		return tapParams;
 	}
 
+	/**
+	 * <p>Get the value of the REQUEST parameter.</p>
+	 * 
+	 * <p>This value must be {@value #REQUEST_DO_QUERY}.</p>
+	 * 
+	 * @return	REQUEST value.
+	 */
 	public final String getRequest(){
 		return tapParams.getRequest();
 	}
 
+	/**
+	 * Get the value of the FORMAT parameter.
+	 * 
+	 * @return	FORMAT value.
+	 */
 	public final String getFormat(){
 		return tapParams.getFormat();
 	}
 
+	/**
+	 * <p>Get the value of the LANG parameter.</p>
+	 * 
+	 * <p>This value should always be {@value #LANG_ADQL} in this version of the library</p>
+	 * 
+	 * @return	LANG value.
+	 */
 	public final String getLanguage(){
 		return tapParams.getLang();
 	}
 
+	/**
+	 * <p>Get the value of the MAXREC parameter.</p>
+	 * 
+	 * <p>If this value is negative, it means the number of output rows is not limited.</p>
+	 * 
+	 * @return	MAXREC value.
+	 */
 	public final int getMaxRec(){
 		return tapParams.getMaxRec();
 	}
 
+	/**
+	 * Get the value of the QUERY parameter (i.e. the query, in the language returned by {@link #getLanguage()}, to execute).
+	 * 
+	 * @return	QUERY value.
+	 */
 	public final String getQuery(){
 		return tapParams.getQuery();
 	}
 
+	/**
+	 * <p>Get the value of the VERSION parameter.</p>
+	 * 
+	 * <p>This value should be {@value #VERSION_1_0} in this version of the library.</p>
+	 * 
+	 * @return	VERSION value.
+	 */
 	public final String getVersion(){
 		return tapParams.getVersion();
 	}
 
+	/**
+	 * <p>Get the value of the UPLOAD parameter.</p>
+	 * 
+	 * <p>This value must be formatted as specified by the TAP standard (= a semicolon separated list of DALI uploads).</p>
+	 * 
+	 * @return	UPLOAD value.
+	 */
 	public final String getUpload(){
 		return tapParams.getUpload();
 	}
 
-	public final TableLoader[] getTablesToUpload(){
-		return tapParams.getTableLoaders();
+	/**
+	 * <p>Get the list of tables to upload in the database for the query execution.</p>
+	 * 
+	 * <p>The returned array is an interpretation of the UPLOAD parameter.</p>
+	 * 
+	 * @return	List of tables to upload.
+	 */
+	public final DALIUpload[] getTablesToUpload(){
+		return tapParams.getUploadedTables();
 	}
 
 	/**
+	 * <p>Get the execution report.</p>
+	 * 
+	 * <p>
+	 * 	This report is available only during or after the job execution.
+	 * 	It tells in which step the execution is, and how long was the previous steps.
+	 * 	It can also give more information about the number of resulting rows and columns.
+	 * </p>
+	 * 
 	 * @return The execReport.
 	 */
 	public final TAPExecutionReport getExecReport(){
@@ -125,9 +236,18 @@ public class TAPJob extends UWSJob {
 	}
 
 	/**
-	 * @param execReport The execReport to set.
+	 * <p>Set the execution report.</p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	This function can be called only if the job is running or is being restored, otherwise an exception would be thrown.
+	 * 	It should not be used by implementors, but only by the internal library processing.
+	 * </b></p>
+	 * 
+	 * @param execReport	An execution report.
+	 * 
+	 * @throws UWSException	If this job has never been restored and is not running.
 	 */
-	public final void setExecReport(TAPExecutionReport execReport) throws UWSException{
+	public final void setExecReport(final TAPExecutionReport execReport) throws UWSException{
 		if (getRestorationDate() == null && !isRunning())
 			throw new UWSException("Impossible to set an execution report if the job is not in the EXECUTING phase ! Here, the job \"" + jobId + "\" is in the phase " + getPhase());
 		this.execReport = execReport;
diff --git a/src/tap/TAPRequestParser.java b/src/tap/TAPRequestParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..91c24c6099e227ee1212fbdaccf8738ac06359ac
--- /dev/null
+++ b/src/tap/TAPRequestParser.java
@@ -0,0 +1,216 @@
+package tap;
+
+/*
+ * This file is part of TAPLibrary.
+ * 
+ * TAPLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * TAPLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import uws.UWSException;
+import uws.UWSToolBox;
+import uws.service.file.UWSFileManager;
+import uws.service.request.FormEncodedParser;
+import uws.service.request.MultipartParser;
+import uws.service.request.NoEncodingParser;
+import uws.service.request.RequestParser;
+import uws.service.request.UploadFile;
+
+/**
+ * <p>This parser adapts the request parser to use in function of the request content-type:</p>
+ * <ul>
+ * 	<li><b>application/x-www-form-urlencoded</b>: {@link FormEncodedParser}</li>
+ * 	<li><b>multipart/form-data</b>: {@link MultipartParser}</li>
+ * 	<li><b>other</b>: {@link NoEncodingParser} (the whole request body will be stored as one single parameter)</li>
+ * </ul>
+ * 
+ * <p>
+ * 	The request body size is limited for the multipart AND the no-encoding parsers. If you want to change this limit,
+ * 	you MUST do it for each of these parsers, setting the following static attributes: resp. {@link MultipartParser#SIZE_LIMIT}
+ * 	and {@link NoEncodingParser#SIZE_LIMIT}.
+ * </p> 
+ * 
+ * <p><i>Note:
+ * 	If you want to change the support other request parsing, you will have to write your own {@link RequestParser} implementation.
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 2.0 (12/2014)
+ * @since 2.0
+ */
+public class TAPRequestParser implements RequestParser {
+
+	/** File manager to use to create {@link UploadFile} instances.
+	 * It is required by this new object to execute open, move and delete operations whenever it could be asked. */
+	private final UWSFileManager fileManager;
+
+	/** {@link RequestParser} to use when a application/x-www-form-urlencoded request must be parsed. This attribute is set by {@link #parse(HttpServletRequest)}
+	 * only when needed, by calling the function {@link #getFormParser()}. */
+	private RequestParser formParser = null;
+
+	/** {@link RequestParser} to use when a multipart/form-data request must be parsed. This attribute is set by {@link #parse(HttpServletRequest)}
+	 * only when needed, by calling the function {@link #getMultipartParser()}. */
+	private RequestParser multipartParser = null;
+
+	/** {@link RequestParser} to use when none of the other parsers can be used ; it will then transform the whole request body in a parameter called "JDL"
+	 * (Job Description Language). This attribute is set by {@link #parse(HttpServletRequest)} only when needed, by calling the function
+	 * {@link #getNoEncodingParser()}. */
+	private RequestParser noEncodingParser = null;
+
+	/**
+	 * Build a {@link RequestParser} able to choose the most appropriate {@link RequestParser} in function of the request content-type.
+	 * 
+	 * @param fileManager	The file manager to use in order to store any eventual upload. <b>MUST NOT be NULL</b>
+	 */
+	public TAPRequestParser(final UWSFileManager fileManager){
+		if (fileManager == null)
+			throw new NullPointerException("Missing file manager => can not create a TAPRequestParser!");
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	public Map<String,Object> parse(final HttpServletRequest req) throws UWSException{
+		if (req == null)
+			return new HashMap<String,Object>();
+
+		// Get the method:
+		String method = (req.getMethod() == null) ? "" : req.getMethod().toLowerCase();
+
+		if (method.equals("post") || method.equals("put")){
+			Map<String,Object> params = null;
+
+			// Get the parameters:
+			if (FormEncodedParser.isFormEncodedRequest(req))
+				params = getFormParser().parse(req);
+			else if (MultipartParser.isMultipartContent(req))
+				params = getMultipartParser().parse(req);
+			else
+				params = getNoEncodingParser().parse(req);
+
+			// Only for POST requests, the parameters specified in the URL must be added:
+			if (method.equals("post"))
+				params = UWSToolBox.addGETParameters(req, (params == null) ? new HashMap<String,Object>() : params);
+
+			return params;
+		}else
+			return UWSToolBox.addGETParameters(req, new HashMap<String,Object>());
+	}
+
+	/**
+	 * Get the {@link RequestParser} to use for application/x-www-form-urlencoded HTTP requests.
+	 * This parser may be created if not already done.
+	 * 
+	 * @return	The {@link RequestParser} to use for application/x-www-form-urlencoded requests. <i>Never NULL</i>
+	 */
+	private synchronized final RequestParser getFormParser(){
+		return (formParser != null) ? formParser : (formParser = new FormEncodedParser(){
+			@Override
+			protected void consumeParameter(String name, Object value, final Map<String,Object> allParams){
+				// Modify the value if it is an UPLOAD parameter:
+				if (name != null && name.equalsIgnoreCase("upload")){
+					// if no value, ignore this parameter:
+					if (value == null)
+						return;
+					// put in lower case the parameter name:
+					name = name.toLowerCase();
+					// transform the value in a String array:
+					value = append((String)value, (allParams.containsKey("upload") ? (String[])allParams.get("upload") : null));
+				}
+
+				// Update the map, normally:
+				super.consumeParameter(name, value, allParams);
+			}
+		});
+	}
+
+	/**
+	 * Get the {@link RequestParser} to use for multipart/form-data HTTP requests.
+	 * This parser may be created if not already done.
+	 * 
+	 * @return	The {@link RequestParser} to use for multipart/form-data requests. <i>Never NULL</i>
+	 */
+	private synchronized final RequestParser getMultipartParser(){
+		return (multipartParser != null) ? multipartParser : (multipartParser = new MultipartParser(fileManager){
+			@Override
+			protected void consumeParameter(String name, Object value, final Map<String,Object> allParams){
+				// Modify the value if it is an UPLOAD parameter:
+				if (name != null && name.equalsIgnoreCase(TAPJob.PARAM_UPLOAD)){
+					// if no value, ignore this parameter:
+					if (value == null)
+						return;
+					// ignore also parameter having the same name in the same case and which is a file (only strings can be processed as DALI UPLOAD parameter):
+					else if (name.equals(TAPJob.PARAM_UPLOAD) && value instanceof UploadFile){
+						try{
+							((UploadFile)value).deleteFile();
+						}catch(IOException ioe){}
+						return;
+					}
+					// use the same case for the parameter name:
+					name = TAPJob.PARAM_UPLOAD;
+					// transform the value in a String array:
+					value = append((String)value, (allParams.containsKey(TAPJob.PARAM_UPLOAD) ? (String[])allParams.get(TAPJob.PARAM_UPLOAD) : null));
+				}
+
+				// Update the map, normally:
+				super.consumeParameter(name, value, allParams);
+			}
+		});
+	}
+
+	/**
+	 * Get the {@link RequestParser} to use for HTTP requests whose the content type is neither application/x-www-form-urlencoded nor multipart/form-data.
+	 * This parser may be created if not already done.
+	 * 
+	 * @return	The {@link RequestParser} to use for requests whose the content-type is not supported. <i>Never NULL</i>
+	 */
+	private synchronized final RequestParser getNoEncodingParser(){
+		return (noEncodingParser == null) ? (noEncodingParser = new NoEncodingParser(fileManager)) : noEncodingParser;
+	}
+
+	/**
+	 * Create a new array in which the given String is appended at the end of the given array.
+	 * 
+	 * @param value		String to append in the array.
+	 * @param oldValue	The array after which the given String must be appended.
+	 * 
+	 * @return	The new array containing the values of the array and then the given String.
+	 */
+	private final static String[] append(final String value, final String[] oldValue){
+		// Create the corresponding array of Strings: 
+		// ...if the array already exists, extend it:
+		String[] newValue;
+		if (oldValue != null){
+			newValue = new String[oldValue.length + 1];
+			for(int i = 0; i < oldValue.length; i++)
+				newValue[i] = oldValue[i];
+		}
+		// ...otherwise, create a new array:
+		else
+			newValue = new String[1];
+
+		// Add the new value in the array:
+		newValue[newValue.length - 1] = value;
+
+		// Update the value to put inside the map:
+		return newValue;
+	}
+
+}
diff --git a/src/tap/backup/DefaultTAPBackupManager.java b/src/tap/backup/DefaultTAPBackupManager.java
index 714c8174e0970a49d0226dcf46b754aa634839c5..5e740ddd33e13f8630969c81fd05ae4ec7ba05b1 100644
--- a/src/tap/backup/DefaultTAPBackupManager.java
+++ b/src/tap/backup/DefaultTAPBackupManager.java
@@ -20,17 +20,25 @@ package tap.backup;
  *                       Astronomisches Rechen Institut (ARI)
  */
 
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+
+import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.json.Json4Uws;
 
 import tap.ExecutionProgression;
 import tap.TAPExecutionReport;
 import tap.TAPJob;
+import tap.parameters.DALIUpload;
 import uws.UWSException;
 import uws.job.UWSJob;
 import uws.service.UWS;
 import uws.service.backup.DefaultUWSBackupManager;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.UploadFile;
 
 /**
  * <p>Let backup all TAP asynchronous jobs.</p>
@@ -38,7 +46,7 @@ import uws.service.log.UWSLog.LogLevel;
  * <p><i>note: Basically the saved data are the same, but in addition some execution statistics are also added.</i></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (07/2014)
+ * @version 2.0 (12/2014)
  * 
  * @see DefaultUWSBackupManager
  */
@@ -96,11 +104,45 @@ public class DefaultTAPBackupManager extends DefaultUWSBackupManager {
 
 	@Override
 	protected JSONObject getJSONJob(UWSJob job, String jlName) throws UWSException, JSONException{
-		JSONObject json = super.getJSONJob(job, jlName);
+		JSONObject jsonJob = Json4Uws.getJson(job);
 
+		// Re-Build the parameters map, by separating the uploads and the "normal" parameters:
+		JSONArray uploads = new JSONArray();
+		JSONObject params = new JSONObject();
+		Object val;
+		for(String name : job.getAdditionalParameters()){
+			// get the raw value:
+			val = job.getAdditionalParameterValue(name);
+			// if no value, skip this item:
+			if (val == null)
+				continue;
+			// if an array, build a JSON array of strings:
+			else if (val.getClass().isArray()){
+				JSONArray array = new JSONArray();
+				for(Object o : (Object[])val){
+					if (o != null && o instanceof DALIUpload)
+						array.put(getDALIUploadJson((DALIUpload)o));
+					else if (o != null)
+						array.put(o.toString());
+				}
+				params.put(name, array);
+			}
+			// if upload file:
+			else if (val instanceof UploadFile)
+				uploads.put(getUploadJson((UploadFile)val));
+			// if DALIUpload:
+			else if (val instanceof DALIUpload)
+				params.put(name, getDALIUploadJson((DALIUpload)val));
+			// otherwise, just put the value:
+			else
+				params.put(name, val);
+		}
+
+		// Deal with the execution report of the job:
 		if (job instanceof TAPJob && ((TAPJob)job).getExecReport() != null){
 			TAPExecutionReport execReport = ((TAPJob)job).getExecReport();
 
+			// Build the JSON representation of the execution report of this job:
 			JSONObject jsonExecReport = new JSONObject();
 			jsonExecReport.put("success", execReport.success);
 			jsonExecReport.put("uploadduration", execReport.getUploadDuration());
@@ -109,52 +151,153 @@ public class DefaultTAPBackupManager extends DefaultUWSBackupManager {
 			jsonExecReport.put("formattingduration", execReport.getFormattingDuration());
 			jsonExecReport.put("totalduration", execReport.getTotalDuration());
 
-			JSONObject params = json.getJSONObject(UWSJob.PARAM_PARAMETERS);
-			if (params == null)
-				params = new JSONObject();
+			// Add the execution report into the parameters list:
 			params.put("tapexecreport", jsonExecReport);
-
-			json.put(UWSJob.PARAM_PARAMETERS, params);
 		}
 
-		return json;
+		// Add the parameters and the uploads inside the JSON representation of the job:
+		jsonJob.put(UWSJob.PARAM_PARAMETERS, params);
+		jsonJob.put("uwsUploads", uploads);
+
+		// Add the job owner:
+		jsonJob.put(UWSJob.PARAM_OWNER, (job != null && job.getOwner() != null) ? job.getOwner().getID() : null);
+
+		// Add the name of the job list owning the given job:
+		jsonJob.put("jobListName", jlName);
+
+		return jsonJob;
+	}
+
+	/**
+	 * Get the JSON representation of the given {@link DALIUpload}.
+	 * 
+	 * @param upl	The DALI upload specification to serialize in JSON.
+	 * 
+	 * @return		Its JSON representation.
+	 * 
+	 * @throws JSONException	If there is an error while building the JSON object.
+	 * 
+	 * @since 2.0
+	 */
+	protected JSONObject getDALIUploadJson(final DALIUpload upl) throws JSONException{
+		if (upl == null)
+			return null;
+		JSONObject o = new JSONObject();
+		o.put("label", upl.label);
+		o.put("uri", upl.uri);
+		o.put("file", (upl.file == null ? null : upl.file.paramName));
+		return o;
 	}
 
 	@Override
 	protected void restoreOtherJobParams(JSONObject json, UWSJob job) throws UWSException{
-		if (job != null && json != null && job instanceof TAPJob){
-			TAPJob tapJob = (TAPJob)job;
-			Object obj = job.getAdditionalParameterValue("tapexecreport");
-			if (obj != null){
-				if (obj instanceof JSONObject){
-					JSONObject jsonExecReport = (JSONObject)obj;
-					TAPExecutionReport execReport = new TAPExecutionReport(job.getJobId(), false, tapJob.getTapParams());
-					String[] keys = JSONObject.getNames(jsonExecReport);
-					for(String key : keys){
+		// 0. Nothing to do in this function if the job is missing OR if it is not an instance of TAPJob:
+		if (job == null || !(job instanceof TAPJob))
+			return;
+
+		// 1. Build correctly the TAP UPLOAD parameter (the value of this parameter should be an array of DALIUpload):
+		if (json != null && json.has(TAPJob.PARAM_PARAMETERS)){
+			try{
+				// Retrieve the whole list of parameters:
+				JSONObject params = json.getJSONObject(TAPJob.PARAM_PARAMETERS);
+				// If there is an UPLOAD parameter, convert the JSON array into a DALIUpload[] and add it to the job:
+				if (params.has(TAPJob.PARAM_UPLOAD)){
+					// retrieve the JSON array:
+					JSONArray uploads = params.getJSONArray(TAPJob.PARAM_UPLOAD);
+					// for each item of this array, build the corresponding DALIUpload and add it into an ArrayList:
+					DALIUpload upl;
+					ArrayList<DALIUpload> lstTAPUploads = new ArrayList<DALIUpload>();
+					for(int i = 0; i < uploads.length(); i++){
 						try{
-							if (key.equalsIgnoreCase("success"))
-								execReport.success = jsonExecReport.getBoolean(key);
-							else if (key.equalsIgnoreCase("uploadduration"))
-								execReport.setDuration(ExecutionProgression.UPLOADING, jsonExecReport.getLong(key));
-							else if (key.equalsIgnoreCase("parsingduration"))
-								execReport.setDuration(ExecutionProgression.PARSING, jsonExecReport.getLong(key));
-							else if (key.equalsIgnoreCase("executionduration"))
-								execReport.setDuration(ExecutionProgression.EXECUTING_ADQL, jsonExecReport.getLong(key));
-							else if (key.equalsIgnoreCase("formattingduration"))
-								execReport.setDuration(ExecutionProgression.WRITING_RESULT, jsonExecReport.getLong(key));
-							else if (key.equalsIgnoreCase("totalduration"))
-								execReport.setTotalDuration(jsonExecReport.getLong(key));
-							else
-								getLogger().logUWS(LogLevel.WARNING, obj, "RESTORATION", "The execution report attribute '" + key + "' of the job \"" + job.getJobId() + "\" has been ignored because unknown!", null);
+							upl = getDALIUpload(uploads.getJSONObject(i), job);
+							if (upl != null)
+								lstTAPUploads.add(upl);
 						}catch(JSONException je){
-							getLogger().logUWS(LogLevel.ERROR, obj, "RESTORATION", "Incorrect JSON format for the execution report serialization of the job \"" + job.getJobId() + "\" (attribute: \"" + key + "\")!", je);
+							getLogger().logUWS(LogLevel.ERROR, uploads.get(i), "RESTORATION", "Incorrect JSON format for a DALIUpload of the job \"" + job.getJobId() + "\": a JSONObject was expected!", null);
 						}
 					}
-					tapJob.setExecReport(execReport);
-				}else if (!(obj instanceof JSONObject))
-					getLogger().logUWS(LogLevel.WARNING, obj, "RESTORATION", "Impossible to restore the execution report of the job \"" + job.getJobId() + "\" because the stored object is not a JSONObject!", null);
-			}
+					// finally convert the ArrayList into a DALIUpload[] and add it inside the parameters list of the job:
+					job.addOrUpdateParameter(TAPJob.PARAM_UPLOAD, lstTAPUploads.toArray(new DALIUpload[lstTAPUploads.size()]));
+				}
+			}catch(JSONException ex){}
+		}
+
+		// 2. Get the execution report and add it into the given job:
+		TAPJob tapJob = (TAPJob)job;
+		Object obj = job.getAdditionalParameterValue("tapexecreport");
+		if (obj != null){
+			if (obj instanceof JSONObject){
+				JSONObject jsonExecReport = (JSONObject)obj;
+				TAPExecutionReport execReport = new TAPExecutionReport(job.getJobId(), false, tapJob.getTapParams());
+				String[] keys = JSONObject.getNames(jsonExecReport);
+				for(String key : keys){
+					try{
+						if (key.equalsIgnoreCase("success"))
+							execReport.success = jsonExecReport.getBoolean(key);
+						else if (key.equalsIgnoreCase("uploadduration"))
+							execReport.setDuration(ExecutionProgression.UPLOADING, jsonExecReport.getLong(key));
+						else if (key.equalsIgnoreCase("parsingduration"))
+							execReport.setDuration(ExecutionProgression.PARSING, jsonExecReport.getLong(key));
+						else if (key.equalsIgnoreCase("executionduration"))
+							execReport.setDuration(ExecutionProgression.EXECUTING_ADQL, jsonExecReport.getLong(key));
+						else if (key.equalsIgnoreCase("formattingduration"))
+							execReport.setDuration(ExecutionProgression.WRITING_RESULT, jsonExecReport.getLong(key));
+						else if (key.equalsIgnoreCase("totalduration"))
+							execReport.setTotalDuration(jsonExecReport.getLong(key));
+						else
+							getLogger().logUWS(LogLevel.WARNING, obj, "RESTORATION", "The execution report attribute '" + key + "' of the job \"" + job.getJobId() + "\" has been ignored because unknown!", null);
+					}catch(JSONException je){
+						getLogger().logUWS(LogLevel.ERROR, obj, "RESTORATION", "Incorrect JSON format for the execution report serialization of the job \"" + job.getJobId() + "\" (attribute: \"" + key + "\")!", je);
+					}
+				}
+				tapJob.setExecReport(execReport);
+			}else if (!(obj instanceof JSONObject))
+				getLogger().logUWS(LogLevel.WARNING, obj, "RESTORATION", "Impossible to restore the execution report of the job \"" + job.getJobId() + "\" because the stored object is not a JSONObject!", null);
 		}
 	}
 
+	/**
+	 * Restore a {@link DALIUpload} from its JSON representation.
+	 * 
+	 * @param item	{@link JSONObject} representing the {@link DALIUpload} to restore.
+	 * @param job	The job which owns this upload.
+	 * 
+	 * @return	The corresponding {@link DALIUpload} or NULL, if an error occurs while converting the JSON.
+	 * 
+	 * @since 2.0
+	 */
+	private DALIUpload getDALIUpload(final JSONObject item, final UWSJob job){
+		try{
+
+			// Get its label:
+			String label = item.getString("label");
+
+			// Build the DALIUpload object:
+			/* If the upload spec. IS A FILE, the attribute 'file' should point toward a job parameter
+			 * being an UploadFile. If so, get it and use it to build the DALIUpload: */
+			if (item.has("file")){
+				Object f = job.getAdditionalParameterValue(item.getString("file"));
+				if (f == null || !(f instanceof UploadFile))
+					getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect JSON format for the DALIUpload labelled \"" + label + "\" of the job \"" + job.getJobId() + "\": \"" + item.getString("file") + "\" is not pointing a job parameter representing a file!", null);
+				return new DALIUpload(label, (UploadFile)f);
+			}
+			/* If the upload spec. IS A URI, the attribute 'uri' should contain it
+			 * and should be used to build the DALIUpload: */
+			else if (item.has("uri")){
+				try{
+					return new DALIUpload(label, new URI(item.getString("uri")), uws.getFileManager());
+				}catch(URISyntaxException e){
+					getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect URI for the DALIUpload labelled \"" + label + "\" of the job \"" + job.getJobId() + "\": \"" + item.getString("uri") + "\"!", null);
+				}
+			}
+			/* If none of this both attribute is provided, it is an error and it is not possible to build the DALIUpload. */
+			else
+				getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect JSON format for the DALIUpload labelled \"" + label + "\" of the job \"" + job.getJobId() + "\": missing attribute 'file' or 'uri'!", null);
+
+		}catch(JSONException je){
+			getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect JSON format for a DALIUpload of the job \"" + job.getJobId() + "\": missing attribute 'label'!", null);
+		}
+
+		return null;
+	}
 }
diff --git a/src/tap/file/LocalTAPFileManager.java b/src/tap/file/LocalTAPFileManager.java
deleted file mode 100644
index f7555c36be2593d5928b163935c5e74fe1397106..0000000000000000000000000000000000000000
--- a/src/tap/file/LocalTAPFileManager.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package tap.file;
-
-/*
- * This file is part of TAPLibrary.
- * 
- * TAPLibrary is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * TAPLibrary is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public License
- * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
- * 
- * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomisches Rechen Institut (ARI)
- */
-
-import java.io.File;
-
-import uws.UWSException;
-import uws.service.file.DefaultOwnerGroupIdentifier;
-import uws.service.file.LocalUWSFileManager;
-import uws.service.file.OwnerGroupIdentifier;
-
-/**
- * <p>
- * 	Lets creating and managing all files needed in a TAP service.
- * 	These files are: UWS job results and errors, log files, backup files and the upload directory.
- * </p>
- * <p>
- * 	All files are written in the local machine, into the given directory.
- * </p>
- * 
- * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
- * 
- * @see LocalUWSFileManager
- */
-public class LocalTAPFileManager extends LocalUWSFileManager implements TAPFileManager {
-
-	/** Default name of the upload directory. */
-	public final static String DEFAULT_UPLOAD_DIRECTORY_NAME = "Upload";
-
-	/** Local directory in which all uploaded files will be kept until they are read or ignored (in this case, they will be deleted). */
-	private final File uploadDirectory;
-
-	/**
-	 * <p>Builds a {@link TAPFileManager} which manages all UWS files in the given directory.</p>
-	 * <p>
-	 * 	There will be one directory for each owner ID and owner directories will be grouped
-	 * 	thanks to {@link DefaultOwnerGroupIdentifier}.
-	 * </p>
-	 * 
-	 * @param root				TAP root directory.
-	 *
-	 * @throws UWSException		If the given root directory is <i>null</i>, is not a directory or has not the READ and WRITE permissions.
-	 * 
-	 * @see LocalUWSFileManager#LocalUWSFileManager(File)
-	 * @see #getUploadDirectoryName()
-	 */
-	public LocalTAPFileManager(File root) throws UWSException{
-		super(root);
-		uploadDirectory = new File(rootDirectory, getUploadDirectoryName());
-	}
-
-	/**
-	 * <p>Builds a {@link TAPFileManager} which manages all UWS files in the given directory.</p>
-	 * <p>
-	 * 	If, according to the third parameter, the owner directories must be grouped,
-	 * 	the {@link DefaultOwnerGroupIdentifier} will be used.
-	 * </p>
-	 * 
-	 * @param root						TAP root directory.
-	 * @param oneDirectoryForEachUser	<i>true</i> to create one directory for each owner ID, <i>false</i> otherwise.
-	 * @param groupUserDirectories		<i>true</i> to group user directories, <i>false</i> otherwise.
-	 * 									<i><u>note:</u> this value is ignored if the previous parameter is false.</i>
-	 *
-	 * @throws UWSException				If the given root directory is <i>null</i>, is not a directory or has not the READ and WRITE permissions.
-	 * 
-	 * @see LocalUWSFileManager#LocalUWSFileManager(File, boolean, boolean)
-	 * @see #getUploadDirectoryName()
-	 */
-	public LocalTAPFileManager(File root, boolean oneDirectoryForEachUser, boolean groupUserDirectories) throws UWSException{
-		super(root, oneDirectoryForEachUser, groupUserDirectories);
-		uploadDirectory = new File(rootDirectory, getUploadDirectoryName());
-	}
-
-	/**
-	 * Builds a {@link TAPFileManager} which manages all UWS files in the given directory.
-	 * 
-	 * @param root						TAP root directory.
-	 * @param oneDirectoryForEachUser	<i>true</i> to create one directory for each owner ID, <i>false</i> otherwise.
-	 * @param groupUserDirectories		<i>true</i> to group user directories, <i>false</i> otherwise.
-	 * 									<i><u>note:</u> this value is ignored if the previous parameter is false.</i>
-	 * @param ownerGroupIdentifier		The "function" to use to identify the group of a job owner.
-	 * 									<i><ul>
-	 * 										<li><u>note 1:</u> this value is ignored if one of the two previous parameters is false.</li>
-	 * 										<li><u>note 2:</u> if this value is null but the previous parameters are true,
-	 * 											{@link DefaultOwnerGroupIdentifier} will be chosen as default group identifier.</li>
-	 *									</ul></i>
-	 *
-	 * @throws UWSException				If the given root directory is <i>null</i>, is not a directory or has not the READ and WRITE permissions.
-	 * 
-	 * @see LocalUWSFileManager#LocalUWSFileManager(File, boolean, boolean, OwnerGroupIdentifier)
-	 * @see #getUploadDirectoryName()
-	 */
-	public LocalTAPFileManager(File root, boolean oneDirectoryForEachUser, boolean groupUserDirectories, OwnerGroupIdentifier ownerGroupIdentifier) throws UWSException{
-		super(root, oneDirectoryForEachUser, groupUserDirectories, ownerGroupIdentifier);
-		uploadDirectory = new File(rootDirectory, getUploadDirectoryName());
-	}
-
-	/**
-	 * <p>Gets the name of the directory in which all uploaded files will be saved.</p>
-	 * 
-	 * <p><i><u>note 1:</u> this function is called ONLY one time: at the creation.</i></p>
-	 * <p><i><u>note 2:</u> by default, this function returns: {@link #DEFAULT_UPLOAD_DIRECTORY_NAME}.</i></p>
-	 * 
-	 * @return	The name of the upload directory.
-	 */
-	protected String getUploadDirectoryName(){
-		return DEFAULT_UPLOAD_DIRECTORY_NAME;
-	}
-
-	@Override
-	public final File getUploadDirectory(){
-		if (uploadDirectory != null && !uploadDirectory.exists())
-			uploadDirectory.mkdirs();
-		return uploadDirectory;
-	}
-
-}
diff --git a/src/tap/file/TAPFileManager.java b/src/tap/file/TAPFileManager.java
deleted file mode 100644
index f15a024693c7c3747438a3cb6e9f653832d37e98..0000000000000000000000000000000000000000
--- a/src/tap/file/TAPFileManager.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package tap.file;
-
-/*
- * This file is part of TAPLibrary.
- * 
- * TAPLibrary is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * TAPLibrary is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public License
- * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
- * 
- * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
- */
-
-import java.io.File;
-
-import uws.service.file.UWSFileManager;
-
-/**
- * Minimal API of the object which will be used by the TAP service (but more particularly by its UWS resource)
- * to create, delete, write and read files needed to the service (i.e. results, errors, logs, backups, upload files).
- * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2012
- * 
- * @see UWSFileManager
- */
-public interface TAPFileManager extends UWSFileManager {
-
-	/**
-	 * Local directory in which all uploaded files will be kept until they are read or ignored (in this case, they will be deleted).
-	 * 
-	 * @return	Path of the directory in which uploaded files must be written.
-	 */
-	public File getUploadDirectory();
-
-}
diff --git a/src/tap/log/DefaultTAPLog.java b/src/tap/log/DefaultTAPLog.java
index 629e24de6301c70c173c74fab2b4a881e573b6cd..e2269bc30ff94f6490a0860a64ce40e60f26991e 100644
--- a/src/tap/log/DefaultTAPLog.java
+++ b/src/tap/log/DefaultTAPLog.java
@@ -27,15 +27,15 @@ import java.sql.SQLException;
 import tap.TAPExecutionReport;
 import tap.TAPSyncJob;
 import tap.db.DBConnection;
-import tap.file.TAPFileManager;
 import tap.parameters.TAPParameters;
+import uws.service.file.UWSFileManager;
 import uws.service.log.DefaultUWSLog;
 
 /**
  * Default implementation of the {@link TAPLog} interface which lets logging any message about a TAP service.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (11/2014)
+ * @version 2.0 (12/2014)
  * 
  * @see DefaultUWSLog
  */
@@ -43,7 +43,7 @@ public class DefaultTAPLog extends DefaultUWSLog implements TAPLog {
 
 	/**
 	 * <p>Builds a {@link TAPLog} which will use the given file
-	 * manager to get the log output (see {@link TAPFileManager#getLogOutput(LogLevel, String)}).</p>
+	 * manager to get the log output (see {@link UWSFileManager#getLogOutput(LogLevel, String)}).</p>
 	 * 
 	 * <p><i><u>note 1</u>: This constructor is particularly useful if the way of managing log output may change in the given file manager.
 	 * Indeed, the output may change in function of the type of message to log ({@link LogLevel}).</i></p>
@@ -53,9 +53,9 @@ public class DefaultTAPLog extends DefaultUWSLog implements TAPLog {
 	 * 
 	 * @param fm	A TAP file manager.
 	 * 
-	 * @see DefaultUWSLog#DefaultUWSLog(uws.service.file.UWSFileManager)
+	 * @see DefaultUWSLog#DefaultUWSLog(UWSFileManager)
 	 */
-	public DefaultTAPLog(final TAPFileManager fm){
+	public DefaultTAPLog(final UWSFileManager fm){
 		super(fm);
 	}
 
diff --git a/src/tap/parameters/DALIUpload.java b/src/tap/parameters/DALIUpload.java
new file mode 100644
index 0000000000000000000000000000000000000000..3356510b3217e46225a5bb0ecaf3a38ff6d70cc9
--- /dev/null
+++ b/src/tap/parameters/DALIUpload.java
@@ -0,0 +1,602 @@
+package tap.parameters;
+
+/*
+ * This file is part of TAPLibrary.
+ * 
+ * TAPLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * TAPLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import tap.TAPException;
+import tap.TAPJob;
+import uws.UWSException;
+import uws.service.file.UWSFileManager;
+import uws.service.file.UnsupportedURIProtocolException;
+import uws.service.request.RequestParser;
+import uws.service.request.UploadFile;
+
+/**
+ * <p>Description of an uploaded content specified using the DALI/TAP syntax.</p>
+ * 
+ * <h3>How to access the upload content?</h3>
+ * 
+ * <p>
+ * 	This parameter is either a reference to a distant content and is then specified by a URI,
+ * 	or a pointer to the stored version of a file submitted inline in a HTTP request. In both cases,
+ * 	this class lets access the upload content with the function {@link #open()}.
+ * </p>
+ * 
+ * <h3>How to get {@link DALIUpload} objects from HTTP request parameters?</h3>
+ * 
+ * <p>
+ * 	The static function {@link #getDALIUploads(Map, boolean, UWSFileManager)} should be used in order to
+ * 	extract the {@link DALIUpload} items specified in a list of request parameters.
+ * </p>
+ * <p><i>Note:
+ * 	It is recommended to provide these parameters as a map generated by a {@link RequestParser}.
+ * 	If not, you should ensure that values of the map associated to the "UPLOAD" parameter(s) are {@link String}s, {@link String}[]s,
+ * 	{@link DALIUpload}s, {@link DALIUpload}[]s or {@link Object}[] containing {@link String}s and/or {@link DALIUpload}s.
+ * 	Besides, the request parameters referenced using the syntax "param:{param-name}" must be instances of only {@link UploadFile}
+ * 	or an array of {@link Object}s containing at least one {@link UploadFile} instance (if several are found, just the last one will be used).
+ * </i></p>
+ * <p>
+ * 	Calling this function will also modify a little the given list of parameters by rewriting the "UPLOAD" parameter and
+ * 	removing unreferenced uploaded files (from the list and from the file-system).
+ * </p>
+ * 
+ * <h3>Reminder about the "UPLOAD" parameter</h3>
+ * 
+ * <p>
+ * 	The IVOA standards DAL and TAP define both the same special parameter: "UPLOAD" (not case-sensitive).
+ * </p>
+ * 
+ * <p>
+ * 	This parameter lists all upload items. A such item can be either an inline file or a reference to a distant file.
+ * 	In both cases, it is specified as a URI. The parameter "UPLOAD" sets also a label/name to this item.
+ * 	The syntax to use for a single item is the following: "{label},{URI}". Several items can be provided, but there is
+ * 	a slight difference between DALI and TAP in the way to do it. DALI says that multiple uploads MUST be done
+ * 	by several submit of a single "UPLOAD" parameter with the syntax described above. TAP says that multiple uploads CAN
+ * 	be done in one "UPLOAD" parameter by separating each item with a semicolon (;). For instance:
+ * </p>
+ * <ul>
+ * 	<li><b>In TAP:</b> "UPLOAD=tableA,param:foo;tableB,http://..." =&gt; only 1 parameter for 2 uploads</li>
+ * 	<li><b>In DALI:</b> "UPLOAD=tableA,param:foo" and "UPLOAD=tableB,http://..." =&gt; 2 parameters, one for each upload</li>
+ * </ul>
+ * 
+ * <p><i>Note:
+ * 	The drawback of the TAP method is: what happens when a URI contains a semicolon? URI can indeed contain a such character
+ * 	and in this case the parsing becomes more tricky, or even impossible in some cases. In such cases, it is strongly
+ * 	recommended to either encode the URI (so the ";" becomes "%3B") or to forbid the TAP syntax. This latter can be
+ * 	done by setting the second parameter of {@link #getDALIUploads(Map, boolean, UWSFileManager)} to <i>false</i>.
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 2.0 (12/2014)
+ * @since 2.0
+ * 
+ * @see RequestParser
+ */
+public class DALIUpload {
+
+	/** <p>Pointer to the stored version of the file submitted inline in a HTTP request.</p>
+	 * <p><i>Note:
+	 * 	If NULL, this {@link DALIUpload} is then a "byReference" upload, meaning that its content is distant
+	 * 	and can be accessed only with the URI {@link #uri}.
+	 * </i></p> */
+	public final UploadFile file;
+
+	/** <p>URI toward a distant resource.</p>
+	 * <p><i>Note:
+	 * 	If NULL, this {@link DALIUpload} corresponds to a file submitted inline in a HTTP request.
+	 * 	Its content has then been stored by this service and can be accessed using the pointer {@link #file}.
+	 *  </i></p>*/
+	public final URI uri;
+
+	/** <p>Name to use in the service to label this upload.</p>
+	 * <p><i>Note:
+	 * 	In a TAP service, this label is the name of the table to create in the database
+	 * 	when creating the corresponding table inside it.
+	 * </i></p> */
+	public final String label;
+
+	/** The file manager to use when a stream will be opened toward the given URI.
+	 * It should know how to access it, because the URI can use a URL scheme (http, https, ftp) but also another scheme
+	 * unknown by the library (e.g. ivo, vos). */
+	protected final UWSFileManager fileManager;
+
+	/**
+	 * <p>Build a {@link DALIUpload} whose the content has been submitted inline in an HTTP request.</p>
+	 * 
+	 * <p>
+	 * 	A such upload has been specified by referencing another HTTP request parameter containing an inline file.
+	 * 	The used syntax was then: "{label},param:{param-name}".
+	 * </p>
+	 * 
+	 * @param label	Label of the DALIUpload (i.e. {label} inside an "UPLOAD" parameter value "{label},{URI}").
+	 *             	<i>Note: If NULL, the file name will be used as label.</i>
+	 * @param file	Pointer to the uploaded file.
+	 */
+	public DALIUpload(final String label, final UploadFile file){
+		if (file == null)
+			throw new NullPointerException("Missing UploadFile! => Can not build a DaliUpload instance.");
+
+		this.label = (label == null) ? file.paramName : label;
+		this.file = file;
+		this.uri = null;
+		this.fileManager = null;
+	}
+
+	/**
+	 * <p>Build a {@link DALIUpload} whose the content is distant and specified by a URI.</p>
+	 * 
+	 * <p>
+	 * 	A such upload has been specified by referencing a URI (whose the scheme is different from "param").
+	 * 	The used syntax was then: "{label},{URI}".
+	 * </p>
+	 * 
+	 * @param label			Label of the DALIUpload (i.e. {label} inside an "UPLOAD" parameter value "{label},{URI}"). <i>Note: If NULL, the URI will be used as label.</i>
+	 * @param uri			URI toward a distant file. <i><b>The scheme of this URI must be different from "param".</b> This scheme is indeed reserved by the DALI syntax to reference a HTTP request parameter containing an inline file.</i>
+	 * @param fileManager	The file manager to use when a stream will be opened toward the given URI. This file manager should know how to access it,
+	 *                   	because the URI can use a URL scheme (http, https, ftp) but also another scheme unknown by the library (e.g. ivo, vos).
+	 */
+	public DALIUpload(final String label, final URI uri, final UWSFileManager fileManager){
+		if (uri == null)
+			throw new NullPointerException("Missing URI! => Can not build a DaliUpload instance.");
+		else if (uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("param"))
+			throw new IllegalArgumentException("Wrong URI scheme: \"param\" is reserved to reference a HTTP request parameter! If used, the content of this parameter must be stored in a file, then the parameter must be represented by an UploadFile and integrated into a DALIUpload with the other constructor.");
+		else if (uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("file"))
+			throw new IllegalArgumentException("Wrong URI scheme: \"file\" is forbidden!");
+		else if (fileManager == null)
+			throw new NullPointerException("Missing File Manager! => Can not build a DaliUpload instance.");
+
+		this.label = (label == null) ? uri.toString() : label;
+		this.uri = uri;
+		this.file = null;
+		this.fileManager = fileManager;
+	}
+
+	/**
+	 * Tell whether this upload is actually a reference toward a distant resource.
+	 * 
+	 * @return	<i>true</i> if this upload is referenced by a URI,
+	 *        	<i>false</i> if the upload has been submitted inline in the HTTP request.
+	 */
+	public boolean isByReference(){
+		return (file == null);
+	}
+
+	/**
+	 * Open a stream to the content of this upload.
+	 * 
+	 * @return	An InputStream.
+	 * 
+	 * @throws UnsupportedURIProtocolException	If the URI of this upload item is using a protocol not supported by this service implementation.
+	 * @throws IOException				If the stream can not be opened.
+	 */
+	public InputStream open() throws UnsupportedURIProtocolException, IOException{
+		if (file == null)
+			return fileManager.openURI(uri);
+		else
+			return file.open();
+	}
+
+	@Override
+	public String toString(){
+		return label + "," + (file != null ? "param:" + file.paramName : uri.toString());
+	}
+
+	/* ****************************** */
+	/* EXTRACTION OF DALI/TAP UPLOADS */
+	/* ****************************** */
+
+	/** <p>Regular expression of an UPLOAD parameter as defined by DALI (REC-DALI-1.0-20131129).</p>
+	 * <p><i>Note:
+	 * 	In DALI, multiple uploads must be done by posting several UPLOAD parameters.
+	 * 	It is not possible to provide directly a list of parameters as in TAP.
+	 * 	However, the advantage of the DALI method is to allow ; in URI (while ; is the
+	 * 	parameter separator in TAP).
+	 * </i></p> */
+	protected static final String DALI_UPLOAD_REGEXP = "[^,]+,\\s*(param:.+|.+)";
+
+	/** <p>Regular expression of an UPLOAD parameter as defined by TAP (REC-TAP-1.0).</p>
+	 * <p><i>Note:
+	 * 	In TAP, multiple uploads may be done by POSTing only one UPLOAD parameter
+	 * 	whose the value is a list of DALI UPLOAD parameters, separated by a ;
+	 * </i></p> */
+	protected static final String TAP_UPLOAD_REGEXP = DALI_UPLOAD_REGEXP + "(\\s*;\\s*" + DALI_UPLOAD_REGEXP + ")*";
+
+	/**
+	 * <p>Get all uploads specified in the DALI parameter "UPLOAD" from the given request parameters.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	This function is case INsensitive for the "UPLOAD" parameter.
+	 * </i></p>
+	 * <p><b>WARNING:</b>
+	 * 	Calling this function modifies the given map ONLY IF the "UPLOAD" parameter (whatever is its case) is found.
+	 * 	In such case, the following modifications are applied:
+	 * </p>
+	 * <ul>
+	 * 	<li>
+	 * 		All "UPLOAD" parameters will be removed and then added again in the map with their corresponding {@link DALIUpload} item (not any more a String).
+	 * 	</li>
+	 * 	<li>
+	 * 		If <i>allowTAPSyntax</i> is <i>true</i>, several uploads may be specified in the same "UPLOAD" parameter value.
+	 * 		For more clarity for the user (once the parameters listed), this list of uploads will be split in the same number of "UPLOAD" parameters.
+	 * 		That's to say, there will be only one "UPLOAD" item in the Map, but its value will be an array containing every specified uploads:
+	 * 		<i>an array of {@link DALIUpload} objects</i>.
+	 * 	</li>
+	 * 	<li>
+	 * 		If there is at least one "UPLOAD" parameter, all uploaded files (parameters associated with instances of {@link UploadFile}) will be removed
+	 * 		from the map (and also from the file system). They are indeed not useful for a DALI service since all interesting uploads have already been
+	 * 		listed.
+	 * 	</li>
+	 * </ul>
+	 * 
+	 * <p><i>Note:
+	 * 	This function can be called several times on the same map. After a first call, this function will just gathers into a List
+	 * 	all found {@link DALIUpload} objects. Of course, only uploads specified in the "UPLOAD" parameter(s) will be returned and others will be removed
+	 * 	as explained above.
+	 * </i></p>
+	 * 
+	 * <h3>DALI and TAP syntax</h3>
+	 * <p>
+	 * 	The "UPLOAD" parameter lists all files to consider as uploaded.
+	 * 	The syntax for one item is the following: "{name},{uri}", where {uri} is "param:{param-ref}" when the file is provided
+	 * 	inline in the parameter named {param-ref}, otherwise, it can be any valid URI (http:..., ftp:..., vos:..., ivo:..., etc...).
+	 * </p>
+	 * 
+	 * <p>
+	 * 	The parameter <i>allowTAPSyntax</i> lets switch between the DALI and TAP syntax.
+	 * 	The only difference between them, is in the way to list multiple uploads. In TAP, they can be given as a semicolon separated
+	 * 	list in a single parameter, whereas in DALI, there must be submitted as several individual parameters. For instance:
+	 * </p>
+	 * <ul>
+	 * 	<li><b>In TAP:</b> "UPLOAD=tableA,param:foo;tableB,http://..." =&gt; only 1 parameter</li>
+	 * 	<li><b>In DALI:</b> "UPLOAD=tableA,param:foo" and "UPLOAD=tableB,http://..." =&gt; 2 parameters</li>
+	 * </ul>
+	 * 
+	 * <p><i>Note:
+	 * 	Because of the possible presence of a semicolon in a URI (which is also used as separator of uploads in the TAP syntax),
+	 * 	there could be a problem while splitting the uploads specified in "UPLOAD". In that case, it is strongly recommended to
+	 * 	either encode the URI (in UTF-8) (i.e. ";" becomes "%3B") or to merely restrict the syntax to the DALI one. In this last case,
+	 * 	the parameter {@link #allowTAPSyntax} should be set to <i>false</i> and then all parameters should be submitted individually.
+	 * </i></p>
+	 * 
+	 * @param requestParams		All parameters extracted from an HTTP request by a {@link RequestParser}.
+	 * @param allowTAPSyntax	<i>true</i> to allow a list of several upload items in one "UPLOAD" parameter value (each item separated by a semicolon),
+	 *                      	<i>false</i> to forbid it (and so, multiple upload items shall be submitted individually).
+	 * @param fileManager		The file manager to use in order to build a {@link DALIUpload} objects from a URI.
+	 *                   		<i>(a link to the file manager will be set in the {@link DALIUpload} object in order to open it
+	 *                   		whenever it will asked after its creation)</i>
+	 * 
+	 * @return	List of all uploads specified with the DALI or TAP syntax.
+	 * 
+	 * @throws TAPException	If the syntax of an "UPLOAD" parameter is wrong.
+	 * 
+	 * @see {@link RequestParser#parse(javax.servlet.http.HttpServletRequest)}
+	 */
+	public final static List<DALIUpload> getDALIUploads(final Map<String,Object> requestParams, final boolean allowTAPSyntax, final UWSFileManager fileManager) throws TAPException{
+
+		// 1. Get all "UPLOAD" parameters and build/get their corresponding DALIUpload(s):
+		ArrayList<DALIUpload> uploads = new ArrayList<DALIUpload>(3);
+		ArrayList<String> usedFiles = new ArrayList<String>(3);
+		Iterator<Map.Entry<String,Object>> it = requestParams.entrySet().iterator();
+		Map.Entry<String,Object> entry;
+		Object value;
+		while(it.hasNext()){
+			entry = it.next();
+
+			// If the parameter is an "UPLOAD" one:
+			if (entry.getKey() != null && entry.getKey().toLowerCase().equals(TAPJob.PARAM_UPLOAD)){
+				// get its value:
+				value = entry.getValue();
+
+				if (value != null){
+					// CASE DALIUpload: just add the upload item inside the list:
+					if (value instanceof DALIUpload){
+						DALIUpload upl = (DALIUpload)value;
+						uploads.add(upl);
+						if (!upl.isByReference())
+							usedFiles.add(upl.file.paramName);
+
+						// CASE String: it must be parsed and transformed into a DALIUpload item which will be then added inside the list:
+					}else if (value instanceof String)
+						fetchDALIUploads(uploads, usedFiles, (String)value, requestParams, allowTAPSyntax, fileManager);
+
+					// CASE Array: 
+					else if (value.getClass().isArray()){
+						Object[] objects = (Object[])value;
+						for(Object o : objects){
+							if (o != null){
+								if (o instanceof DALIUpload)
+									uploads.add((DALIUpload)o);
+								else if (o instanceof String)
+									fetchDALIUploads(uploads, usedFiles, (String)o, requestParams, allowTAPSyntax, fileManager);
+							}
+						}
+					}
+				}
+
+				// remove this "UPLOAD" parameter ; if it was not NULL, it will be added again in the map but as DALIUpload item(s) after this loop:
+				it.remove();
+			}
+		}
+
+		// 2. Remove all other files of the request parameters ONLY IF there was a not-NULL "UPLOAD" parameter:
+		if (uploads.size() > 0){
+			it = requestParams.entrySet().iterator();
+			while(it.hasNext()){
+				entry = it.next();
+				value = entry.getValue();
+				if (value == null)
+					it.remove();
+				else if (value instanceof UploadFile && !usedFiles.contains(entry.getKey())){
+					try{
+						((UploadFile)value).deleteFile();
+					}catch(IOException ioe){}
+					it.remove();
+				}else if (value.getClass().isArray()){
+					Object[] objects = (Object[])value;
+					int cnt = objects.length;
+					for(int i = 0; i < objects.length; i++){
+						if (objects[i] == null){
+							objects[i] = null;
+							cnt--;
+						}else if (objects[i] instanceof UploadFile && !usedFiles.contains(entry.getKey())){
+							try{
+								((UploadFile)objects[i]).deleteFile();
+							}catch(IOException ioe){}
+							objects[i] = null;
+							cnt--;
+						}
+					}
+					if (cnt == 0)
+						it.remove();
+				}
+			}
+		}
+
+		// 3. Re-add a new "UPLOAD" parameter gathering all extracted DALI Uploads:
+		requestParams.put("UPLOAD", uploads.toArray(new DALIUpload[uploads.size()]));
+
+		return uploads;
+	}
+
+	/**
+	 * <p>Fetch all uploads specified in the DALI/TAP "UPLOAD" parameter.
+	 * The fetched {@link DALIUpload}s are added in the given {@link ArrayList}.</p>
+	 * 
+	 * <p><i>Note: A DALI upload can be either a URI or an inline file (specified as "param:{param-ref}").</i></p>
+	 * 
+	 * @param uploads			List of {@link DALIUpload}s. <b>to update</b>.
+	 * @param usedFiles			List of the the names of the referenced file parameters. <b>to update</b>.
+	 * @param uploadParam		Value of the "UPLOAD" parameter.
+	 * @param parameters		List of all extracted parameters (including {@link UploadFile}(s)).
+	 * @param allowTAPSyntax	<i>true</i> to allow a list of several upload items in one "UPLOAD" parameter value (each item separated by a semicolon),
+	 *                      	<i>false</i> to forbid it (and so, multiple upload items shall be submitted individually).
+	 * @param fileManager		The file manager to use in order to build a {@link DALIUpload} objects from a URI.
+	 *                   		<i>(a link to the file manager will be set in the {@link DALIUpload} object in order to open it
+	 *                   		whenever it will asked after its creation)</i>
+	 * 
+	 * @return	The corresponding {@link DALIUpload} objects.
+	 * 
+	 * @throws TAPException	If the syntax of the given "UPLOAD" parameter is incorrect.
+	 */
+	protected static void fetchDALIUploads(final ArrayList<DALIUpload> uploads, final ArrayList<String> usedFiles, String uploadParam, final Map<String,Object> parameters, final boolean allowTAPSyntax, final UWSFileManager fileManager) throws TAPException{
+		if (uploadParam == null || uploadParam.trim().length() <= 0)
+			return;
+
+		// TAP SYNTAX (list of DALI UPLOAD items, separated by a semicolon):
+		if (allowTAPSyntax && uploadParam.matches("([^,]+,.+);([^,]+,.+)")){
+			Pattern p = Pattern.compile("([^,]+,.+);([^,]+,.+)");
+			Matcher m = p.matcher(uploadParam);
+			while(m != null && m.matches()){
+				// Fetch the last UPLOAD item:
+				DALIUpload upl = fetchDALIUpload(m.group(2), parameters, fileManager);
+				uploads.add(upl);
+				if (!upl.isByReference())
+					usedFiles.add(upl.file.paramName);
+
+				// Prepare the fetching of the other DALI parameters:
+				if (m.group(1) != null)
+					m = p.matcher(uploadParam = m.group(1));
+			}
+		}
+
+		// DALI SYNTAX (only one UPLOAD item):
+		if (uploadParam.matches("[^,]+,.+")){
+			// Fetch the single UPLOAD item:
+			DALIUpload upl = fetchDALIUpload(uploadParam, parameters, fileManager);
+			uploads.add(upl);
+			if (!upl.isByReference())
+				usedFiles.add(upl.file.paramName);
+		}
+
+		// /!\ INCORRECT SYNTAX /!\
+		else
+			throw new TAPException("Wrong DALI syntax for the parameter UPLOAD \"" + uploadParam + "\"!", UWSException.BAD_REQUEST);
+	}
+
+	/**
+	 * Fetch the single upload item (a pair with the syntax: "{label},{URI}".
+	 * 
+	 * @param uploadParam	Value of the "UPLOAD" parameter. <i>A single upload item is expected ; that's to say something like "{label},{URI}".</i>
+	 * @param parameters	List of extracted parameters. The fetched LOB must be added as a new parameter in this map. <b>MUST not be NULL</b>
+	 * @param fileManager	The file manager to use in order to build a {@link DALIUpload} objects from a URI.
+	 *                   	<i>(a link to the file manager will be set in the {@link DALIUpload} object in order to open it
+	 *                   	whenever it will asked after its creation)</i>
+	 * 
+	 * @return	The corresponding {@link DALIUpload} object.
+	 * 
+	 * @throws TAPException	If the syntax of the given "UPLOAD" parameter is incorrect.
+	 * 
+	 * @see #parseDALIParam(String)
+	 * @see #buildDALIUpload(String, String, Map, UWSFileManager)
+	 */
+	protected static DALIUpload fetchDALIUpload(final String uploadParam, final Map<String,Object> parameters, final UWSFileManager fileManager) throws TAPException{
+		if (uploadParam.matches("[^,]+,.+")){
+			// Check and extract the pair parts ([0]=label, [1]=URI):
+			String[] parts = parseDALIParam(uploadParam);
+
+			// Build the corresponding DALIUpload:
+			return buildDALIUpload(parts[0], parts[1], parameters, fileManager);
+		}else
+			throw new TAPException("Wrong DALI syntax for the parameter UPLOAD \"" + uploadParam + "\"!", UWSException.BAD_REQUEST);
+	}
+
+	/**
+	 * <p>Extract the two parts (label and URI) of the given DALI parameter, and then, check their syntax.</p>
+	 * 
+	 * <p><i><b>Important note:</b>
+	 * 	It MUST be ensured before calling this function that the given DALI parameter is not NULL
+	 * 	and contains at least one comma (,).
+	 * </i></p>
+	 * 
+	 * <p>
+	 * 	The first comma found in the given string will be the separator of the two parts
+	 * 	of the given DALI parameter: {label},{URI}
+	 * </p>
+	 * 
+	 * <p>
+	 * 	The label part - {label} - must start with one letter and may be followed by a letter,
+	 * 	a digit or an underscore. The corresponding regular expression is: [a-zA-Z][a-zA-Z0-9_]*
+	 * </p>
+	 * 
+	 * <p>
+	 * 	The URI part - {URI} - must start with a scheme, followed by a colon (:) and then by several characters
+	 * 	(no restriction). A scheme must start with one letter and may be followed by a letter,
+	 * 	a digit, a plus (+), a dot (.) or an hyphen/minus (-). The corresponding regular expression is:
+	 * 	[a-zA-Z][a-zA-Z0-9\+\.-]*
+	 * </p>
+	 * 
+	 * @param definition	MUST BE A PAIR label,value
+	 * 
+	 * @return	An array of exactly 2 items: [0]=upload label/name, [1]=an URI.	<i>(note: the special DALI syntax "param:..." is also a valid URI)</i>
+	 * 
+	 * @throws TAPException	If the given upload definition is not following the valid DALI syntax.
+	 */
+	protected static String[] parseDALIParam(final String definition) throws TAPException{
+		// Locate the separator:
+		int sep = definition.indexOf(',');
+		if (sep <= 0)
+			throw new TAPException("A DALI parameter must be a pair whose the items are separated by a colon!", UWSException.INTERNAL_SERVER_ERROR);
+
+		// Extract the two parts: {label},{uri}
+		String[] parts = new String[]{definition.substring(0, sep),definition.substring(sep + 1)};
+
+		// Check the label:
+		if (!parts[0].matches("[a-zA-Z][a-zA-Z0-9_]*"))
+			throw new TAPException("Wrong uploaded item name syntax: \"" + parts[0] + "\"! An uploaded item must have a label with the syntax: [a-zA-Z][a-zA-Z0-9_]*.", UWSException.BAD_REQUEST);
+		// Check the URI:
+		else if (!parts[1].matches("[a-zA-Z][a-zA-Z0-9\\+\\.\\-]*:.+"))
+			throw new TAPException("Bad URI syntax: \"" + parts[1] + "\"! A URI must start with: \"<scheme>:\", where <scheme>=\"[a-zA-Z][a-zA-Z0-9+.-]*\".", UWSException.BAD_REQUEST);
+
+		return parts;
+	}
+
+	/**
+	 * <p>Build a {@link DALIUpload} corresponding to the specified URI.</p>
+	 * 
+	 * <p>
+	 * 	If the URI starts, case-insensitively, with "param:", it is then a reference to another request parameter containing a file content.
+	 * 	In this case, the file content has been already stored inside a local file and represented by an {@link UploadFile} instance in the map.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	If the URI does not start with "param:", the DALI upload is considered as a reference to a distant file which can be accessed using this URI.
+	 * 	Any URI scheme is allowed here, but the given file manager should be able to interpret it and open a stream toward the referenced resource
+	 * 	whenever it will be asked.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the URI is not a parameter reference (i.e. started by "param:"), it will be decoded using {@link URLDecoder#decode(String, String)}
+	 * 	(character encoding: UTF-8).
+	 * </i></p>
+	 * 
+	 * @param label			Label of the {@link DALIUpload} to build.
+	 * @param uri			URI of the LOB. <b>MUST be NOT-NULL</b>
+	 * @param parameters	All parameters extracted from an HTTP request by a {@link RequestParser}.
+	 * @param fileManager	The file manager to use in order to build a {@link DALIUpload} objects from a URI.
+	 *                   	<i>(a link to the file manager will be set in the {@link DALIUpload} object in order to open it
+	 *                   	whenever it will asked after its creation)</i>
+	 * 
+	 * @return	The corresponding {@link DALIUpload} object.
+	 * 
+	 * @throws TAPException	If the parameter reference is broken or if the given URI has a wrong syntax.
+	 */
+	protected final static DALIUpload buildDALIUpload(final String label, String uri, final Map<String,Object> parameters, final UWSFileManager fileManager) throws TAPException{
+		// FILE case:
+		if (uri.toLowerCase().startsWith("param:")){
+
+			// get the specified parameter name:
+			uri = uri.substring(6);
+
+			// get the corresponding file:
+			Object obj = parameters.get(uri);
+
+			/* a map value can be an array of objects in case several parameters have the same name ;
+			 * in this case, we just keep the last instance of UploadFile: */
+			if (obj != null && obj.getClass().isArray()){
+				Object[] objects = (Object[])obj;
+				obj = null;
+				for(Object o : objects){
+					if (o != null && o instanceof UploadFile)
+						obj = o;
+				}
+			}
+
+			// ensure the type of the retrieved parameter is correct:
+			if (obj == null)
+				throw new TAPException("Missing file parameter to upload: \"" + uri + "\"!", UWSException.BAD_REQUEST);
+			else if (!(obj instanceof UploadFile))
+				throw new TAPException("Incorrect parameter type \"" + uri + "\": a file was expected!", UWSException.BAD_REQUEST);
+
+			// build the LOB:
+			return new DALIUpload(label, (UploadFile)obj);
+		}
+
+		// URI case:
+		else{
+			// extract the URI as it is given:
+			uri = uri.trim();
+			if (uri.toLowerCase().startsWith("file:"))
+				throw new TAPException("Wrong URI scheme in the upload specification labeled \"" + label + "\": \"file\" is forbidden!", UWSException.BAD_REQUEST);
+			// decode it in case there is any illegal character:
+			try{
+				uri = URLDecoder.decode(uri, "UTF-8");
+			}catch(UnsupportedEncodingException uee){}
+			try{
+				// build the LOB:
+				return new DALIUpload(label, new URI(uri), fileManager);
+			}catch(URISyntaxException e){
+				throw new TAPException("Incorrect URI syntax: \"" + uri + "\"!", UWSException.BAD_REQUEST);
+			}
+		}
+	}
+
+}
diff --git a/src/tap/parameters/TAPParameters.java b/src/tap/parameters/TAPParameters.java
index 21a2e69010c1ab438acb870788a4c871b9f0ced7..8ab7466093020564add6f92b1bb2cc146bcfb717 100644
--- a/src/tap/parameters/TAPParameters.java
+++ b/src/tap/parameters/TAPParameters.java
@@ -20,171 +20,220 @@ package tap.parameters;
  *                       Astronomisches Rechen Institut (ARI)
  */
 
-import java.io.File;
-import java.io.IOException;
 import java.util.Arrays;
-import java.util.Date;
-import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.servlet.http.HttpServletRequest;
 
 import tap.ServiceConnection;
 import tap.TAPException;
 import tap.TAPJob;
-import tap.upload.TableLoader;
 import uws.UWSException;
 import uws.job.parameters.InputParamController;
 import uws.job.parameters.StringParamController;
 import uws.job.parameters.UWSParameters;
 
-import com.oreilly.servlet.MultipartRequest;
-import com.oreilly.servlet.multipart.FileRenamePolicy;
-
 /**
- * This class describes all defined parameters of a TAP request.
+ * This class lets list and describe all standard TAP parameters
+ * submitted by a TAP client to this TAP service.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (11/2014)
+ * @version 2.0 (12/2014)
  */
 public class TAPParameters extends UWSParameters {
 
 	/** All the TAP parameters. */
 	protected static final List<String> TAP_PARAMETERS = Arrays.asList(new String[]{TAPJob.PARAM_REQUEST,TAPJob.PARAM_LANGUAGE,TAPJob.PARAM_VERSION,TAPJob.PARAM_FORMAT,TAPJob.PARAM_QUERY,TAPJob.PARAM_MAX_REC,TAPJob.PARAM_UPLOAD});
 
-	/** Part of HTTP content type header. */
-	public static final String MULTIPART = "multipart/";
-
-	/** All the tables to upload. If NULL, there is no tables to upload. */
-	protected TableLoader[] tablesToUpload = null;
-
+	/**
+	 * Create an empty list of parameters.
+	 * 
+	 * @param service	Description of the TAP service in which the parameters are created and will be used.
+	 */
 	public TAPParameters(final ServiceConnection service){
-		super(TAP_PARAMETERS, null);
-		initDefaultTAPControllers(service);
+		super(TAP_PARAMETERS, buildDefaultControllers(service));
 	}
 
-	public TAPParameters(final HttpServletRequest request, final ServiceConnection service) throws UWSException, TAPException{
-		this(service);
+	/**
+	 * Create a {@link TAPParameters} instance whose the parameters must be extracted from the given {@link HttpServletRequest}.
+	 * 
+	 * @param request	HTTP request containing the parameters to gather inside this class.
+	 * @param service	Description of the TAP service in which the parameters are created and will be used.
+	 * 
+	 * @throws TAPException	If any error occurs while extracting the DALIParameters OR while setting a parameter.
+	 * 
+	 * @see #getParameters(HttpServletRequest)
+	 */
+	public TAPParameters(final HttpServletRequest request, final ServiceConnection service) throws TAPException{
+		this(service, getParameters(request));
+	}
 
-		MultipartRequest multipart = null;
+	/**
+	 * Create a {@link TAPParameters} instance whose the parameters are given in parameter.
+	 * 
+	 * @param request	HTTP request containing the parameters to gather inside this class.
+	 * @param params	List of parameters to load inside this object.
+	 * 
+	 * @throws TAPException	If any error occurs while extracting the DALIParameters OR while setting a parameter.
+	 */
+	public TAPParameters(final ServiceConnection service, final Map<String,Object> params) throws TAPException{
+		super(TAP_PARAMETERS, buildDefaultControllers(service));
 
-		// Multipart HTTP parameters:
-		if (isMultipartContent(request)){
-			if (!service.uploadEnabled())
-				throw new TAPException("Request error! This TAP service has no Upload capability!", UWSException.BAD_REQUEST);
+		if (params != null && !params.isEmpty()){
+			// Deal with the UPLOAD parameter(s):
+			DALIUpload.getDALIUploads(params, true, service.getFileManager());
 
-			File uploadDir = service.getFileManager().getUploadDirectory();
+			// Load all parameters:			
+			Iterator<Entry<String,Object>> it = params.entrySet().iterator();
+			Entry<String,Object> entry;
 			try{
-				multipart = new MultipartRequest(request, (uploadDir != null) ? uploadDir.getAbsolutePath() : null, service.getMaxUploadSize(), new FileRenamePolicy(){
-					@Override
-					public File rename(File file){
-						return new File(file.getParentFile(), (new Date()).toString() + "_" + file.getName());
-					}
-				});
-				@SuppressWarnings("unchecked")
-				Enumeration<String> e = multipart.getParameterNames();
-				while(e.hasMoreElements()){
-					String param = e.nextElement();
-					set(param, multipart.getParameter(param));
+				while(it.hasNext()){
+					entry = it.next();
+					set(entry.getKey(), entry.getValue());
 				}
-			}catch(IOException ioe){
-				throw new TAPException("Error while reading the Multipart content!", ioe);
-			}catch(IllegalArgumentException iae){
-				String confError = iae.getMessage();
-				if (service.getMaxUploadSize() <= 0)
-					confError = "The maximum upload size (see ServiceConnection.getMaxUploadSize() must be positive!";
-				else if (uploadDir == null)
-					confError = "Missing upload directory (see TAPFileManager.getUploadDirectory())!";
-				throw new TAPException("Incorrect Upload capability configuration! " + confError, iae);
-			}
-
-		}// Classic HTTP parameters (GET or POST):
-		else{
-			// Extract and identify each pair (key,value):
-			Enumeration<String> e = request.getParameterNames();
-			while(e.hasMoreElements()){
-				String name = e.nextElement();
-				set(name, request.getParameter(name));
+			}catch(UWSException ue){
+				throw new TAPException(ue);
 			}
 		}
-
-		// Identify the tables to upload, if any:
-		String uploadParam = getUpload();
-		if (service.uploadEnabled() && uploadParam != null)
-			tablesToUpload = buildLoaders(uploadParam, multipart);
 	}
 
-	public TAPParameters(final ServiceConnection service, final Map<String,Object> params) throws UWSException, TAPException{
-		super(params, TAP_PARAMETERS, null);
-		initDefaultTAPControllers(service);
-	}
-
-	@Override
-	protected final HashMap<String,InputParamController> getDefaultControllers(){
-		return new HashMap<String,InputParamController>(10);
-	}
-
-	protected void initDefaultTAPControllers(final ServiceConnection service){
-		mapParamControllers.put(TAPJob.PARAM_EXECUTION_DURATION, new TAPExecutionDurationController(service));
-		mapParamControllers.put(TAPJob.PARAM_DESTRUCTION_TIME, new TAPDestructionTimeController(service));
-		mapParamControllers.put(TAPJob.PARAM_REQUEST, new StringParamController(TAPJob.PARAM_REQUEST, null, new String[]{TAPJob.REQUEST_DO_QUERY,TAPJob.REQUEST_GET_CAPABILITIES}, true));
-		mapParamControllers.put(TAPJob.PARAM_LANGUAGE, new StringParamController(TAPJob.PARAM_LANGUAGE, TAPJob.LANG_ADQL, null, true));
-		mapParamControllers.put(TAPJob.PARAM_VERSION, new StringParamController(TAPJob.PARAM_VERSION, TAPJob.VERSION_1_0, new String[]{TAPJob.VERSION_1_0}, true));
-		mapParamControllers.put(TAPJob.PARAM_QUERY, new StringParamController(TAPJob.PARAM_QUERY));
-		mapParamControllers.put(TAPJob.PARAM_UPLOAD, new StringParamController(TAPJob.PARAM_UPLOAD));
-		mapParamControllers.put(TAPJob.PARAM_FORMAT, new FormatController(service));
-		mapParamControllers.put(TAPJob.PARAM_MAX_REC, new MaxRecController(service));
+	/**
+	 * <p>Build a map containing all controllers for all standard TAP parameters.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	All standard parameters, except UPLOAD. Indeed, since this parameter can be provided in several times (in one HTTP request)
+	 * 	and needs to be interpreted immediately after initialization, no controller has been set for it. Its value will be actually
+	 * 	tested in the constructor while interpreting it.
+	 * </i></p>
+	 * 
+	 * @param service	Description of the TAP service.
+	 * 
+	 * @return	Map of all default controllers.
+	 * 
+	 * @since 2.0
+	 */
+	protected static final Map<String,InputParamController> buildDefaultControllers(final ServiceConnection service){
+		Map<String,InputParamController> controllers = new HashMap<String,InputParamController>(10);
+		controllers.put(TAPJob.PARAM_EXECUTION_DURATION, new TAPExecutionDurationController(service));
+		controllers.put(TAPJob.PARAM_DESTRUCTION_TIME, new TAPDestructionTimeController(service));
+		controllers.put(TAPJob.PARAM_REQUEST, new StringParamController(TAPJob.PARAM_REQUEST, null, new String[]{TAPJob.REQUEST_DO_QUERY,TAPJob.REQUEST_GET_CAPABILITIES}, true));
+		controllers.put(TAPJob.PARAM_LANGUAGE, new StringParamController(TAPJob.PARAM_LANGUAGE, TAPJob.LANG_ADQL, null, true));
+		controllers.put(TAPJob.PARAM_VERSION, new StringParamController(TAPJob.PARAM_VERSION, TAPJob.VERSION_1_0, new String[]{TAPJob.VERSION_1_0}, true));
+		controllers.put(TAPJob.PARAM_QUERY, new StringParamController(TAPJob.PARAM_QUERY));
+		controllers.put(TAPJob.PARAM_FORMAT, new FormatController(service));
+		controllers.put(TAPJob.PARAM_MAX_REC, new MaxRecController(service));
+		return controllers;
 	}
 
-	@Override
-	public String[] update(UWSParameters newParams) throws UWSException{
-		if (newParams != null && !(newParams instanceof TAPParameters))
-			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Can not update a TAPParameters instance with only a UWSException !");
-
-		String[] updated = super.update(newParams);
-		for(String p : updated){
-			if (p.equals(TAPJob.PARAM_UPLOAD)){
-				tablesToUpload = ((TAPParameters)newParams).tablesToUpload;
-				break;
+	/**
+	 * <p>Get the value of the given parameter, but as a String, whatever is its original type.</p>
+	 * 
+	 * <p>Basically, the different cases of conversion into String are the following:</p>
+	 * <ul>
+	 * 	<li><b>NULL</b>: NULL is returned.</li>
+	 * 	<li><b>An array (of whatever is the items' type)</b>: a string in which each Object.toString() are concatenated ; each item is separated by a semicolon</li>
+	 * 	<li><b>Anything else</b>: Object.toString()</li> 
+	 * </ul>
+	 * 
+	 * @param paramName	Name of the parameter whose the value must be returned as a String.
+	 * 
+	 * @return	The string value of the specified parameter.
+	 */
+	protected final String getStringParam(final String paramName){
+		// Get the parameter value as an Object:
+		Object value = params.get(paramName);
+
+		// Convert this Object into a String:
+		// CASE: NULL
+		if (value == null)
+			return null;
+
+		// CASE: ARRAY
+		else if (value.getClass().isArray()){
+			StringBuffer buf = new StringBuffer();
+			for(Object o : (Object[])value){
+				if (buf.length() > 0)
+					buf.append(';');
+				buf.append(o.toString());
 			}
+			return buf.toString();
 		}
-		return updated;
-	}
-
-	protected final String getStringParam(final String paramName){
-		return (params.get(paramName) != null) ? params.get(paramName).toString() : null;
+		// DEFAULT:
+		else
+			return value.toString();
 	}
 
+	/**
+	 * Get the value of the standard TAP parameter "REQUEST".
+	 * @return	"REQUEST" value.
+	 */
 	public final String getRequest(){
 		return getStringParam(TAPJob.PARAM_REQUEST);
 	}
 
+	/**
+	 * Get the value of the standard TAP parameter "LANG".
+	 * @return	"LANG" value.
+	 */
 	public final String getLang(){
 		return getStringParam(TAPJob.PARAM_LANGUAGE);
 	}
 
+	/**
+	 * Get the value of the standard TAP parameter "VERSION".
+	 * @return	"VERSION" value.
+	 */
 	public final String getVersion(){
 		return getStringParam(TAPJob.PARAM_VERSION);
 	}
 
+	/**
+	 * Get the value of the standard TAP parameter "FORMAT".
+	 * @return	"FORMAT" value.
+	 */
 	public final String getFormat(){
 		return getStringParam(TAPJob.PARAM_FORMAT);
 	}
 
+	/**
+	 * Get the value of the standard TAP parameter "QUERY".
+	 * @return	"QUERY" value.
+	 */
 	public final String getQuery(){
 		return getStringParam(TAPJob.PARAM_QUERY);
 	}
 
+	/**
+	 * <p>Get the value of the standard TAP parameter "UPLOAD".</p>
+	 * <p><i>Note:
+	 * 	This parameter is generally a set of several Strings, each representing one table to upload.
+	 * 	This function returns this set as a String in which each items are joined, semicolon separated, inside a single String.
+	 * <i></p>
+	 * @return	"UPLOAD" value.
+	 */
 	public final String getUpload(){
 		return getStringParam(TAPJob.PARAM_UPLOAD);
 	}
 
-	public final TableLoader[] getTableLoaders(){
-		return tablesToUpload;
+	/**
+	 * Get the list of all tables uploaded and defined by the standard TAP parameter "UPLOAD". 
+	 * 
+	 * @return	Tables to upload in database at query execution.
+	 */
+	public final DALIUpload[] getUploadedTables(){
+		return (DALIUpload[])get(TAPJob.PARAM_UPLOAD);
 	}
 
+	/**
+	 * Get the value of the standard TAP parameter "MAX_REC".
+	 * This value is the maximum number of rows that the result of the query must contain.
+	 * 
+	 * @return	Maximum number of output rows.
+	 */
 	public final Integer getMaxRec(){
 		Object value = params.get(TAPJob.PARAM_MAX_REC);
 		if (value != null){
@@ -206,55 +255,15 @@ public class TAPParameters extends UWSParameters {
 	}
 
 	/**
-	 * Utility method that determines whether the request contains multipart
-	 * content.
-	 *
-	 * @param request The servlet request to be evaluated. Must be non-null.
-	 *
-	 * @return <code>true</code> if the request is multipart;
-	 *         <code>false</code> otherwise.
-	 */
-	public static final boolean isMultipartContent(HttpServletRequest request){
-		if (!"post".equals(request.getMethod().toLowerCase())){
-			return false;
-		}
-		String contentType = request.getContentType();
-		if (contentType == null){
-			return false;
-		}
-		if (contentType.toLowerCase().startsWith(MULTIPART)){
-			return true;
-		}
-		return false;
-	}
-
-	/**
-	 * Builds as many TableLoader instances as tables to upload.
+	 * <p>Check the coherence between all TAP parameters.</p>
 	 * 
-	 * @param upload	The upload field (syntax: "tableName1,URI1 ; tableName2,URI2 ; ...", where URI may start by "param:" to indicate that the VOTable is inline).
-	 * @param multipart	The multipart content of the request if any.
+	 * <p>
+	 * 	This function does not test individually each parameters, but all of them as a coherent whole.
+	 * 	Thus, the parameter REQUEST must be provided and if its value is "doQuery", the parameters LANG and QUERY must be also provided.
+	 * </p>
 	 * 
-	 * @return			All table loaders (one per table to upload).
-	 * 
-	 * @throws TAPException	If the syntax of the "upload" field is incorrect.
+	 * @throws TAPException	If one required parameter is missing.
 	 */
-	private TableLoader[] buildLoaders(final String upload, final MultipartRequest multipart) throws TAPException{
-		if (upload == null || upload.trim().isEmpty())
-			return new TableLoader[0];
-
-		String[] pairs = upload.split(";");
-		TableLoader[] loaders = new TableLoader[pairs.length];
-
-		for(int i = 0; i < pairs.length; i++){
-			String[] table = pairs[i].split(",");
-			if (table.length != 2)
-				throw new TAPException("UPLOAD parameter incorrect: bad syntax! An UPLOAD parameter must contain a list of pairs separated by a ';'. Each pair is composed of 2 parts, a table name and a URI separated by a ','.", UWSException.BAD_REQUEST);
-			loaders[i] = new TableLoader(table[0], table[1], multipart);
-		}
-
-		return loaders;
-	}
-
 	public void check() throws TAPException{
 		// Check that required parameters are not NON-NULL:
 		String requestParam = getRequest();
@@ -267,32 +276,5 @@ public class TAPParameters extends UWSParameters {
 			else if (get(TAPJob.PARAM_QUERY) == null)
 				throw new TAPException("The parameter \"" + TAPJob.PARAM_QUERY + "\" must be provided if " + TAPJob.PARAM_REQUEST + "=" + TAPJob.REQUEST_DO_QUERY + " !", UWSException.BAD_REQUEST);
 		}
-
-		// Check the version if needed:
-		/*Object versionParam = get(TAPJob.PARAM_VERSION);
-		if (versionParam != null && !versionParam.equals("1") && !versionParam.equals("1.0"))
-			throw new TAPException("Version \""+versionParam+"\" of TAP not implemented !");*/
-
-		/*// Check format if needed:
-		if (format == null)
-			format = FORMAT_VOTABLE;
-
-		// Check maxrec:
-		if (maxrec <= -1)
-			maxrec = defaultOutputLimit;
-
-		if (maxOutputLimit > -1){
-			if (maxrec > maxOutputLimit)
-				maxrec = maxOutputLimit;
-			else if (maxrec <= -1)
-				maxrec = maxOutputLimit;
-		}*/
-	}
-
-	public static final void deleteUploadedTables(final TableLoader[] loaders){
-		if (loaders != null){
-			for(TableLoader loader : loaders)
-				loader.deleteFile();
-		}
 	}
 }
diff --git a/src/tap/upload/TableLoader.java b/src/tap/upload/TableLoader.java
deleted file mode 100644
index ee3d62159128321c802afe59c558449f46bca6aa..0000000000000000000000000000000000000000
--- a/src/tap/upload/TableLoader.java
+++ /dev/null
@@ -1,203 +0,0 @@
-package tap.upload;
-
-/*
- * This file is part of TAPLibrary.
- * 
- * TAPLibrary is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * TAPLibrary is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public License
- * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
- * 
- * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomisches Rechen Institut (ARI)
- */
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.security.InvalidParameterException;
-import java.util.Enumeration;
-
-import tap.TAPException;
-import tap.parameters.TAPParameters;
-
-import com.oreilly.servlet.MultipartRequest;
-
-/**
- * <p>Represent an uploaded table in a {@link TAPParameters} object.</p>
- * 
- * <p>
- * 	This class is very useful to interpret the "upload" parameter and to get the ADQL name of the table
- * 	and particularly to get easily an input stream on its data. Thus, it is able to open a stream on table data
- * 	provided as a URL or inline (inside a multipart HTTP request). 
- * </p>
- * 
- * <p>The syntax for the "upload" parameter is the following:</p>
- * <ul>
- * 	<li><b>Case tables provided as URL:</b> table_a,http://host_a/path;table_b,http://host_b/path;...</li>
- * 	<li><b>Case tables provided inline:</b> table_c,param:table1;...
- * 		and "table1" is the name of the parameter (a multipart item = a file) containing the table data.
- * 	</li>
- * </ul>
- * 
- * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (08/2014)
- */
-public class TableLoader {
-	/** Regular expression of any acceptable URL for a table data source. */
-	private static final String URL_REGEXP = "^(https?|ftp)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
-	/** Prefix of a multipart item name (when tables are included inline in a multipart HTTP request). */
-	private static final String PARAM_PREFIX = "param:";
-
-	/** Name of the uploaded table. This name is the one used in the ADQL query. */
-	public final String tableName;
-
-	/** URL at which the table data are.
-	 * <i>Note: This attribute is NULL if the table is provided inline.</i> */
-	private final URL url;
-
-	/** Name of the multipart HTTP request parameter (a multipart item = a file) containing the table data.
-	 * <i>Note: This attribute is NULL if the table is provided as a URL.</i> */
-	private final String param;
-	/** File containing the table data. It points toward the multipart item/parameter whose the name matches the attribute {@link #param}.
-	 * <i>Note: This attribute is NULL if the table is provided as a URL.</i> */
-	private final File file;
-
-	/**
-	 * <p>Build the object representation of an item of the UPLOAD parameter: a table.</p>
-	 * 
-	 * <p>
-	 * 	<b>This table MUST be provided as a URL!</b> Otherwise, a multipart request MUST be provided and in this case
-	 * 	the other constructor ({@link #TableLoader(String, String, MultipartRequest)}) MUST be used.
-	 * </p>
-	 * 
-	 * @param name	ADQL name of the table. <i>It is the key of an item inside the UPLOAD parameter value.</i>
-	 * @param url	URL at which the table data can be found.
-	 * 
-	 * @throws TAPException	If the given URI is malformed,
-	 *                     	or if no multipart request is provided whereas the table is provided as a request parameter,
-	 *                     	or if the name or value is missing.
-	 * 
-	 * @see #TableLoader(String, String, MultipartRequest)
-	 */
-	public TableLoader(final String name, final String url) throws TAPException{
-		this(name, url, (MultipartRequest)null);
-	}
-
-	/**
-	 * <p>Build the object representation of an item of the UPLOAD parameter: a table.</p>
-	 * 
-	 * <p>This table can be provided either as a URL or inline ; the generated instance of {@link TableLoader} is able to deal with both.</p>
-	 * 
-	 * <p><i>Note:
-	 * 	The search of a parameter inside the multipart request is done case sensitively.
-	 * </i></p>
-	 * 
-	 * @param name		ADQL name of the table. <i>It is the key of an item inside the UPLOAD parameter value.</i>
-	 * @param value		URL or "param:"+paramName (where paramName is the name of the multipart request parameter containing the table data).
-	 *             		<i>It is the value of an item inside the UPLAOD parameter value.</i>
-	 * @param multipart	Request containing all parameters provided in multipart. <i>It MAY be NULL if the given "value" is an URL. Otherwise, it MUST NOT be NULL.</i>
-	 * 
-	 * @throws TAPException	If the given URI is malformed,
-	 *                     	or if no multipart request is provided whereas the table is provided as a request parameter,
-	 *                     	or if the name or value is missing.
-	 */
-	@SuppressWarnings("unchecked")
-	public TableLoader(final String name, final String value, final MultipartRequest multipart) throws TAPException{
-		// Get the ADQL table name:
-		if (name == null || name.trim().isEmpty())
-			throw new TAPException("UPLOAD parameter incorrect: missing table name!");
-		else
-			tableName = name.trim();
-
-		// Get the item value (either URL or parameter name):
-		if (value == null || value.trim().isEmpty())
-			throw new NullPointerException("UPLOAD parameter incorrect: missing table URI!");
-		String tableId = value.trim();
-
-		// CASE MULTIPART PARAMETER:
-		if (tableId.startsWith(PARAM_PREFIX)){
-			// Ensure the multipart request is provided and the parameter name is correct:
-			if (multipart == null)
-				throw new TAPException("UPLOAD parameter incorrect: incorrect table URI: \"" + tableId + "\"! The URI scheme \"" + PARAM_PREFIX + "\" can be used ONLY IF the VOTable is provided inside the HTTP request (multipart/form-data)!");
-			else if (tableId.length() <= PARAM_PREFIX.length())
-				throw new TAPException("UPLOAD parameter incorrect: missing parameter name in \"" + tableId + "\"!");
-
-			// Set the parameter name:
-			url = null;
-			param = tableId.substring(PARAM_PREFIX.length()).trim();
-
-			// Get the corresponding file in the multipart request (search case sensitive):
-			Enumeration<String> enumeration = multipart.getFileNames();
-			File foundFile = null;
-			while(foundFile == null && enumeration.hasMoreElements()){
-				String fileName = enumeration.nextElement();
-				if (fileName.equals(param))
-					foundFile = multipart.getFile(fileName);
-			}
-
-			// Set the file:
-			if (foundFile == null)
-				throw new TAPException("UPLOAD parameter incorrect: parameter not found: \"" + tableId + "\"!");
-			else
-				file = foundFile;
-		}
-		// CASE URL:
-		else if (tableId.matches(URL_REGEXP)){
-			try{
-				url = new URL(tableId);
-				param = null;
-				file = null;
-			}catch(MalformedURLException mue){
-				throw new InvalidParameterException(mue.getMessage());
-			}
-		}
-		// OTHER:
-		else
-			throw new TAPException("UPLOAD parameter incorrect: invalid table URI: \"" + tableId + "\"!");
-	}
-
-	/**
-	 * Open a stream toward the table data (whatever is their source, a URL or a file).
-	 * 
-	 * @return	Input over the table data.
-	 * 
-	 * @throws IOException	If any error occurs while open the stream.
-	 */
-	public InputStream openStream() throws IOException{
-		if (url != null)
-			return url.openStream();
-		else
-			return new FileInputStream(file);
-	}
-
-	/**
-	 * <p>Delete the table data stored in the cache.</p>
-	 * 
-	 * <p>
-	 * 	This function will just delete the file in case the table data are coming from a multipart request.
-	 * 	If the table data are provided as a URL, nothing is done (so we can consider that the cache does not contain any more the associated table data).
-	 * </p>
-	 * 
-	 * @return	<i>true</i> if the file does not exist any more in the cache,
-	 *        	<i>false</i> otherwise.
-	 */
-	public boolean deleteFile(){
-		if (file != null && file.exists())
-			return file.delete();
-		else
-			return true;
-	}
-
-}
diff --git a/src/tap/upload/Uploader.java b/src/tap/upload/Uploader.java
index 36e7b9e80b088e3c96306c9767ce020a26008109..d1e8643ff22935bd63ed1fad184fdebcdb7a9b19 100644
--- a/src/tap/upload/Uploader.java
+++ b/src/tap/upload/Uploader.java
@@ -36,7 +36,9 @@ import tap.metadata.TAPMetadata;
 import tap.metadata.TAPMetadata.STDSchema;
 import tap.metadata.TAPSchema;
 import tap.metadata.TAPTable;
+import tap.parameters.DALIUpload;
 import uws.UWSException;
+import uws.service.file.UnsupportedURIProtocolException;
 
 import com.oreilly.servlet.multipart.ExceededSizeException;
 
@@ -49,7 +51,7 @@ import com.oreilly.servlet.multipart.ExceededSizeException;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (09/2014)
+ * @version 2.0 (11/2014)
  * 
  * @see LimitedTableIterator
  * @see VOTableIterator
@@ -137,7 +139,7 @@ public class Uploader {
 	 * 	is created, will be associated with the uploaded tables and will be returned by this function.
 	 * </i></p>
 	 * 
-	 * @param loaders	Array of tables to upload.
+	 * @param uploads	Array of tables to upload.
 	 * 
 	 * @return	A {@link TAPSchema} containing the list and the description of all uploaded tables.
 	 * 
@@ -145,16 +147,16 @@ public class Uploader {
 	 * 
 	 * @see DBConnection#addUploadedTable(TAPTable, tap.data.TableIterator)
 	 */
-	public TAPSchema upload(final TableLoader[] loaders) throws TAPException{
+	public TAPSchema upload(final DALIUpload[] uploads) throws TAPException{
 		InputStream votable = null;
 		String tableName = null;
 		try{
 			// Iterate over the full list of uploaded tables:
-			for(TableLoader loader : loaders){
-				tableName = loader.tableName;
+			for(DALIUpload upl : uploads){
+				tableName = upl.label;
 
 				// Open a stream toward the VOTable:
-				votable = loader.openStream();
+				votable = upl.open();
 
 				// Start reading the VOTable (with the identified limit, if any):
 				TableIterator dataIt = new LimitedTableIterator(VOTableIterator.class, votable, limitUnit, limit);
@@ -183,6 +185,8 @@ public class Uploader {
 				throw new TAPException("Error while reading the VOTable \"" + tableName + "\": " + dre.getMessage(), dre, UWSException.BAD_REQUEST);
 		}catch(IOException ioe){
 			throw new TAPException("IO error while reading the VOTable of \"" + tableName + "\"!", ioe);
+		}catch(UnsupportedURIProtocolException e){
+			throw new TAPException("URI error while trying to open the VOTable of \"" + tableName + "\"!", e);
 		}finally{
 			try{
 				if (votable != null)
diff --git a/src/uws/UWSToolBox.java b/src/uws/UWSToolBox.java
index f6dc4f55373acd251c029d468050ac11427129d8..9e48842a6e93bc49d07dc00037a68a037e7cc627 100644
--- a/src/uws/UWSToolBox.java
+++ b/src/uws/UWSToolBox.java
@@ -16,7 +16,8 @@ package uws;
  * You should have received a copy of the GNU Lesser General Public License
  * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.io.File;
@@ -24,33 +25,34 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-
+import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URL;
-
+import java.net.URLDecoder;
+import java.net.URLEncoder;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
 
 import javax.servlet.ServletOutputStream;
-
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import uws.job.ErrorSummary;
 import uws.job.UWSJob;
-
+import uws.service.UWS;
 import uws.service.UWSUrl;
-
 import uws.service.log.DefaultUWSLog;
 import uws.service.log.UWSLog;
+import uws.service.request.RequestParser;
+import uws.service.request.UploadFile;
 
 /**
  * Some useful functions for the managing of a UWS service.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 05/2012
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 4.1 (12/2014)
  */
 public class UWSToolBox {
 
@@ -111,9 +113,15 @@ public class UWSToolBox {
 	/**
 	 * <p>Builds a map of strings with all parameters of the given HTTP request.</p>
 	 * 
-	 * <p><i>NOTE:
-	 * 		it converts the Map&lt;String, <b>String[]</b>&gt; returned by {@link HttpServletRequest#getParameterMap()}
-	 * 		into a Map&lt;String, <b>String</b>&gt; (the key is put in lower case).
+	 * <p><i>Note:
+	 * 	If the request attribute {@value UWS#REQ_ATTRIBUTE_PARAMETERS} has been already set by the UWS library,
+	 * 	this map (after conversion into a Map<String,String>) is returned.
+	 * 	Otherwise, the parameters identified automatically by the Servlet are returned (just the last occurrence of each parameter is kept).
+	 * </i></p>
+	 * 
+	 * <p><i><b>WARNING:</b>
+	 * 	This function does not extract directly the parameters from the request content. It is just returning those already extracted
+	 * 	either by the Servlet or by a {@link RequestParser}.
 	 * </i></p>
 	 * 
 	 * @param req	The HTTP request which contains the parameters to extract.
@@ -121,25 +129,58 @@ public class UWSToolBox {
 	 * @return		The corresponding map of string.
 	 */
 	@SuppressWarnings("unchecked")
-	public static final HashMap<String,String> getParamsMap(HttpServletRequest req){
-		HashMap<String,String> params = new HashMap<String,String>(req.getParameterMap().size());
+	public static final HashMap<String,String> getParamsMap(final HttpServletRequest req){
+		HashMap<String,String> map = new HashMap<String,String>();
+
+		/* If the attribute "PARAMETERS" has been already set by the UWS library,
+		 * return it by casting it from Map<String,Object> into Map<String,String>: */
+		if (req.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS) != null){
+			try{
+				// Get the extracted parameters:
+				Map<String,Object> params = (Map<String,Object>)req.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS);
+
+				// Transform the map of Objects into a map of Strings:
+				for(Map.Entry<String,Object> e : params.entrySet()){
+					if (e.getValue() != null)
+						map.put(e.getKey(), e.getValue().toString());
+				}
 
-		Enumeration<String> e = req.getParameterNames();
-		while(e.hasMoreElements()){
-			String name = e.nextElement();
-			params.put(name.toLowerCase(), req.getParameter(name));
+				// Return the fetched map:
+				return map;
+
+			}catch(Exception ex){
+				map.clear();
+			}
 		}
 
-		return params;
+		/* If there is no "PARAMETERS" attribute or if an error occurs while reading it,
+		 * return all the parameters fetched by the Servlet: */
+		Enumeration<String> names = req.getParameterNames();
+		int i;
+		String n;
+		String[] values;
+		while(names.hasMoreElements()){
+			n = names.nextElement();
+			values = req.getParameterValues(n);
+			// search for the last non-null occurrence:
+			i = values.length - 1;
+			while(i >= 0 && values[i] == null)
+				i--;
+			// if there is one, keep it:
+			if (i >= 0)
+				map.put(n.toLowerCase(), values[i]);
+		}
+		return map;
 	}
 
 	/**
 	 * Converts map of UWS parameters into a string corresponding to the query part of a HTTP-GET URL (i.e. ?EXECUTIONDURATION=60&DESTRUCTION=2010-09-01T13:58:00:000-0200).
 	 * 
 	 * @param parameters	A Map of parameters.
+	 * 
 	 * @return				The corresponding query part of an HTTP-GET URL (all keys have been set in upper case).
 	 */
-	public final static String getQueryPart(Map<String,String> parameters){
+	public final static String getQueryPart(final Map<String,String> parameters){
 		if (parameters == null || parameters.isEmpty())
 			return "";
 
@@ -154,8 +195,10 @@ public class UWSToolBox {
 				val = val.trim();
 
 			if (key != null && !key.isEmpty() && val != null && !val.isEmpty()){
-				queryPart.append(e.getKey() + "=" + val);
-				queryPart.append("&");
+				try{
+					queryPart.append(URLEncoder.encode(e.getKey(), "UTF-8") + "=" + URLEncoder.encode(val, "UTF-8"));
+					queryPart.append("&");
+				}catch(UnsupportedEncodingException uee){}
 			}
 		}
 
@@ -166,6 +209,7 @@ public class UWSToolBox {
 	 * Converts the given query part of a HTTP-GET URL to a map of parameters.
 	 * 
 	 * @param queryPart		A query part of a HTTP-GET URL.
+	 * 
 	 * @return				The corresponding map of parameters (all keys have been set in lower case).
 	 */
 	public final static Map<String,String> getParameters(String queryPart){
@@ -180,8 +224,11 @@ public class UWSToolBox {
 					if (keyValue.length == 2){
 						keyValue[0] = keyValue[0].trim().toLowerCase();
 						keyValue[1] = keyValue[1].trim();
-						if (!keyValue[0].isEmpty() && !keyValue[1].isEmpty())
-							parameters.put(keyValue[0].trim(), keyValue[1].trim());
+						if (!keyValue[0].isEmpty() && !keyValue[1].isEmpty()){
+							try{
+								parameters.put(URLDecoder.decode(keyValue[0], "UTF-8"), URLDecoder.decode(keyValue[1], "UTF-8"));
+							}catch(UnsupportedEncodingException uee){}
+						}
 					}
 				}
 			}
@@ -190,6 +237,163 @@ public class UWSToolBox {
 		return parameters;
 	}
 
+	/**
+	 * <p>Extract only the GET parameters from the given HTTP request and add them inside the given map.</p>
+	 * 
+	 * <p><b>Warning</b>:
+	 * 	If entries with the same key already exist in the map, they will overwritten.
+	 * </p>
+	 * 
+	 * @param req			The HTTP request whose the GET parameters must be extracted.
+	 * @param parameters	List of parameters to update.
+	 * 
+	 * @return	The same given parameters map (but updated with all found GET parameters).
+	 * 
+	 * @since 4.1
+	 */
+	public static final Map<String,Object> addGETParameters(final HttpServletRequest req, final Map<String,Object> parameters){
+		String queryString = req.getQueryString();
+		if (queryString != null){
+			String[] params = queryString.split("&");
+			int indSep;
+			for(String p : params){
+				indSep = p.indexOf('=');
+				if (indSep >= 0){
+					try{
+						parameters.put(URLDecoder.decode(p.substring(0, indSep), "UTF-8"), URLDecoder.decode(p.substring(indSep + 1), "UTF-8"));
+					}catch(UnsupportedEncodingException uee){}
+				}
+			}
+		}
+		return parameters;
+	}
+
+	/**
+	 * Get the number of parameters submitted in the given HTTP request.
+	 * 
+	 * @param request	An HTTP request;
+	 * 
+	 * @return	The number of submitted parameters.
+	 * 
+	 * @since 4.1
+	 */
+	@SuppressWarnings("unchecked")
+	public static final int getNbParameters(final HttpServletRequest request){
+		if (request == null)
+			return 0;
+		try{
+			return ((Map<String,Object>)request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS)).size();
+		}catch(Exception ex){
+			return request.getParameterMap().size();
+		}
+	}
+
+	/**
+	 * Check whether a parameter has been submitted with the given name.
+	 * 
+	 * @param name				Name of the parameter to search. <b>The case is important!</b>
+	 * @param request			HTTP request in which the specified parameter must be searched.
+	 * @param caseSensitive		<i>true</i> to perform the research case-sensitively,
+	 *                     		<i>false</i> for a case INsensitive research.
+	 * 
+	 * @return	<i>true</i> if the specified parameter has been found, <i>false</i> otherwise.
+	 * 
+	 * @since 4.1
+	 */
+	public static final boolean hasParameter(final String name, final HttpServletRequest request, final boolean caseSensitive){
+		return getParameter(name, request, caseSensitive) != null;
+	}
+
+	/**
+	 * Check whether the parameter specified with the given pair (name,value) exists in the given HTTP request. 
+	 * 
+	 * @param name				Name of the parameter to search.
+	 * @param value				Expected value of the parameter.
+	 * @param request			HTTP request in which the given pair must be searched.
+	 * @param caseSensitive		<i>true</i> to perform the research (on name AND value) case-sensitively,
+	 *                     		<i>false</i> for a case INsensitive research.
+	 * 
+	 * @return	<i>true</i> if the specified parameter has been found with the given value in the given HTTP request,
+	 *        	<i>false</i> otherwise.
+	 * 
+	 * @since 4.1
+	 */
+	public static final boolean hasParameter(final String name, final String value, final HttpServletRequest request, final boolean caseSensitive){
+		Object found = getParameter(name, request, caseSensitive);
+		if (value == null)
+			return found != null;
+		else{
+			if (found == null || !(found instanceof String))
+				return false;
+			else
+				return (caseSensitive && ((String)found).equals(value)) || (!caseSensitive && ((String)found).equalsIgnoreCase(value));
+		}
+	}
+
+	/**
+	 * Get the parameter specified by the given name from the given HTTP request. 
+	 * 
+	 * @param name				Name of the parameter to search.
+	 * @param request			HTTP request in which the given pair must be searched.
+	 * @param caseSensitive		<i>true</i> to perform the research case-sensitively,
+	 *                     		<i>false</i> for a case INsensitive research.
+	 * 
+	 * @return	Value of the parameter.
+	 * 
+	 * @since 4.1
+	 */
+	@SuppressWarnings("unchecked")
+	public static final Object getParameter(final String name, final HttpServletRequest request, final boolean caseSensitive){
+		try{
+			// Get the extracted parameters:
+			Map<String,Object> params = (Map<String,Object>)request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS);
+
+			// Search case IN-sensitively the given pair (name, value):
+			for(Map.Entry<String,Object> e : params.entrySet()){
+				if ((!caseSensitive && e.getKey().equalsIgnoreCase(name)) || (caseSensitive && e.getKey().equals(name)))
+					return (e.getValue() != null) ? e.getValue() : null;
+			}
+		}catch(Exception ex){}
+		return null;
+	}
+
+	/**
+	 * <p>Delete all unused uploaded files of the given request.</p>
+	 * 
+	 * <p>
+	 * 	These files have been stored on the file system
+	 * 	if there is a request attribute named {@value UWS#REQ_ATTRIBUTE_PARAMETERS}.
+	 * </p>
+	 * 
+	 * @param req	Request in which files have been uploaded.
+	 * 
+	 * @return	The number of deleted files.
+	 * 
+	 * @see UploadFile#isUsed()
+	 * 
+	 * @since 4.1
+	 */
+	@SuppressWarnings("unchecked")
+	public static final int deleteUploads(final HttpServletRequest req){
+		int cnt = 0;
+		Object attribute = req.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS);
+		// If there is the request attribute "UWS_PARAMETERS":
+		if (attribute != null && attribute instanceof Map){
+			Map<String,Object> params = (Map<String,Object>)attribute;
+			// For each parameter...
+			for(Map.Entry<String,Object> e : params.entrySet()){
+				// ...delete physically the uploaded file ONLY IF not used AND IF it is an uploaded file:
+				if (e.getValue() != null && e.getValue() instanceof UploadFile && !((UploadFile)e.getValue()).isUsed()){
+					try{
+						((UploadFile)e.getValue()).deleteFile();
+						cnt++;
+					}catch(IOException ioe){}
+				}
+			}
+		}
+		return cnt;
+	}
+
 	/* **************************** */
 	/* DIRECTORY MANAGEMENT METHODS */
 	/* **************************** */
@@ -246,7 +450,7 @@ public class UWSToolBox {
 			byte[] buffer = new byte[1024];
 			int length;
 			while((length = input.read(buffer)) > 0)
-				output.print(new String(buffer, 0, length));
+				output.write(buffer, 0, length);
 		}finally{
 			if (output != null)
 				output.flush();
diff --git a/src/uws/job/JobList.java b/src/uws/job/JobList.java
index dfd4802596414bdb568a20f248213d065caade89..fcab5d3461383aec9eeda36115c77f81ca85639d 100644
--- a/src/uws/job/JobList.java
+++ b/src/uws/job/JobList.java
@@ -97,7 +97,7 @@ import uws.service.log.UWSLog.LogLevel;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (11/2014)
  * 
  * @see UWSJob
  */
@@ -135,7 +135,7 @@ public class JobList extends SerializableUWSObject implements Iterable<UWSJob> {
 	 * @see #JobList(String, ExecutionManager)
 	 */
 	public JobList(String jobListName) throws NullPointerException{
-		this(jobListName, new DefaultExecutionManager(), new DefaultDestructionManager());
+		this(jobListName, null, new DefaultDestructionManager());
 	}
 
 	/**
@@ -525,7 +525,9 @@ public class JobList extends SerializableUWSObject implements Iterable<UWSJob> {
 	 * @see UWSJob#applyPhaseParam(JobOwner)
 	 */
 	public synchronized String addNewJob(final UWSJob j) throws UWSException{
-		if (j == null || jobsList.containsKey(j.getJobId())){
+		if (uws == null)
+			throw new IllegalStateException("Jobs can not be added to this job list until this job list is linked to a UWS!");
+		else if (j == null || jobsList.containsKey(j.getJobId())){
 			return null;
 		}else{
 			JobOwner owner = j.getOwner();
@@ -592,13 +594,6 @@ public class JobList extends SerializableUWSObject implements Iterable<UWSJob> {
 		UWSJob removedJob = (jobId == null) ? null : jobsList.remove(jobId);
 
 		if (removedJob != null){
-			// Delete completely their association:
-			/*try{
-				removedJob.setJobList(null);
-			}catch(IllegalStateException ue){
-				getLogger().error("Impossible to set the job list of the removed job to NULL !", ue);
-			}*/
-
 			// Clear its owner index:
 			JobOwner owner = removedJob.getOwner();
 			if (owner != null && ownerJobs.containsKey(owner)){
diff --git a/src/uws/job/UWSJob.java b/src/uws/job/UWSJob.java
index 735558d0fc6d8f9b151c276a054f3960997cf158..f0c5cf048396b49ca71eefce301801380e850ff8 100644
--- a/src/uws/job/UWSJob.java
+++ b/src/uws/job/UWSJob.java
@@ -47,6 +47,7 @@ import uws.service.UWSUrl;
 import uws.service.file.UWSFileManager;
 import uws.service.log.UWSLog;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.UploadFile;
 
 /**
  * <h3>Brief description</h3>
@@ -119,7 +120,7 @@ import uws.service.log.UWSLog.LogLevel;
  * </ul>
  * 
  * @author	Gr&eacute;gory Mantelet (CDS;ARI)
- * @version	4.1 (10/2014)
+ * @version	4.1 (12/2014)
  */
 public class UWSJob extends SerializableUWSObject {
 	private static final long serialVersionUID = 1L;
@@ -307,6 +308,14 @@ public class UWSJob extends SerializableUWSObject {
 
 		jobId = generateJobId();
 		restorationDate = null;
+
+		// Move all uploaded files in a location related with this job:
+		Iterator<UploadFile> files = inputParams.getFiles();
+		while(files.hasNext()){
+			try{
+				files.next().move(this);
+			}catch(IOException ioe){}
+		}
 	}
 
 	/**
@@ -582,7 +591,8 @@ public class UWSJob extends SerializableUWSObject {
 			ExecutionPhase oldPhase = phase.getPhase();
 			phase.setPhase(p, force);
 
-			getLogger().logJob(LogLevel.INFO, this, "CHANGE_PHASE", "The job \"" + getJobId() + "\" goes from " + oldPhase + " to " + p, null);
+			if (!force)
+				getLogger().logJob(LogLevel.INFO, this, "CHANGE_PHASE", "The job \"" + getJobId() + "\" goes from " + oldPhase + " to " + p, null);
 
 			// Notify the execution manager:
 			if (phase.isFinished() && getJobList() != null)
@@ -709,13 +719,13 @@ public class UWSJob extends SerializableUWSObject {
 	 * 	If known the jobs list is notify of this destruction time update.
 	 * </p>
 	 * 
-	 * @param destructionTime The destruction time of this job.
+	 * @param destructionTime The destruction time of this job. <i>MUST NOT be NULL</i>
 	 * 
 	 * @see JobList#updateDestruction(UWSJob)
 	 * @see UWSParameters#set(String, Object)
 	 */
 	public final void setDestructionTime(Date destructionTime){
-		if (phase.isJobUpdatable()){
+		if (destructionTime != null && phase.isJobUpdatable()){
 			try{
 				inputParams.set(PARAM_DESTRUCTION_TIME, destructionTime);
 				if (myJobList != null)
@@ -741,14 +751,16 @@ public class UWSJob extends SerializableUWSObject {
 	 * <p><b><u>IMPORTANT:</u> This function will have no effect if the job is finished, that is to say if the current phase is
 	 * {@link ExecutionPhase#ABORTED ABORTED}, {@link ExecutionPhase#ERROR ERROR} or {@link ExecutionPhase#COMPLETED COMPLETED}.</i>.</b></p>
 	 * 
-	 * @param errorSummary	A summary of the error.
+	 * @param errorSummary	A summary of the error. <i>MUST NOT be NULL</i>
 	 * 
 	 * @throws UWSException	If the job execution is finished that is to say if the phase is ABORTED, ERROR or COMPLETED.
 	 * 
 	 * @see #isFinished()
 	 */
 	public final void setErrorSummary(ErrorSummary errorSummary) throws UWSException{
-		if (!isFinished())
+		if (errorSummary == null)
+			return;
+		else if (!isFinished())
 			this.errorSummary = errorSummary;
 		else{
 			UWSException ue = new UWSException(UWSException.NOT_ALLOWED, UWSExceptionFactory.jobModificationForbidden(jobId, getPhase(), "ERROR SUMMARY"));
@@ -873,8 +885,49 @@ public class UWSJob extends SerializableUWSObject {
 	 * @see JobPhase#isJobUpdatable()
 	 */
 	public final boolean addOrUpdateParameter(String paramName, Object paramValue) throws UWSException{
-		if (!phase.isFinished()){
+		return addOrUpdateParameter(paramName, paramValue, null);
+	}
+
+	/**
+	 * Adds or updates the specified parameter with the given value ONLY IF the job can be updated (considering its current execution phase, see {@link JobPhase#isJobUpdatable()}).
+	 * 
+	 * @param paramName		The name of the parameter to add or to update.
+	 * @param paramValue	The (new) value of the specified parameter.
+	 * @param user			The user who asks for this update.
+	 * 
+	 * @return				<ul><li><i>true</i> if the parameter has been successfully added/updated,</li>
+	 * 						<li><i>false</i> otherwise <i>(particularly if paramName=null or paramName="" or paramValue=null)</i>.</li></ul>
+	 * 
+	 * @throws UWSException	If a parameter value is incorrect.
+	 * 
+	 * @since 4.1
+	 * 
+	 * @see JobPhase#isJobUpdatable()
+	 */
+	public final boolean addOrUpdateParameter(String paramName, Object paramValue, final JobOwner user) throws UWSException{
+		if (paramValue != null && !phase.isFinished()){
+
+			// Set the parameter:
 			inputParams.set(paramName, paramValue);
+
+			// If it is a file or an array containing files, they must be moved in a location related to this job:
+			try{
+				if (paramValue instanceof UploadFile)
+					((UploadFile)paramValue).move(this);
+				else if (paramValue.getClass().isArray()){
+					for(Object o : (Object[])paramValue){
+						if (o != null && o instanceof UploadFile)
+							((UploadFile)o).move(this);
+					}
+				}
+			}catch(IOException ioe){
+				getLogger().logJob(LogLevel.WARNING, this, "MOVE_UPLOAD", "Can not move an uploaded file in the job \"" + jobId + "\"!", ioe);
+				return false;
+			}
+
+			// Apply the retrieved phase:
+			applyPhaseParam(user);
+
 			return true;
 		}else
 			return false;
@@ -913,6 +966,8 @@ public class UWSJob extends SerializableUWSObject {
 	 * </ul></p>
 	 * 
 	 * @param params		The UWS parameters to update.
+	 * @param user			The user who asks for this update.
+	 * 
 	 * @return				<ul><li><i>true</i> if all the given parameters have been successfully added/updated,</li>
 	 * 						<li><i>false</i> if some parameters have not been managed.</li></ul>
 	 * 
@@ -924,6 +979,10 @@ public class UWSJob extends SerializableUWSObject {
 	 * @see #applyPhaseParam()
 	 */
 	public boolean addOrUpdateParameters(UWSParameters params, final JobOwner user) throws UWSException{
+		// The job can be modified ONLY IF in PENDING phase: 
+		if (!phase.isJobUpdatable())
+			throw new UWSException(UWSException.FORBIDDEN, "Parameters modification is now forbidden for this job: it is not any more in PENDING phase!");
+
 		// Forbids the update if the user has not the required permission:
 		if (user != null && !user.equals(owner) && !user.hasWritePermission(this))
 			throw new UWSException(UWSException.PERMISSION_DENIED, UWSExceptionFactory.writePermissionDenied(user, false, getJobId()));
@@ -932,11 +991,24 @@ public class UWSJob extends SerializableUWSObject {
 		String[] updated = inputParams.update(params);
 
 		// If the destruction time has been updated, the modification must be propagated to the jobs list:
+		Object newValue;
 		for(String updatedParam : updated){
+			// CASE DESTRUCTION_TIME: update the thread dedicated to the destruction:
 			if (updatedParam.equals(PARAM_DESTRUCTION_TIME)){
 				if (myJobList != null)
 					myJobList.updateDestruction(this);
-				break;
+			}
+			// DEFAULT: test whether the parameter is a file, and if yes, move it in a location related to this job:
+			else{
+				newValue = inputParams.get(updatedParam);
+				if (newValue != null && newValue instanceof UploadFile){
+					try{
+						((UploadFile)newValue).move(this);
+					}catch(IOException ioe){
+						getLogger().logJob(LogLevel.WARNING, this, "MOVE_UPLOAD", "Can not move an uploaded file in the job \"" + jobId + "\"!", ioe);
+						inputParams.remove(updatedParam);
+					}
+				}
 			}
 		}
 
@@ -960,7 +1032,16 @@ public class UWSJob extends SerializableUWSObject {
 		if (phase.isFinished() || paramName == null)
 			return false;
 		else{
-			inputParams.remove(paramName);
+			// Remove the parameter from the map:
+			Object removed = inputParams.remove(paramName);
+			// If the parameter value was an uploaded file, delete it physically:
+			if (removed != null && removed instanceof UploadFile){
+				try{
+					((UploadFile)removed).deleteFile();
+				}catch(IOException ioe){
+					getLogger().logJob(LogLevel.WARNING, this, "MOVE_UPLOAD", "Can not delete the uploaded file \"" + paramName + "\" of the job \"" + jobId + "\"!", ioe);
+				}
+			}
 			return true;
 		}
 	}
@@ -1138,7 +1219,7 @@ public class UWSJob extends SerializableUWSObject {
 	public void start(boolean useManager) throws UWSException{
 		// This job must know its jobs list and this jobs list must know its UWS:
 		if (myJobList == null || myJobList.getUWS() == null)
-			throw new NullPointerException("A UWSJob can not start if it is not part of a job list or if its job list is not part of a UWS.");
+			throw new IllegalStateException("A UWSJob can not start if it is not linked to a job list or if its job list is not linked to a UWS.");
 
 		// If already running do nothing:
 		else if (isRunning())
@@ -1153,7 +1234,7 @@ public class UWSJob extends SerializableUWSObject {
 			// Create its corresponding thread:
 			thread = getFactory().createJobThread(this);
 			if (thread == null)
-				throw new NullPointerException("Missing job work ! The thread created by the factory is NULL => The job can't be executed !");
+				throw new NullPointerException("Missing job work! The thread created by the factory is NULL => The job can't be executed!");
 
 			// Change the job phase:
 			setPhase(ExecutionPhase.EXECUTING);
@@ -1333,7 +1414,7 @@ public class UWSJob extends SerializableUWSObject {
 	 * <p>Stops the job if running, removes the job from the execution manager, stops the timer for the execution duration
 	 * and may clear all files or any other resources associated to this job.</p>
 	 * 
-	 * <p><i>By default the job is aborted, only the {@link UWSJob#thread} attribute is set to null and the timers are stopped; no other operations (i.e. clear result files and error files) is done.</i></p>
+	 * <p><i>By default the job is aborted, the {@link UWSJob#thread} attribute is set to null, the timers are stopped and uploaded files, results and the error summary are deleted.</i></p>
 	 */
 	public void clearResources(){
 		// If still running, abort/stop the job:
@@ -1352,6 +1433,18 @@ public class UWSJob extends SerializableUWSObject {
 
 		thread = null;
 
+		// Clear all uploaded files:
+		Iterator<UploadFile> files = inputParams.getFiles();
+		UploadFile upl;
+		while(files.hasNext()){
+			upl = files.next();
+			try{
+				upl.deleteFile();
+			}catch(IOException ioe){
+				getLogger().logJob(LogLevel.ERROR, this, "CLEAR_RESOURCES", "Impossible to delete the file uploaded as parameter \"" + upl.paramName + "\" (" + upl.getLocation() + ") of the job \"" + jobId + "\"!", null);
+			}
+		}
+
 		// Clear all results file:
 		for(Result r : results.values()){
 			try{
diff --git a/src/uws/job/parameters/UWSParameters.java b/src/uws/job/parameters/UWSParameters.java
index 43b91256883130535a199baa3ff1bd8a0625731b..3609e1e0ff4620e4a6ca609f7ee58af1dc5f4fae 100644
--- a/src/uws/job/parameters/UWSParameters.java
+++ b/src/uws/job/parameters/UWSParameters.java
@@ -20,12 +20,15 @@ package uws.job.parameters;
  *                       Astronomisches Rechen Institut (ARI)
  */
 
+import java.io.IOException;
 import java.text.ParseException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -36,48 +39,68 @@ import uws.ISO8601Format;
 import uws.UWSException;
 import uws.job.UWSJob;
 import uws.service.UWS;
+import uws.service.request.UploadFile;
 
 /**
- * <p>Let extracting all UWS standard and non-standard parameters from a map or a {@link HttpServletRequest}.</p>
+ * <p>Let extracting all UWS standard and non-standard parameters from a map.</p>
  * 
  * <h3>Input parameter check</h3>
- * <p>It is possible to check the value of some or all parameters by calling the function {@link InputParamController#check(Object)}
- * of an {@link InputParamController} associated with the name of the parameter. Input parameter controllers can be
- * provided at the creation of a {@link UWSParameters}. If none are given, default ones are used (see {@link #getDefaultUWSParamControllers()}).</p>
+ * <p>
+ * 	It is possible to check the value of some or all parameters by calling the function {@link InputParamController#check(Object)}
+ * 	of an {@link InputParamController} associated with the name of the parameter. Input parameter controllers can be
+ * 	provided at the creation of a {@link UWSParameters}. If none are given, default ones are used (see {@link #getDefaultUWSParamControllers()})
+ * 	for the standard UWS parameters (e.g. destruction time, duration, etc...).
+ * </p>
  * 
  * <h3>Default value</h3>
- * <p>By calling the function {@link #init()}, you set a default value to any parameter which has an {@link InputParamController}
- * and which has not yet a value.</p>
- * <p>The function {@link InputParamController#getDefault()} returns a default value for its associated parameter.
- * This value must be obviously different from <i>NULL</i>.</p>
+ * <p>
+ * 	By calling the function {@link #init()}, you set a default value to any parameter which has an {@link InputParamController}
+ * 	and which has not yet a value.
+ * </p>
+ * <p>
+ * 	The function {@link InputParamController#getDefault()} returns a default value for its associated parameter.
+ * 	This value must be obviously different from <i>NULL</i>.
+ * </p>
  * 
  * <h3>Updating a {@link UWSParameters}</h3>
- * <p>It is possible to update a {@link UWSParameters} with another {@link UWSParameters} thanks to the function
- * {@link #update(UWSParameters)}. In this case, no check is done since the values given by a
- * {@link UWSParameters} should be theoretically already correct.</p>
- * <p>In order to forbid the modification of some parameters after their initialization, you must associate an
- * {@link InputParamController} with them and override the function {@link InputParamController#allowModification()}
- * so that it returns <i>false</i>.</p>
+ * <p>
+ * 	It is possible to update a {@link UWSParameters} with another {@link UWSParameters} thanks to the function
+ * 	{@link #update(UWSParameters)}. In this case, no check is done since the values given by a
+ * 	{@link UWSParameters} should be theoretically already correct.
+ * </p>
+ * <p>
+ * 	In order to forbid the modification of some parameters after their initialization, you must associate an
+ * 	{@link InputParamController} with them and override the function {@link InputParamController#allowModification()}
+ * 	so that it returns <i>false</i>.
+ * </p>
  * 
  * <h3>Case sensitivity</h3>
- * <p>All UWS STANDARD parameters can be provided in any case: they will always be identified and updated.
- * However any other parameter will be stored as it is provided: so with the same case. Thus, you must respect
- * the case for all UWS additional parameters in your other operations on the parameters.</p>
- * <p>If you want to identify your own parameters without case sensitivity, you must provides a list
- * of all the additional parameters you are expected at the creation: see {@link #UWSParameters(HttpServletRequest, Collection, Map)}
- * and {@link #UWSParameters(Map, Collection, Map)}.</p>
+ * <p>
+ * 	All UWS STANDARD parameters can be provided in any case: they will always be identified and updated.
+ * 	However any other parameter will be stored as it is provided: so with the same case. Thus, you must respect
+ * 	the case for all UWS additional parameters in your other operations on the parameters.
+ * </p>
+ * <p>
+ * 	If you want to identify your own parameters without case sensitivity, you must provides a list
+ * 	of all the additional parameters you are expected at the creation: see {@link #UWSParameters(HttpServletRequest, Collection, Map)}
+ * 	and {@link #UWSParameters(Map, Collection, Map)}.
+ * </p>
  * 
  * <h3>Additional parameters case normalization</h3>
- * <p>Indeed, the second parameter of these constructors (if != NULL) is used to normalize the name of the additional parameters so
- * that they have exactly the given case.</p>
- * <p>For instance, suppose that the given HttpServletRequest has a parameter named "foo" and
- * you expect a parameter named "FOO" (only the case changes). By providing a second parameter
- * which contains the entry "FOO", all parameters having the same name - even if the case is different -
- * will be named "FOO".</p>
+ * <p>
+ * 	Indeed, the second parameter of these constructors (if != NULL) is used to normalize the name of the additional parameters so
+ * 	that they have exactly the given case.
+ * </p>
+ * <p>
+ * 	For instance, suppose that the request had a parameter named "foo" and
+ * 	you expect a parameter named "FOO" (only the case changes). By providing a second parameter
+ * 	which contains the entry "FOO", all parameters having the same name - even if the case is different -
+ * 	will be named "FOO".
+ * </p>
  * <p>In brief:</p>
  * <ul>
- * 	<li><u>With "FOO" in the second parameter of the constructor:</u> {@link #get(String) get("FOO")} will return something if in the HttpServletRequest there is a parameter named: "foo", "FOO", "Foo", ...</li>
- * 	<li><u>If the second parameter is empty, NULL or does not contain "FOO":</u> {@link #get(String) get("FOO")} will return something if in the HttpServletRequest there is a parameter named exactly "FOO".</li>
+ * 	<li><u>With "FOO" in the second parameter of the constructor:</u> {@link #get(String) get("FOO")} will return something if in the request there was a parameter named: "foo", "FOO", "Foo", ...</li>
+ * 	<li><u>If the second parameter is empty, NULL or does not contain "FOO":</u> {@link #get(String) get("FOO")} will return something if in the request there was a parameter named exactly "FOO".</li>
  * </ul>
  * 
  * <h3>UWS standard parameters</h3>
@@ -88,11 +111,12 @@ import uws.service.UWS;
  * 	<li>executionDuration ({@link UWSJob#PARAM_EXECUTION_DURATION})</li>
  * 	<li>destruction ({@link UWSJob#PARAM_DESTRUCTION_TIME})</li>
  * </ul>
- * <p><i><u>note:</u> All parameters stored under the parameter {@link UWSJob#PARAM_PARAMETERS} (that's to say, additional parameters)
+ * <p><i><u>note 1:</u> All parameters stored under the parameter {@link UWSJob#PARAM_PARAMETERS} (that's to say, additional parameters)
  * are also considered as READ/WRITE parameters !</i></p>
+ * <p><i><u>note 2:</u> If several values have been submitted for the same UWS standard parameter, just the last occurrence is taken into account.</i></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (12/2014)
  */
 public class UWSParameters implements Iterable<Entry<String,Object>> {
 
@@ -101,6 +125,10 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 	 * <p>Names of the UWS parameters whose the value can be modified by the user.</p>
 	 */
 	protected final static String[] UWS_RW_PARAMETERS = new String[]{UWSJob.PARAM_PHASE,UWSJob.PARAM_RUN_ID,UWSJob.PARAM_EXECUTION_DURATION,UWSJob.PARAM_DESTRUCTION_TIME,UWSJob.PARAM_PARAMETERS};
+
+	/** Regular expression allowing to test which UWS parameters can be set. Actually, only: phase, runID, executionduration and destruction. */
+	public final static String UWS_RW_PARAMETERS_REGEXP = ("(" + UWSJob.PARAM_PHASE + "|" + UWSJob.PARAM_RUN_ID + "|" + UWSJob.PARAM_EXECUTION_DURATION + "|" + UWSJob.PARAM_DESTRUCTION_TIME + ")").toLowerCase();
+
 	/**
 	 * <p>Read-Only parameters.</p>
 	 * <p>Names of the UWS parameters whose the value can NOT be modified by the user. These value are not kept. They are only ignored.</p>
@@ -118,7 +146,13 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 	 * It is deleted (set to NULL) when there is a modification in the list of all parameters
 	 * (so in the function {@link #set(String, Object)}, {@link #update(UWSParameters)} and {@link #init()}).</i></p>
 	 */
-	private HashMap<String,Object> additionalParams = null;
+	private Map<String,Object> additionalParams = null;
+
+	/**
+	 * List of all uploaded files among the whole set of parameters.
+	 * @since 4.1
+	 */
+	protected List<UploadFile> files = null;
 
 	/**
 	 * List of the expected additional parameters.
@@ -191,20 +225,60 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 	 * 
 	 * @throws UWSException		If one of the given parameter is incorrect or badly formatted.
 	 * 
-	 * @see #UWSParameters(Collection, Map)
+	 * @see #UWSParameters(Map, Collection, Map)
 	 */
 	public UWSParameters(final HttpServletRequest request, final Collection<String> expectedAdditionalParams, final Map<String,InputParamController> inputParamControllers) throws UWSException{
-		this(expectedAdditionalParams, inputParamControllers);
+		this(getParameters(request), expectedAdditionalParams, inputParamControllers);
+	}
 
-		// Load all parameters:
-		if (request != null){
-			Enumeration<String> names = request.getParameterNames();
-			String paramName;
-			while(names.hasMoreElements()){
-				paramName = names.nextElement();
-				set(paramName, request.getParameter(paramName));
-			}
+	/**
+	 * <p>Get the parameters stored in the given HTTP request.</p>
+	 * 
+	 * <p>
+	 * 	Since the version 4.1, parameters are extracted immediately when the request is received. They are then stored in an attribute
+	 * 	under the name of {@value UWS#REQ_ATTRIBUTE_PARAMETERS}. Thus, the map of parameters can be got in that way. However, if this attribute
+	 * 	does not exist, this function will ask for the parameters extracted by {@link HttpServletRequest} ({@link HttpServletRequest#getParameterNames()}
+	 * 	and {@link HttpServletRequest#getParameter(String)}). In this last case only the last non-null occurrence of any parameter will be kept.
+	 * </p>
+	 * 
+	 * @param request	HTTP request from which the parameters must be got.
+	 * 
+	 * @return			The extracted parameters.
+	 * 
+	 * @since 4.1
+	 */
+	@SuppressWarnings("unchecked")
+	protected static Map<String,Object> getParameters(final HttpServletRequest request){
+		// No request => no parameters:
+		if (request == null)
+			return null;
+
+		/* The UWS service has theoretically already extracted all parameters in function of the content-type.
+		 * If so, these parameters can be found as a Map<String,Object> in the request attribute "UWS_PARAMETERS": */
+		try{
+			if (request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS) != null)
+				return (Map<String,Object>)request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS);
+		}catch(Exception e){} // 2 possible exceptions: ClassCastException and NullPointerException
+
+		/* If there is no such attribute or if it is not of the good type,
+		 * extract only application/x-www-form-urlencoded parameters: */
+		Map<String,Object> map = new HashMap<String,Object>(request.getParameterMap().size());
+		Enumeration<String> names = request.getParameterNames();
+		int i;
+		String n;
+		String[] values;
+		while(names.hasMoreElements()){
+			n = names.nextElement();
+			values = request.getParameterValues(n);
+			// search for the last non-null occurrence:
+			i = values.length - 1;
+			while(i >= 0 && values[i] == null)
+				i--;
+			// if there is one, keep it:
+			if (i >= 0)
+				map.put(n, values[i]);
 		}
+		return map;
 	}
 
 	/**
@@ -243,13 +317,11 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 
 		// Load all parameters:
 		if (params != null && !params.isEmpty()){
-			synchronized(params){
-				Iterator<Entry<String,Object>> it = params.entrySet().iterator();
-				Entry<String,Object> entry;
-				while(it.hasNext()){
-					entry = it.next();
-					set(entry.getKey(), entry.getValue());
-				}
+			Iterator<Entry<String,Object>> it = params.entrySet().iterator();
+			Entry<String,Object> entry;
+			while(it.hasNext()){
+				entry = it.next();
+				set(entry.getKey(), entry.getValue());
 			}
 		}
 	}
@@ -357,19 +429,36 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 		if (newParams != null && !newParams.params.isEmpty()){
 			synchronized(params){
 				additionalParams = null;
+				files = null;
 				String[] updated = new String[newParams.params.size()];
+				Object oldValue;
 				int i = 0;
 				for(Entry<String,Object> entry : newParams){
 					// Test whether this parameter is allowed to be modified after its initialization:
 					InputParamController controller = getController(entry.getKey());
 					if (controller != null && !controller.allowModification())
-						throw new UWSException(UWSException.NOT_ALLOWED, "The parameter \"" + entry.getKey() + "\" can not be modified after initialization!");
-					// If the value is NULL, removes this parameter:
-					if (entry.getValue() == null)
-						params.remove(entry.getKey());
-					// Else set it:
-					else
-						params.put(entry.getKey(), entry.getValue());
+						throw new UWSException(UWSException.FORBIDDEN, "The parameter \"" + entry.getKey() + "\" can not be modified after initialization!");
+					// Determine whether the value already exists:
+					if (params.containsKey(entry.getKey()) || entry.getKey().toLowerCase().matches(UWS_RW_PARAMETERS_REGEXP)){
+						// If the value is NULL, throw an error (no parameter can be removed after job creation):
+						if (entry.getValue() == null)
+							throw new UWSException(UWSException.FORBIDDEN, "Removing a parameter (here: \"" + entry.getKey() + "\") from a job is forbidden!");
+						// Else update the parameter value:
+						else{
+							// If the parameter to replace is an uploaded file, it must be physically removed before replacement:
+							oldValue = params.get(entry.getKey());
+							if (oldValue != null && oldValue instanceof UploadFile){
+								try{
+									((UploadFile)oldValue).deleteFile();
+								}catch(IOException ioe){}
+							}
+							// Perform the replacement:
+							params.put(entry.getKey(), entry.getValue());
+						}
+					}else
+						// No parameter can be added after job creation:
+						throw new UWSException(UWSException.FORBIDDEN, "Adding a parameter (here: \"" + entry.getKey() + "\") to an existing job is forbidden by the UWS protocol!");
+					// Update the list of updated parameters:
 					updated[i++] = entry.getKey();
 				}
 				return updated;
@@ -383,10 +472,12 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 	 * 
 	 * <p><i><u>note 1:</u> The case of the parameter name MUST BE correct EXCEPT FOR the standard UWS parameters (i.e. runId, executionDuration, destructionTime).</i></p>
 	 * <p><i><u>note 2:</u> If the name of the parameter is {@link UWSJob#PARAM_PARAMETERS PARAMETERS}, this function will return exactly what {@link #getAdditionalParameters()} returns.</i></p>
+	 * <p><i><u>note 3:</u> Depending of the way the parameters are fetched from an HTTP request, the returned object may be an array. Each item of this array would then be an occurrence of the parameter in the request (MAYBE in the same order as submitted).</i></p>
 	 * 
 	 * @param name	Name of the parameter to get.
 	 * 
-	 * @return		Value of the specified parameter, or <i>null</i> if the given name is <i>null</i>, empty or has no value.
+	 * @return		Value of the specified parameter, or <i>null</i> if the given name is <i>null</i>, or an array or {@link Object}s if several values
+	 *        		have been submitted for the same parameter, empty or has no value.
 	 * 
 	 * @see #normalizeParamName(String)
 	 * @see #getAdditionalParameters()
@@ -401,6 +492,34 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 			return params.get(normalizedName);
 	}
 
+	/**
+	 * Get the list of all uploaded files.
+	 * 
+	 * @return	An iterator over the list of uploaded files.
+	 * 
+	 * @since 4.1
+	 */
+	public final Iterator<UploadFile> getFiles(){
+		if (files == null){
+			files = new ArrayList<UploadFile>(3);
+			synchronized(params){
+				for(Object v : params.values()){
+					if (v == null)
+						continue;
+					else if (v instanceof UploadFile)
+						files.add((UploadFile)v);
+					else if (v.getClass().isArray()){
+						for(Object o : (Object[])v){
+							if (o instanceof UploadFile)
+								files.add((UploadFile)o);
+						}
+					}
+				}
+			}
+		}
+		return files.iterator();
+	}
+
 	/**
 	 * <p>Sets the given value to the specified parameter.
 	 * But if the given value is <i>null</i>, the specified parameter is merely removed.</p>
@@ -412,7 +531,7 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 	 * <p><i><u>note 5:</u> If the parameter {@link UWSJob#PARAM_PARAMETERS PARAMETERS} is given, it must be a Map<String, Object>. In this case, the map is read and all its entries are added individually.</i></p>
 	 * 
 	 * @param name				Name of the parameter to set (add, update or remove). <i><u>note:</u> not case sensitive ONLY FOR the standard UWS parameters !</i>
-	 * @param value				The value to set. <i><u>note:</u> NULL means that the specified parameter must be removed !</i>
+	 * @param value				The value to set. <i><u>note:</u> NULL means that the specified parameter must be removed ; several values may have been provided using an array of Objects.</i>
 	 * 
 	 * @return					The old value of the specified parameter. <i>null</i> may mean that the parameter has just been added, but it may also mean that nothing has been done (because, the given name is null, empty or corresponds to a read-only parameter).
 	 * 
@@ -422,6 +541,10 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 	 */
 	@SuppressWarnings("unchecked")
 	public final Object set(final String name, Object value) throws UWSException{
+		// If the given value is NULL, the parameter must be removed:
+		if (value == null)
+			return remove(name);
+
 		// Normalize (take into account the case ONLY FOR the non-standard UWS parameters) the given parameter name:
 		String normalizedName = normalizeParamName(name);
 
@@ -431,38 +554,42 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 
 		synchronized(params){
 			additionalParams = null;
-
-			// If the given value is NULL, the parameter must be removed:
-			if (value == null)
-				return params.remove(normalizedName);
-			else{
-				// Case of the PARAMETERS parameter: read all parameters and set them individually into this UWSParameters instance:
-				if (normalizedName.equals(UWSJob.PARAM_PARAMETERS)){
-					// the value MUST BE a Map<String, Object>:
-					if (value instanceof Map){
-						try{
-							Map<String,Object> otherParams = (Map<String,Object>)value;
-							HashMap<String,Object> mapOldValues = new HashMap<String,Object>(otherParams.size());
-							Object oldValue = null;
-							for(Entry<String,Object> entry : otherParams.entrySet()){
-								oldValue = set(entry.getKey(), entry.getValue());
-								mapOldValues.put(entry.getKey(), oldValue);
-							}
-							return mapOldValues;
-						}catch(ClassCastException cce){
-							return null;
+			files = null;
+
+			// Case of the PARAMETERS parameter: read all parameters and set them individually into this UWSParameters instance:
+			if (normalizedName.equals(UWSJob.PARAM_PARAMETERS)){
+				// the value MUST BE a Map<String, Object>:
+				if (value instanceof Map){
+					try{
+						Map<String,Object> otherParams = (Map<String,Object>)value;
+						HashMap<String,Object> mapOldValues = new HashMap<String,Object>(otherParams.size());
+						Object oldValue = null;
+						for(Entry<String,Object> entry : otherParams.entrySet()){
+							oldValue = set(entry.getKey(), entry.getValue());
+							mapOldValues.put(entry.getKey(), oldValue);
 						}
-					}else
+						return mapOldValues;
+					}catch(ClassCastException cce){
 						return null;
-				}else{
-					// Check the value before setting it:
-					InputParamController controller = getController(normalizedName);
-					if (controller != null)
-						value = controller.check(value);
-
-					// Set the new value:
-					return params.put(normalizedName, value);
+					}
+				}else
+					return null;
+			}else{
+				// Check the value before setting it:
+				InputParamController controller = getController(normalizedName);
+				if (controller != null)
+					value = controller.check(value);
+
+				// If the parameter already exists and it is an uploaded file, delete it before its replacement:
+				Object oldValue = params.get(normalizedName);
+				if (oldValue != null && oldValue instanceof UploadFile){
+					try{
+						((UploadFile)oldValue).deleteFile();
+					}catch(IOException ioe){}
 				}
+
+				// Set the new value:
+				return params.put(normalizedName, value);
 			}
 		}
 	}
@@ -486,7 +613,17 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
 
 		synchronized(params){
 			additionalParams = null;
-			return params.remove(normalizedName);
+			files = null;
+			// Remove the file:
+			Object removed = params.remove(normalizedName);
+			// If the removed parameter was a file, remove it from the server:
+			if (removed != null && removed instanceof UploadFile){
+				try{
+					((UploadFile)removed).deleteFile();
+				}catch(IOException ioe){}
+			}
+			// Return the value of the removed parameter:
+			return removed;
 		}
 	}
 
diff --git a/src/uws/job/serializer/XMLSerializer.java b/src/uws/job/serializer/XMLSerializer.java
index b5a1942c5d7d01fdc9e1481da7f50361d302063e..4049a8b408595d973fc1e4c80d60f79dcf27a4c5 100644
--- a/src/uws/job/serializer/XMLSerializer.java
+++ b/src/uws/job/serializer/XMLSerializer.java
@@ -32,12 +32,13 @@ import uws.job.UWSJob;
 import uws.job.user.JobOwner;
 import uws.service.UWS;
 import uws.service.UWSUrl;
+import uws.service.request.UploadFile;
 
 /**
  * Lets serializing any UWS resource in XML.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (12/2014)
  */
 public class XMLSerializer extends UWSSerializer {
 	private static final long serialVersionUID = 1L;
@@ -191,7 +192,8 @@ public class XMLSerializer extends UWSSerializer {
 		// general information:
 		xml.append("<job").append(getUWSNamespace(root)).append(">");
 		xml.append(newLine).append(getJobID(job, false));
-		xml.append(newLine).append(getRunID(job, false));
+		if (job.getRunId() != null)
+			xml.append(newLine).append(getRunID(job, false));
 		xml.append(newLine).append(getOwnerID(job, false));
 		xml.append(newLine).append(getPhase(job, false));
 		xml.append(newLine).append(getQuote(job, false));
@@ -346,11 +348,42 @@ public class XMLSerializer extends UWSSerializer {
 	@Override
 	public String getAdditionalParameter(final String paramName, final Object paramValue, final boolean root){
 		if (paramName != null && paramValue != null){
-			if (root)
-				return paramValue.toString();
-			else
-				return (new StringBuffer("<parameter")).append(getUWSNamespace(root)).append(" id=\"").append(escapeXMLAttribute(paramName)).append("\">").append(escapeXMLData(paramValue.toString())).append("</parameter>").toString();
-		}else
+			// If ROOT, just the value must be returned:
+			if (root){
+				if (paramValue.getClass().isArray()){
+					StringBuffer buf = new StringBuffer();
+					for(Object o : (Object[])paramValue){
+						if (buf.length() > 0)
+							buf.append(';');
+						buf.append(o.toString());
+					}
+					return buf.toString();
+				}else
+					return paramValue.toString();
+			}
+			// OTHERWISE, return the XML description:
+			else{
+				StringBuffer buf = new StringBuffer();
+				// if array (=> multiple occurrences of the parameter), each item must be one individual parameter:
+				if (paramValue.getClass().isArray()){
+					for(Object o : (Object[])paramValue){
+						if (buf.length() > 0)
+							buf.append("\n\t").append(tabPrefix);
+						buf.append(getAdditionalParameter(paramName, o, root));
+					}
+				}
+				// otherwise, just return the XML parameter description:
+				else{
+					buf.append("<parameter").append(getUWSNamespace(root)).append(" id=\"").append(escapeXMLAttribute(paramName));
+					if (paramValue instanceof UploadFile)
+						buf.append("\" byReference=\"true");
+					buf.append("\">").append(escapeXMLData(paramValue.toString())).append("</parameter>");
+				}
+				return buf.toString();
+			}
+		}
+		// If NO VALUE or NO NAME, return an empty string:
+		else
 			return "";
 	}
 
@@ -393,16 +426,31 @@ public class XMLSerializer extends UWSSerializer {
 	/* ESCAPE METHODS */
 	/* ************** */
 	/**
-	 * <p>Escapes the content of a node (data between the open and the close tags).</p>
-	 * 
-	 * <p><i>By default: surrounds the given data by "&lt;![CDATA[" and "]]&gt;".</i></p>
+	 * Escapes the content of a node (data between the open and the close tags).
 	 * 
 	 * @param data	Data to escape.
 	 * 
 	 * @return		Escaped data.
 	 */
 	public static String escapeXMLData(final String data){
-		return "<![CDATA[" + data + "]]>";
+		StringBuffer encoded = new StringBuffer();
+		for(int i = 0; i < data.length(); i++){
+			char c = data.charAt(i);
+			switch(c){
+				case '&':
+					encoded.append("&amp;");
+					break;
+				case '<':
+					encoded.append("&lt;");
+					break;
+				case '>':
+					encoded.append("&gt;");
+					break;
+				default:
+					encoded.append(ensureLegalXml(c));
+			}
+		}
+		return encoded.toString();
 	}
 
 	/**
@@ -429,11 +477,8 @@ public class XMLSerializer extends UWSSerializer {
 				case '"':
 					encoded.append("&quot;");
 					break;
-				case '\'':
-					encoded.append("&#039;");
-					break;
 				default:
-					encoded.append(c);
+					encoded.append(ensureLegalXml(c));
 			}
 		}
 		return encoded.toString();
@@ -457,4 +502,22 @@ public class XMLSerializer extends UWSSerializer {
 		}
 	}
 
+	/**
+	 * <p>Returns a legal XML character corresponding to an input character.
+	 * Certain characters are simply illegal in XML (regardless of encoding).
+	 * If the input character is legal in XML, it is returned;
+	 * otherwise some other weird but legal character 
+	 * (currently the inverted question mark, "\u00BF") is returned instead.</p>
+	 * 
+	 * <p><i>Note: copy of the STILTS VOSerializer.ensureLegalXml(char) function.</i></p>
+	 *
+	 * @param   c  input character
+	 * @return  legal XML character, <code>c</code> if possible
+	 * 
+	 * @since 4.1
+	 */
+	public static char ensureLegalXml(char c){
+		return ((c >= '\u0020' && c <= '\uD7FF') || (c >= '\uE000' && c <= '\uFFFD') || ((c) == 0x09 || (c) == 0x0A || (c) == 0x0D)) ? c : '\u00BF';
+	}
+
 }
diff --git a/src/uws/service/AbstractUWSFactory.java b/src/uws/service/AbstractUWSFactory.java
index 04fbe8a5e18c9fbe76fef551e03bab0a00edccee..86d1112601db72eacc434cf3238760b137a34544 100644
--- a/src/uws/service/AbstractUWSFactory.java
+++ b/src/uws/service/AbstractUWSFactory.java
@@ -16,7 +16,8 @@ package uws.service;
  * You should have received a copy of the GNU Lesser General Public License
  * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.ArrayList;
@@ -28,26 +29,26 @@ import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
 
 import uws.UWSException;
-
 import uws.job.ErrorSummary;
 import uws.job.JobThread;
 import uws.job.Result;
 import uws.job.UWSJob;
-
 import uws.job.parameters.DestructionTimeController;
+import uws.job.parameters.DestructionTimeController.DateField;
 import uws.job.parameters.ExecutionDurationController;
 import uws.job.parameters.InputParamController;
 import uws.job.parameters.UWSParameters;
-import uws.job.parameters.DestructionTimeController.DateField;
-
 import uws.job.user.JobOwner;
+import uws.service.file.UWSFileManager;
+import uws.service.request.RequestParser;
+import uws.service.request.UWSRequestParser;
 
 /**
  * <p>Abstract implementation of {@link UWSFactory}.
  * Only the function which creates a {@link JobThread} from a {@link UWSJob} needs to be implemented.</p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2012
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 4.1 (11/2014)
  */
 public abstract class AbstractUWSFactory implements UWSFactory {
 
@@ -101,6 +102,11 @@ public abstract class AbstractUWSFactory implements UWSFactory {
 		return new UWSParameters(req, expectedAdditionalParams, inputParamControllers);
 	}
 
+	@Override
+	public RequestParser createRequestParser(final UWSFileManager fileManager) throws UWSException{
+		return new UWSRequestParser(fileManager);
+	}
+
 	/**
 	 * Adds the name of an additional parameter which must be identified without taking into account its case
 	 * and then stored with the case of the given name.
diff --git a/src/uws/service/UWS.java b/src/uws/service/UWS.java
index a81e2968d2df3a4d42114bd105bde6c1904edd80..257960fc785888e1800f14885b4a24c69724209d 100644
--- a/src/uws/service/UWS.java
+++ b/src/uws/service/UWS.java
@@ -16,7 +16,8 @@ package uws.service;
  * You should have received a copy of the GNU Lesser General Public License
  * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import uws.UWSException;
@@ -55,11 +56,19 @@ import uws.service.log.UWSLog;
  * 	the UWS and the servlet.
  * </p>
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 05/2012
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 4.1 (12/2014)
  */
 public interface UWS extends Iterable<JobList> {
 
+	/** Attribute of the HttpServletRequest to set and to get in order to access the request ID set by the UWS library.
+	 * @since 4.1 */
+	public static final String REQ_ATTRIBUTE_ID = "UWS_REQUEST_ID";
+
+	/** Attribute of the HttpServletRequest to set and to get in order to access the parameters extracted by the UWS library (using a RequestParser).
+	 * @since 4.1 */
+	public static final String REQ_ATTRIBUTE_PARAMETERS = "UWS_PARAMETERS";
+
 	/**
 	 * Gets the name of this UWS.
 	 * 
@@ -106,17 +115,6 @@ public interface UWS extends Iterable<JobList> {
 	 */
 	public int getNbJobList();
 
-	/*
-	 * <p>Removes the specified jobs list.</p>
-	 * <p><i><u>note:</u> After the call of this function, the UWS reference of the given jobs list should be removed (see {@link JobList#setUWS(UWS)}).</i></p>
-	 * 
-	 * @param name	Name of the jobs list to remove.
-	 * 
-	 * @return		The removed jobs list
-	 * 				or <i>null</i> if no jobs list with the given name has been found.
-	 *
-	public JobList removeJobList(final String name) throws UWSException;*/
-
 	/**
 	 * <p>Destroys the specified jobs list.</p>
 	 * <p><i><u>note:</u> After the call of this function, the UWS reference of the given jobs list should be removed (see {@link JobList#setUWS(UWS)}).</i></p>
@@ -179,8 +177,9 @@ public interface UWS extends Iterable<JobList> {
 	/* ******************* */
 
 	/**
-	 * TODO JAVADOC TO WRITE!
-	 * @return
+	 * Gets the object which is able to identify a user from an HTTP request.
+	 * 
+	 * @return	Its user identifier.
 	 */
 	public UserIdentifier getUserIdentifier();
 
diff --git a/src/uws/service/UWSFactory.java b/src/uws/service/UWSFactory.java
index e9b45c2260685c6f75259390001a276f4edf5710..6860f5f2aff00cec4be79d61ddf58381fcc6a7f8 100644
--- a/src/uws/service/UWSFactory.java
+++ b/src/uws/service/UWSFactory.java
@@ -16,7 +16,8 @@ package uws.service;
  * You should have received a copy of the GNU Lesser General Public License
  * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.List;
@@ -31,12 +32,14 @@ import uws.job.Result;
 import uws.job.UWSJob;
 import uws.job.parameters.UWSParameters;
 import uws.job.user.JobOwner;
+import uws.service.file.UWSFileManager;
+import uws.service.request.RequestParser;
 
 /**
  * Let's creating UWS jobs, their threads and extracting their parameters from {@link HttpServletRequest}.
  * 
- * @author Gr&eacute;gory Mantelet (CDS)
- * @version 06/2012
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 4.1 (11/2014)
  * 
  * @see UWS#getFactory()
  */
@@ -112,4 +115,18 @@ public interface UWSFactory {
 	 */
 	public UWSParameters createUWSParameters(final Map<String,Object> params) throws UWSException;
 
+	/**
+	 * Create a parser of HTTP requests. This object is able to deal with the different formats
+	 * in which parameters are provided in an HTTP request.
+	 * 
+	 * @param manager	File manager to use if files are uploaded in an HTTP request.
+	 * 
+	 * @return			The request parser to use.
+	 * 
+	 * @throws UWSException	If an error occurs while creating the parser.
+	 * 
+	 * @since 4.1
+	 */
+	public RequestParser createRequestParser(final UWSFileManager manager) throws UWSException;
+
 }
diff --git a/src/uws/service/UWSService.java b/src/uws/service/UWSService.java
index a766e9613d82693f6497a174dc24a0682bd365cc..8232173ff85099bbf89e1d7682f9722b4b7a6bf9 100644
--- a/src/uws/service/UWSService.java
+++ b/src/uws/service/UWSService.java
@@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import uws.AcceptHeader;
 import uws.UWSException;
+import uws.UWSToolBox;
 import uws.job.ExecutionPhase;
 import uws.job.JobList;
 import uws.job.UWSJob;
@@ -48,6 +49,7 @@ import uws.service.actions.GetJobParam;
 import uws.service.actions.JobSummary;
 import uws.service.actions.ListJobs;
 import uws.service.actions.SetJobParam;
+import uws.service.actions.SetUWSParameter;
 import uws.service.actions.ShowHomePage;
 import uws.service.actions.UWSAction;
 import uws.service.backup.UWSBackupManager;
@@ -57,6 +59,7 @@ import uws.service.file.UWSFileManager;
 import uws.service.log.DefaultUWSLog;
 import uws.service.log.UWSLog;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.RequestParser;
 
 /**
  * <h3>General description</h3>
@@ -233,6 +236,10 @@ public class UWSService implements UWS {
 	/** Lets logging info/debug/warnings/errors about this UWS. */
 	protected UWSLog logger;
 
+	/** Lets extract all parameters from an HTTP request, whatever is its content-type.
+	 * @since 4.1*/
+	protected final RequestParser requestParser;
+
 	/** Lets writing/formatting any exception/throwable in a HttpServletResponse. */
 	protected ServiceErrorWriter errorWriter;
 
@@ -259,10 +266,11 @@ public class UWSService implements UWS {
 	 * @param fileManager	Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...).
 	 * 
 	 * @throws NullPointerException	If at least one of the parameters is <i>null</i>.
+	 * @throws UWSException			If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}).
 	 * 
 	 * @see #UWSService(UWSFactory, UWSFileManager, UWSLog)
 	 */
-	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager){
+	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager) throws UWSException{
 		this(jobFactory, fileManager, (UWSLog)null);
 	}
 
@@ -280,8 +288,9 @@ public class UWSService implements UWS {
 	 * @param logger		Object which lets printing any message (error, info, debug, warning).
 	 * 
 	 * @throws NullPointerException	If at least one of the parameters is <i>null</i>.
+	 * @throws UWSException			If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}).
 	 */
-	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSLog logger){
+	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSLog logger) throws UWSException{
 		if (jobFactory == null)
 			throw new NullPointerException("Missing UWS factory! Can not create a UWSService.");
 		factory = jobFactory;
@@ -291,6 +300,9 @@ public class UWSService implements UWS {
 		this.fileManager = fileManager;
 
 		this.logger = (logger == null) ? new DefaultUWSLog(this) : logger;
+
+		requestParser = jobFactory.createRequestParser(fileManager);
+
 		errorWriter = new DefaultUWSErrorWriter(this.logger);
 
 		// Initialize the list of jobs:
@@ -308,6 +320,7 @@ public class UWSService implements UWS {
 		uwsActions.add(new ShowHomePage(this));
 		uwsActions.add(new ListJobs(this));
 		uwsActions.add(new AddJob(this));
+		uwsActions.add(new SetUWSParameter(this));
 		uwsActions.add(new DestroyJob(this));
 		uwsActions.add(new JobSummary(this));
 		uwsActions.add(new GetJobParam(this));
@@ -376,9 +389,11 @@ public class UWSService implements UWS {
 	 * @param fileManager	Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...).
 	 * @param urlInterpreter	The UWS URL interpreter to use in this UWS.
 	 * 
+	 * @throws UWSException			If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}).
+	 * 
 	 * @see #UWSService(UWSFactory, UWSFileManager, UWSLog, UWSUrl)
 	 */
-	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSUrl urlInterpreter){
+	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSUrl urlInterpreter) throws UWSException{
 		this(jobFactory, fileManager, null, urlInterpreter);
 	}
 
@@ -389,8 +404,10 @@ public class UWSService implements UWS {
 	 * @param fileManager	Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...).
 	 * @param logger		Object which lets printing any message (error, info, debug, warning).
 	 * @param urlInterpreter	The UWS URL interpreter to use in this UWS.
+	 * 
+	 * @throws UWSException			If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}).
 	 */
-	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSLog logger, final UWSUrl urlInterpreter){
+	public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSLog logger, final UWSUrl urlInterpreter) throws UWSException{
 		this(jobFactory, fileManager, logger);
 		setUrlInterpreter(urlInterpreter);
 		if (this.urlInterpreter != null)
@@ -1041,6 +1058,7 @@ public class UWSService implements UWS {
 
 		// Generate a unique ID for this request execution (for log purpose only):
 		final String reqID = generateRequestID(request);
+		request.setAttribute(UWS.REQ_ATTRIBUTE_ID, reqID);
 
 		// Log the reception of the request:
 		logger.logHttp(LogLevel.INFO, request, reqID, null, null);
@@ -1062,6 +1080,9 @@ public class UWSService implements UWS {
 			UWSUrl urlInterpreter = new UWSUrl(this.urlInterpreter);
 			urlInterpreter.load(request);
 
+			// Extract all parameters:
+			request.setAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS, requestParser.parse(request));
+
 			// Identify the user:
 			user = (userIdentifier == null) ? null : userIdentifier.extractUserId(urlInterpreter, request);
 
@@ -1092,6 +1113,8 @@ public class UWSService implements UWS {
 			sendError(ex, request, reqID, user, ((action != null) ? action.getName() : null), response);
 		}finally{
 			executedAction = action;
+			// Free resources about uploaded files ; only unused files will be deleted:
+			UWSToolBox.deleteUploads(request);
 		}
 
 		return actionApplied;
diff --git a/src/uws/service/UWSServlet.java b/src/uws/service/UWSServlet.java
index 33d8b4a48e7221cf398e3e3b8fa77d18e05792fc..8dab40a1e799b90b0b32553cc073b43de3fd1a27 100644
--- a/src/uws/service/UWSServlet.java
+++ b/src/uws/service/UWSServlet.java
@@ -67,6 +67,9 @@ import uws.service.file.UWSFileManager;
 import uws.service.log.DefaultUWSLog;
 import uws.service.log.UWSLog;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.RequestParser;
+import uws.service.request.UWSRequestParser;
+import uws.service.request.UploadFile;
 
 /**
  * <p>
@@ -129,7 +132,7 @@ import uws.service.log.UWSLog.LogLevel;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (11/2014)
  */
 public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory {
 	private static final long serialVersionUID = 1L;
@@ -170,6 +173,10 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 	/** Lets logging info/debug/warnings/errors about this UWS. */
 	protected UWSLog logger;
 
+	/** Lets extract all parameters from an HTTP request, whatever is its content-type.
+	 * @since 4.1*/
+	protected RequestParser requestParser;
+
 	/** Lets writing/formatting any exception/throwable in a HttpServletResponse. */
 	protected ServiceErrorWriter errorWriter;
 
@@ -202,6 +209,14 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 		logger = new DefaultUWSLog(this);
 		errorWriter = new DefaultUWSErrorWriter(logger);
 
+		// Set the request parser:
+		try{
+			requestParser = createRequestParser(fileManager);
+		}catch(UWSException ue){
+			logger.logUWS(LogLevel.FATAL, null, "INIT", "Can't create a request parser!", ue);
+			throw new ServletException(INIT_ERROR_MSG, ue);
+		}
+
 		// Initialize the list of jobs:
 		mapJobLists = new LinkedHashMap<String,JobList>();
 
@@ -265,6 +280,7 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 
 		// Generate a unique ID for this request execution (for log purpose only):
 		final String reqID = generateRequestID(req);
+		req.setAttribute(UWS.REQ_ATTRIBUTE_ID, reqID);
 
 		// Log the reception of the request:
 		logger.logHttp(LogLevel.INFO, req, reqID, null, null);
@@ -280,6 +296,9 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 			UWSUrl requestUrl = new UWSUrl(this.urlInterpreter);
 			requestUrl.load(req);
 
+			// Extract all parameters:
+			req.setAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS, requestParser.parse(req));
+
 			// Identify the user:
 			user = (userIdentifier == null) ? null : userIdentifier.extractUserId(requestUrl, req);
 
@@ -320,13 +339,18 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 					uwsAction = UWSAction.ADD_JOB;
 					doAddJob(requestUrl, req, resp, user);
 
+				}// SET JOB's UWS STANDARD PARAMETER
+				else if (requestUrl.hasJobList() && requestUrl.hasJob() && requestUrl.getAttributes().length == 1 && requestUrl.getAttributes()[0].toLowerCase().matches(UWSParameters.UWS_RW_PARAMETERS_REGEXP) && UWSToolBox.hasParameter(requestUrl.getAttributes()[0], req, false)){
+					uwsAction = UWSAction.SET_UWS_PARAMETER;
+					doSetUWSParameter(requestUrl, req, resp, user);
+
 				}// SET JOB PARAMETER:
-				else if (requestUrl.hasJobList() && requestUrl.hasJob() && (!requestUrl.hasAttribute() || requestUrl.getAttributes().length == 1) && req.getParameterMap().size() > 0){
+				else if (requestUrl.hasJobList() && requestUrl.hasJob() && (!requestUrl.hasAttribute() || requestUrl.getAttributes().length == 1 && requestUrl.getAttributes()[0].equalsIgnoreCase(UWSJob.PARAM_PARAMETERS)) && UWSToolBox.getNbParameters(req) > 0){
 					uwsAction = UWSAction.SET_JOB_PARAM;
 					doSetJobParam(requestUrl, req, resp, user);
 
 				}// DESTROY JOB:
-				else if (requestUrl.hasJobList() && requestUrl.hasJob() && req.getParameter(UWSJob.PARAM_ACTION) != null && req.getParameter(UWSJob.PARAM_ACTION).equalsIgnoreCase(UWSJob.ACTION_DELETE)){
+				else if (requestUrl.hasJobList() && requestUrl.hasJob() && UWSToolBox.hasParameter(UWSJob.PARAM_ACTION, UWSJob.ACTION_DELETE, req, false)){
 					uwsAction = UWSAction.DESTROY_JOB;
 					doDestroyJob(requestUrl, req, resp, user);
 
@@ -336,10 +360,17 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 			}// METHOD PUT:
 			else if (method.equals("PUT")){
 				// SET JOB PARAMETER:
-				if (requestUrl.hasJobList() && requestUrl.hasJob() && req.getMethod().equalsIgnoreCase("put") && requestUrl.getAttributes().length >= 2 && requestUrl.getAttributes()[0].equalsIgnoreCase(UWSJob.PARAM_PARAMETERS) && req.getParameter(requestUrl.getAttributes()[1]) != null){
+				if (requestUrl.hasJobList() && requestUrl.hasJob() && requestUrl.getAttributes().length >= 2 && requestUrl.getAttributes()[0].equalsIgnoreCase(UWSJob.PARAM_PARAMETERS)){
 					uwsAction = UWSAction.SET_JOB_PARAM;
+					if (!UWSToolBox.hasParameter(requestUrl.getAttributes()[1], req, false))
+						throw new UWSException(UWSException.BAD_REQUEST, "Wrong parameter name in the PUT request! Expected: " + requestUrl.getAttributes()[1]);
 					doSetJobParam(requestUrl, req, resp, user);
 
+				}// SET JOB's UWS STANDARD PARAMETER
+				else if (requestUrl.hasJobList() && requestUrl.hasJob() && requestUrl.getAttributes().length == 1 && requestUrl.getAttributes()[0].toLowerCase().matches(UWSParameters.UWS_RW_PARAMETERS_REGEXP) && UWSToolBox.hasParameter(requestUrl.getAttributes()[0], req, false)){
+					uwsAction = UWSAction.SET_UWS_PARAMETER;
+					doSetUWSParameter(requestUrl, req, resp, user);
+
 				}else
 					throw new UWSException(UWSException.NOT_IMPLEMENTED, "Unknown UWS action!");
 
@@ -368,6 +399,9 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 			logger.logHttp(LogLevel.INFO, req, reqID, "HTTP " + UWSException.OK + " - Action \"" + uwsAction + "\" aborted by the client! [Client abort => ClientAbortException]", cae);
 		}catch(Throwable t){
 			sendError(t, req, reqID, user, uwsAction, resp);
+		}finally{
+			// Free resources about uploaded files ; only unused files will be deleted:
+			UWSToolBox.deleteUploads(req);
 		}
 	}
 
@@ -426,12 +460,30 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 
 		// Add it to the jobs list:
 		if (jobsList.addNewJob(newJob) != null){
+			// Start the job if the phase parameter was provided with the "RUN" value:
+			if (UWSToolBox.hasParameter(UWSJob.PARAM_PHASE, UWSJob.PHASE_RUN, req, false))
+				newJob.start();
 			// Make a redirection to the added job:
 			redirect(requestUrl.jobSummary(jobsList.getName(), newJob.getJobId()).getRequestURL(), req, user, UWSAction.ADD_JOB, resp);
 		}else
 			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Unable to add the new job " + newJob.getJobId() + " to the jobs list " + jobsList.getName() + ". (ID already used = " + (jobsList.getJob(newJob.getJobId()) != null) + ")");
 	}
 
+	protected void doSetUWSParameter(UWSUrl requestUrl, HttpServletRequest req, HttpServletResponse resp, JobOwner user) throws UWSException, ServletException, IOException{
+		// Get the job:
+		UWSJob job = getJob(requestUrl);
+
+		// Forbids the action if the user has not the WRITE permission for the specified job:
+		if (user != null && !user.hasWritePermission(job))
+			throw new UWSException(UWSException.PERMISSION_DENIED, UWSExceptionFactory.writePermissionDenied(user, true, job.getJobId()));
+
+		String name = requestUrl.getAttributes()[0];
+		job.addOrUpdateParameter(name, UWSToolBox.getParameter(name, req, false), user);
+
+		// Make a redirection to the job:
+		redirect(requestUrl.jobSummary(requestUrl.getJobListName(), job.getJobId()).getRequestURL(), req, user, getName(), resp);
+	}
+
 	protected void doDestroyJob(UWSUrl requestUrl, HttpServletRequest req, HttpServletResponse resp, JobOwner user) throws UWSException, ServletException, IOException{
 		// Get the jobs list:
 		JobList jobsList = getJobList(requestUrl.getJobListName());
@@ -510,6 +562,24 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 						input.close();
 				}
 			}
+		}// REFERENCE FILE: Display the content of the uploaded file or redirect to the URL (if it is a URL):
+		else if (attributes[0].equalsIgnoreCase(UWSJob.PARAM_PARAMETERS) && attributes.length > 1 && job.getAdditionalParameterValue(attributes[1]) != null && job.getAdditionalParameterValue(attributes[1]) instanceof UploadFile){
+			UploadFile upl = (UploadFile)job.getAdditionalParameterValue(attributes[1]);
+			if (upl.getLocation().matches("^http(s)?://"))
+				redirect(upl.getLocation(), req, user, getName(), resp);
+			else{
+				InputStream input = null;
+				try{
+					input = getFileManager().getUploadInput(upl);
+					UWSToolBox.write(input, upl.mimeType, upl.length, resp);
+				}catch(IOException ioe){
+					getLogger().logUWS(LogLevel.ERROR, upl, "GET_PARAMETER", "Can not read the content of the uploaded file \"" + upl.paramName + "\" of the job \"" + job.getJobId() + "\"!", ioe);
+					throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Can not read the content of the uploaded file " + upl.paramName + " (job ID: " + job.getJobId() + ").");
+				}finally{
+					if (input != null)
+						input.close();
+				}
+			}
 		}// DEFAULT CASE: Display the serialization of the selected UWS object:
 		else{
 			// Write the value/content of the selected attribute:
@@ -597,6 +667,11 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 		return new UWSParameters(req, expectedAdditionalParams, inputParamControllers);
 	}
 
+	@Override
+	public RequestParser createRequestParser(final UWSFileManager fileManager) throws UWSException{
+		return new UWSRequestParser(fileManager);
+	}
+
 	/* ****************************** */
 	/* REDIRECTION & ERROR MANAGEMENT */
 	/* ****************************** */
diff --git a/src/uws/service/actions/DestroyJob.java b/src/uws/service/actions/DestroyJob.java
index 5b7625f46996560fe12165d0d8c76bb5a3e67fdd..2aeb76a5986640e21cffa19b80fef75d6ff13ed5 100644
--- a/src/uws/service/actions/DestroyJob.java
+++ b/src/uws/service/actions/DestroyJob.java
@@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import uws.UWSException;
+import uws.UWSToolBox;
 import uws.job.JobList;
 import uws.job.UWSJob;
 import uws.job.user.JobOwner;
@@ -42,7 +43,7 @@ import uws.service.log.UWSLog.LogLevel;
  * The response of this action is a redirection to the jobs list.</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (08/2014)
+ * @version 4.1 (11/2014)
  */
 public class DestroyJob extends UWSAction {
 	private static final long serialVersionUID = 1L;
@@ -78,7 +79,7 @@ public class DestroyJob extends UWSAction {
 	 */
 	@Override
 	public boolean match(UWSUrl urlInterpreter, JobOwner user, HttpServletRequest request) throws UWSException{
-		return (urlInterpreter.hasJobList() && urlInterpreter.hasJob() && (request.getMethod().equalsIgnoreCase("delete") || (request.getMethod().equalsIgnoreCase("post") && request.getParameter(UWSJob.PARAM_ACTION) != null && request.getParameter(UWSJob.PARAM_ACTION).equalsIgnoreCase(UWSJob.ACTION_DELETE))));
+		return urlInterpreter.hasJobList() && urlInterpreter.hasJob() && (request.getMethod().equalsIgnoreCase("delete") || (request.getMethod().equalsIgnoreCase("post") && UWSToolBox.hasParameter(UWSJob.PARAM_ACTION, UWSJob.ACTION_DELETE, request, false)));
 	}
 
 	/**
diff --git a/src/uws/service/actions/GetJobParam.java b/src/uws/service/actions/GetJobParam.java
index e261a92ff6772e40aaf0d8eb75b6f83373b64327..c82988774bb39007bb71b79f2c3feb480642b373 100644
--- a/src/uws/service/actions/GetJobParam.java
+++ b/src/uws/service/actions/GetJobParam.java
@@ -37,6 +37,7 @@ import uws.job.user.JobOwner;
 import uws.service.UWSService;
 import uws.service.UWSUrl;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.UploadFile;
 
 /**
  * <p>The "Get Job Parameter" action of a UWS.</p>
@@ -146,8 +147,25 @@ public class GetJobParam extends UWSAction {
 						input.close();
 				}
 			}
-		}
-		// DEFAULT CASE: Display the serialization of the selected UWS object:
+		}// REFERENCE FILE: Display the content of the uploaded file or redirect to the URL (if it is a URL):
+		else if (attributes[0].equalsIgnoreCase(UWSJob.PARAM_PARAMETERS) && attributes.length > 1 && job.getAdditionalParameterValue(attributes[1]) != null && job.getAdditionalParameterValue(attributes[1]) instanceof UploadFile){
+			UploadFile upl = (UploadFile)job.getAdditionalParameterValue(attributes[1]);
+			if (upl.getLocation().matches("^http(s)?://"))
+				uws.redirect(upl.getLocation(), request, user, getName(), response);
+			else{
+				InputStream input = null;
+				try{
+					input = uws.getFileManager().getUploadInput(upl);
+					UWSToolBox.write(input, upl.mimeType, upl.length, response);
+				}catch(IOException ioe){
+					getLogger().logUWS(LogLevel.ERROR, upl, "GET_PARAMETER", "Can not read the content of the uploaded file \"" + upl.paramName + "\" of the job \"" + job.getJobId() + "\"!", ioe);
+					throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Can not read the content of the uploaded file " + upl.paramName + " (job ID: " + job.getJobId() + ").");
+				}finally{
+					if (input != null)
+						input.close();
+				}
+			}
+		}// DEFAULT CASE: Display the serialization of the selected UWS object:
 		else{
 			// Write the value/content of the selected attribute:
 			UWSSerializer serializer = uws.getSerializer(request.getHeader("Accept"));
diff --git a/src/uws/service/actions/SetJobParam.java b/src/uws/service/actions/SetJobParam.java
index aa851fd6bc85857b59ccc793b3047c462857416d..d9f18821609dfde289c33cbf008b3e35d95f8c45 100644
--- a/src/uws/service/actions/SetJobParam.java
+++ b/src/uws/service/actions/SetJobParam.java
@@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import uws.UWSException;
+import uws.UWSToolBox;
 import uws.job.UWSJob;
 import uws.job.parameters.UWSParameters;
 import uws.job.user.JobOwner;
@@ -69,7 +70,7 @@ public class SetJobParam extends UWSAction {
 	 * Checks whether:
 	 * <ul>
 	 * 	<li>a job list name is specified in the given UWS URL <i>(<u>note:</u> by default, the existence of the jobs list is not checked)</i>,</li>
-	 * 	<li>a job ID is given in the UWS URL <i>(<u>note:</u> by default, the existence of the job is not checked)</i>,</li>
+	 * 	<li>a job ID is given in the UWS URL <i>(<u>note:</u> by default, the existence of the job is not yet checked)</i>,</li>
 	 * 	<li>if the HTTP method is HTTP-POST: there is exactly one attribute <b>and</b> at least one parameter</li>
 	 * 	<li>if the HTTP method is HTTP-PUT: there are at least two attributes ({@link UWSJob#PARAM_PARAMETERS}/{parameter_name}) <b>and</b> there are at least two parameters</li>
 	 * </ul>
@@ -78,7 +79,7 @@ public class SetJobParam extends UWSAction {
 	 */
 	@Override
 	public boolean match(UWSUrl urlInterpreter, JobOwner user, HttpServletRequest request) throws UWSException{
-		return (urlInterpreter.hasJobList() && urlInterpreter.hasJob() && ((request.getMethod().equalsIgnoreCase("post") && (!urlInterpreter.hasAttribute() || urlInterpreter.getAttributes().length == 1)) || (request.getMethod().equalsIgnoreCase("put") && urlInterpreter.getAttributes().length >= 2 && urlInterpreter.getAttributes()[0].equalsIgnoreCase(UWSJob.PARAM_PARAMETERS) && request.getParameter(urlInterpreter.getAttributes()[1]) != null)));
+		return (urlInterpreter.hasJobList() && urlInterpreter.hasJob() && ((request.getMethod().equalsIgnoreCase("post") && (!urlInterpreter.hasAttribute() || urlInterpreter.getAttributes().length == 1)) || (request.getMethod().equalsIgnoreCase("put") && urlInterpreter.getAttributes().length >= 2 && urlInterpreter.getAttributes()[0].equalsIgnoreCase(UWSJob.PARAM_PARAMETERS) && UWSToolBox.hasParameter(urlInterpreter.getAttributes()[1], request, false))));
 	}
 
 	/**
diff --git a/src/uws/service/actions/SetUWSParameter.java b/src/uws/service/actions/SetUWSParameter.java
new file mode 100644
index 0000000000000000000000000000000000000000..dab912fd6b6e90994d2d3eb94b3cedc1d703a999
--- /dev/null
+++ b/src/uws/service/actions/SetUWSParameter.java
@@ -0,0 +1,110 @@
+package uws.service.actions;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import uws.UWSException;
+import uws.UWSExceptionFactory;
+import uws.UWSToolBox;
+import uws.job.UWSJob;
+import uws.job.parameters.UWSParameters;
+import uws.job.user.JobOwner;
+import uws.service.UWSService;
+import uws.service.UWSUrl;
+
+/**
+ * <p>The UWS action which lets set the phase (RUN or ABORT), the execution duration and the destruction time of a job
+ * with a POST or PUT request on {job-id}/{uws-param}.</p>
+ * 
+ * <p><i><u>Note:</u> The corresponding name is {@link UWSAction#SET_UWS_PARAMETER}.</i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (11/2014)
+ * @since 4.1
+ */
+public class SetUWSParameter extends UWSAction {
+	private static final long serialVersionUID = 1L;
+
+	public SetUWSParameter(final UWSService u){
+		super(u);
+	}
+
+	/**
+	 * @see UWSAction#SET_UWS_PARAMETER
+	 * @see uws.service.actions.UWSAction#getName()
+	 */
+	@Override
+	public String getName(){
+		return SET_UWS_PARAMETER;
+	}
+
+	@Override
+	public String getDescription(){
+		return "Let change one of the standard UWS parameters of a job (e.g. phase, executionduration, destruction) (URL: {baseUWS_URL}/{jobListName}/{jobId}/{uws-param}, where {uws-param} = \"phase\" or \"executionduration\" or \"destruction\", Method: HTTP-POST or HTTP-PUT, Parameter: \"{uws-param}={param-value}\" in POST and \"{param-value\" in PUT (content-type:text/plain))";
+	}
+
+	/**
+	 * Checks whether:
+	 * <ul>
+	 * 	<li>a job list name is specified in the given UWS URL <i>(<u>note:</u> by default, the existence of the jobs list is not checked)</i>,</li>
+	 * 	<li>a job ID is given in the UWS URL <i>(<u>note:</u> by default, the existence of the job is not yet checked)</i>,</li>
+	 * 	<li>the job attribute "phase", "runID", "executionduration" or "destruction" is used in the UWS URL,
+	 * 	<li>the HTTP method is HTTP-POST or HTTP-PUT.</li>
+	 * </ul>
+	 * @see uws.service.actions.UWSAction#match(uws.service.UWSUrl, java.lang.String, javax.servlet.http.HttpServletRequest)
+	 */
+	@Override
+	public boolean match(UWSUrl urlInterpreter, JobOwner user, HttpServletRequest request) throws UWSException{
+		return (urlInterpreter.hasJobList() && urlInterpreter.hasJob() && urlInterpreter.getAttributes().length == 1 && urlInterpreter.getAttributes()[0].toLowerCase().matches(UWSParameters.UWS_RW_PARAMETERS_REGEXP) && (request.getMethod().equalsIgnoreCase("post") || request.getMethod().equalsIgnoreCase("put")) && UWSToolBox.hasParameter(urlInterpreter.getAttributes()[0], request, false));
+	}
+
+	/**
+	 * Get the specified job <i>(throw an error if not found)</i>,
+	 * and update the specified UWS standard parameter.
+	 * 
+	 * @see #getJob(UWSUrl)
+	 * @see UWSJob#addOrUpdateParameter(String, Object)
+	 * @see UWSService#redirect(String, HttpServletRequest, JobOwner, String, HttpServletResponse)
+	 * 
+	 * @see uws.service.actions.UWSAction#apply(uws.service.UWSUrl, java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+	 */
+	@Override
+	public boolean apply(UWSUrl urlInterpreter, JobOwner user, HttpServletRequest request, HttpServletResponse response) throws UWSException, IOException{
+		// Get the job:
+		UWSJob job = getJob(urlInterpreter);
+
+		// Forbids the action if the user has not the WRITE permission for the specified job:
+		if (user != null && !user.hasWritePermission(job))
+			throw new UWSException(UWSException.PERMISSION_DENIED, UWSExceptionFactory.writePermissionDenied(user, true, job.getJobId()));
+
+		String name = urlInterpreter.getAttributes()[0];
+		job.addOrUpdateParameter(name, UWSToolBox.getParameter(name, request, false), user);
+
+		// Make a redirection to the job:
+		uws.redirect(urlInterpreter.jobSummary(urlInterpreter.getJobListName(), job.getJobId()).getRequestURL(), request, user, getName(), response);
+
+		return true;
+	}
+
+}
diff --git a/src/uws/service/actions/UWSAction.java b/src/uws/service/actions/UWSAction.java
index 7de64b9f1dc8a1cb70b5b5e5743e6da9fd13457d..ed78cfbe3d3f7472d6d16b1aa3ed29777b3052cd 100644
--- a/src/uws/service/actions/UWSAction.java
+++ b/src/uws/service/actions/UWSAction.java
@@ -42,7 +42,7 @@ import uws.service.log.UWSLog;
  * By default the name of a UWS action is the full java name of the class !</b></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (08/2014)
+ * @version 4.1 (11/2014)
  * 
  * @see UWSService
  */
@@ -53,6 +53,9 @@ public abstract class UWSAction implements Serializable {
 	public final static String LIST_JOBS = "List Jobs";
 	/** Name of the UWS action {@link AddJob}. */
 	public final static String ADD_JOB = "Add Job";
+	/** Name of the UWS action {@link SetUWSParameter}.
+	 * @since 4.1 */
+	public final static String SET_UWS_PARAMETER = "Set UWS Parameter";
 	/** Name of the UWS action {@link DestroyJob}. */
 	public final static String DESTROY_JOB = "Destroy Job";
 	/** Name of the UWS action {@link JobSummary}. */
diff --git a/src/uws/service/backup/DefaultUWSBackupManager.java b/src/uws/service/backup/DefaultUWSBackupManager.java
index f63c1f035c10712c1199ad265804ea682adf05d6..6b198bdc4cb4c1f3323178b9ea9fef8ceae1b679 100644
--- a/src/uws/service/backup/DefaultUWSBackupManager.java
+++ b/src/uws/service/backup/DefaultUWSBackupManager.java
@@ -56,6 +56,7 @@ import uws.service.UWS;
 import uws.service.file.UWSFileManager;
 import uws.service.log.UWSLog;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.UploadFile;
 
 /**
  * <p>Default implementation of the interface {@link UWSBackupManager}.</p>
@@ -77,7 +78,7 @@ import uws.service.log.UWSLog.LogLevel;
  * <p>Another positive value will be considered as the frequency (in milliseconds) of the automatic backup (= {@link #saveAll()}).</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (12/2014)
  */
 public class DefaultUWSBackupManager implements UWSBackupManager {
 
@@ -538,11 +539,65 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 	 */
 	protected JSONObject getJSONJob(final UWSJob job, final String jlName) throws UWSException, JSONException{
 		JSONObject jsonJob = Json4Uws.getJson(job);
+
+		// Re-Build the parameters map, by separating the uploads and the "normal" parameters:
+		JSONArray uploads = new JSONArray();
+		JSONObject params = new JSONObject();
+		Object val;
+		for(String name : job.getAdditionalParameters()){
+			// get the raw value:
+			val = job.getAdditionalParameterValue(name);
+			// if an array, build a JSON array of strings:
+			if (val != null && val.getClass().isArray()){
+				JSONArray array = new JSONArray();
+				for(Object o : (Object[])val){
+					if (o != null)
+						array.put(o.toString());
+				}
+				params.put(name, array);
+			}else if (val != null && val instanceof UploadFile)
+				uploads.put(getUploadJson((UploadFile)val));
+			// otherwise, just put the value:
+			else if (val != null)
+				params.put(name, val);
+		}
+
+		// Add the parameters and the uploads inside the JSON representation of the job:
+		jsonJob.put(UWSJob.PARAM_PARAMETERS, params);
+		jsonJob.put("uwsUploads", uploads);
+
+		// Add the job owner:
 		jsonJob.put(UWSJob.PARAM_OWNER, (job != null && job.getOwner() != null) ? job.getOwner().getID() : null);
+
+		// Add the name of the job list owning the given job:
 		jsonJob.put("jobListName", jlName);
+
 		return jsonJob;
 	}
 
+	/**
+	 * Get the JSON representation of the given {@link UploadFile}.
+	 * 
+	 * @param upl	The uploaded file to serialize in JSON.
+	 * 
+	 * @return		Its JSON representation.
+	 * 
+	 * @throws JSONException	If there is an error while building the JSON object.
+	 * 
+	 * @since 4.1
+	 */
+	protected JSONObject getUploadJson(final UploadFile upl) throws JSONException{
+		if (upl == null)
+			return null;
+		JSONObject o = new JSONObject();
+		o.put("paramName", upl.paramName);
+		o.put("fileName", upl.fileName);
+		o.put("location", upl.getLocation());
+		o.put("mime", upl.mimeType);
+		o.put("lenght", upl.length);
+		return o;
+	}
+
 	/* ******************* */
 	/* RESTORATION METHODS */
 	/* ******************* */
@@ -768,6 +823,7 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 		//Map<String, Object> params = null;
 		ArrayList<Result> results = null;
 		ErrorSummary error = null;
+		JSONArray uploads = null;
 
 		String[] keys = JSONObject.getNames(json);
 		for(String key : keys){
@@ -835,6 +891,10 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 				else if (key.equalsIgnoreCase(UWSJob.PARAM_PARAMETERS))
 					inputParams.put(UWSJob.PARAM_PARAMETERS, getParameters(json.getJSONObject(key)));
 
+				// key=uwsUploads:
+				else if (key.equalsIgnoreCase("uwsUploads"))
+					uploads = json.getJSONArray(key);
+
 				// key=RESULTS:
 				else if (key.equalsIgnoreCase(UWSJob.PARAM_RESULTS))
 					results = getResults(json.getJSONArray(key));
@@ -852,6 +912,22 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 			}
 		}
 
+		// Re-Build all the uploaded files' pointers for this job:
+		if (uploads != null){
+			@SuppressWarnings("unchecked")
+			Map<String,Object> params = (Map<String,Object>)(inputParams.get(UWSJob.PARAM_PARAMETERS));
+			UploadFile upl;
+			try{
+				for(int i = 0; i < uploads.length(); i++){
+					upl = getUploadFile(uploads.getJSONObject(i));;
+					if (upl != null)
+						params.put(upl.paramName, upl);
+				}
+			}catch(JSONException je){
+				getLogger().logUWS(LogLevel.ERROR, json, "RESTORATION", "Incorrect JSON format for the serialization of the job \"" + jobId + "\" (attribute: \"uwsUploads\")!", je);
+			}
+		}
+
 		// The job list name is REQUIRED:
 		if (jobListName == null || jobListName.isEmpty())
 			getLogger().logUWS(LogLevel.ERROR, json, "RESTORATION", "Missing job list name! => Can not restore the job " + jobId + "!", null);
@@ -923,10 +999,8 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 	 * 
 	 * @return					The corresponding list of parameters
 	 * 							or <i>null</i> if the given object is empty.
-	 * 
-	 * @throws UWSException
 	 */
-	protected Map<String,Object> getParameters(final JSONObject obj) throws UWSException{
+	protected Map<String,Object> getParameters(final JSONObject obj){
 		if (obj == null || obj.length() == 0)
 			return null;
 
@@ -942,6 +1016,31 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 		return params;
 	}
 
+	/**
+	 * Build the upload file corresponding to the given JSON object.
+	 * 
+	 * @param obj	The JSON representation of the {@link UploadFile} to get.
+	 * 
+	 * @return		The corresponding {@link UploadFile}.
+	 * 
+	 * @since 4.1
+	 */
+	protected UploadFile getUploadFile(final JSONObject obj){
+		try{
+			UploadFile upl = new UploadFile(obj.getString("paramName"), (obj.has("fileName") ? obj.getString("fileName") : null), obj.getString("location"), uws.getFileManager());
+			if (obj.has("mime"))
+				upl.mimeType = obj.getString("mime");
+			try{
+				if (obj.has("length"))
+					upl.length = Long.parseLong(obj.getString("length"));
+			}catch(NumberFormatException ex){}
+			return upl;
+		}catch(JSONException je){
+			getLogger().logUWS(LogLevel.ERROR, obj, "RESTORATION", "Incorrect JSON format for the serialization of an uploaded file!", je);
+			return null;
+		}
+	}
+
 	/**
 	 * Builds the list of results corresponding to the given JSON array.
 	 * 
@@ -1050,7 +1149,7 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 			}
 		}
 		if (message != null)
-			return new ErrorSummary(message, ErrorType.valueOf(type), details);
+			return new ErrorSummary(message, ErrorType.valueOf(type.toUpperCase()), details);
 		else
 			return null;
 	}
diff --git a/src/uws/service/file/LocalUWSFileManager.java b/src/uws/service/file/LocalUWSFileManager.java
index d67f01384ebf549fa3f4ad80c30eca75f1fbe00f..4aa9fd681b8c64f743486ff63c20d3619067ec37 100644
--- a/src/uws/service/file/LocalUWSFileManager.java
+++ b/src/uws/service/file/LocalUWSFileManager.java
@@ -20,6 +20,8 @@ package uws.service.file;
  *                       Astronomisches Rechen Institut (ARI)
  */
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileFilter;
 import java.io.FileInputStream;
@@ -29,6 +31,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -43,6 +47,7 @@ import uws.job.Result;
 import uws.job.UWSJob;
 import uws.job.user.JobOwner;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.UploadFile;
 
 /**
  * <p>All UWS files are stored in the local machine into the specified directory.</p>
@@ -64,7 +69,7 @@ import uws.service.log.UWSLog.LogLevel;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (12/2014)
  */
 public class LocalUWSFileManager implements UWSFileManager {
 
@@ -403,6 +408,120 @@ public class LocalUWSFileManager implements UWSFileManager {
 		out.flush();
 	}
 
+	/* ************************* */
+	/* UPLOADED FILES MANAGEMENT */
+	/* ************************* */
+
+	/**
+	 * Create a File instance from the given upload file description.
+	 * This function is able to deal with location as URI and as file path. 
+	 * 
+	 * @param upload	Description of an uploaded file.
+	 * 
+	 * @return	The corresponding File object.
+	 * 
+	 * @since 4.1
+	 */
+	protected final File getFile(final UploadFile upload){
+		if (upload.getLocation().startsWith("file:")){
+			try{
+				return new File(new URI(upload.getLocation()));
+			}catch(URISyntaxException use){
+				return new File(upload.getLocation());
+			}
+		}else
+			return new File(upload.getLocation());
+	}
+
+	@Override
+	public InputStream getUploadInput(final UploadFile upload) throws IOException{
+		// Check the source file:
+		File source = getFile(upload);
+		if (!source.exists())
+			throw new FileNotFoundException("The uploaded file submitted with the parameter \"" + upload.paramName + "\" can not be found any more on the server!");
+		// Return the stream:
+		return new FileInputStream(source);
+	}
+
+	@Override
+	public InputStream openURI(final URI uri) throws UnsupportedURIProtocolException, IOException{
+		String scheme = uri.getScheme();
+		if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("ftp"))
+			return uri.toURL().openStream();
+		else
+			throw new UnsupportedURIProtocolException(uri);
+	}
+
+	@Override
+	public void deleteUpload(final UploadFile upload) throws IOException{
+		File f = getFile(upload);
+		if (!f.exists())
+			return;
+		else if (f.isDirectory())
+			throw new IOException("Incorrect location! An uploaded file must be a regular file, not a directory. (file location: \"" + f.getPath() + "\")");
+		else{
+			try{
+				if (!f.delete())
+					throw new IOException("Can not delete the file!");
+			}catch(SecurityException se){
+				throw new IOException("Unexpected permission restriction on the uploaded file \"" + f.getPath() + "\" => can not delete it!");
+			}
+		}
+	}
+
+	@Override
+	public String moveUpload(final UploadFile upload, final UWSJob destination) throws IOException{
+		// Check the source file:
+		File source = getFile(upload);
+		if (!source.exists())
+			throw new FileNotFoundException("The uploaded file submitted with the parameter \"" + upload.paramName + "\" can not be found any more on the server!");
+
+		// Build the final location (in the owner directory, under the name "UPLOAD_{job-id}_{param-name}":
+		File ownerDir = getOwnerDirectory(destination.getOwner());
+		File copy = new File(ownerDir, "UPLOAD_" + destination.getJobId() + "_" + upload.paramName);
+
+		OutputStream output = null;
+		InputStream input = null;
+		boolean done = false;
+		try{
+			// open the input and output:
+			input = new BufferedInputStream(getUploadInput(upload));
+			output = new BufferedOutputStream(new FileOutputStream(copy));
+			// proceed to the copy:
+			byte[] buffer = new byte[2048];
+			int len;
+			while((len = input.read(buffer)) > 0)
+				output.write(buffer, 0, len);
+			output.flush();
+			output.close();
+			output = null;
+			// close the input and delete the source file:
+			input.close();
+			input = null;
+			source.delete();
+			// return the new location:
+			done = true;
+			return copy.toURI().toString();
+		}finally{
+			if (output != null){
+				try{
+					output.close();
+				}catch(IOException ioe){}
+			}
+			if (input != null){
+				try{
+					input.close();
+				}catch(IOException ioe){}
+			}
+			// In case of problem, the copy must be deleted:
+			if (!done && copy.exists()){
+				try{
+					copy.delete();
+				}catch(SecurityException ioe){}
+			}
+		}
+	}
+
 	/* *********************** */
 	/* RESULT FILES MANAGEMENT */
 	/* *********************** */
diff --git a/src/uws/service/file/UWSFileManager.java b/src/uws/service/file/UWSFileManager.java
index 2de7431c268ae9d91110b0a2bf402672b6439588..8dbc7f7471c29141f1be524ec4d321968769c1e5 100644
--- a/src/uws/service/file/UWSFileManager.java
+++ b/src/uws/service/file/UWSFileManager.java
@@ -20,10 +20,13 @@ package uws.service.file;
  *                       Astronomisches Rechen Institut (ARI)
  */
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URL;
 import java.util.Iterator;
 
 import uws.job.ErrorSummary;
@@ -31,6 +34,7 @@ import uws.job.Result;
 import uws.job.UWSJob;
 import uws.job.user.JobOwner;
 import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.UploadFile;
 
 /**
  * <p>Lets accessing any file managed by a UWS service.</p>
@@ -41,7 +45,7 @@ import uws.service.log.UWSLog.LogLevel;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (08/2014)
+ * @version 4.1 (11/2014)
  * 
  * @see LocalUWSFileManager
  */
@@ -76,6 +80,95 @@ public interface UWSFileManager {
 	 */
 	public PrintWriter getLogOutput(final LogLevel level, final String context) throws IOException;
 
+	/* ************************* */
+	/* UPLOADED FILES MANAGEMENT */
+	/* ************************* */
+
+	/** <p>Temporary directory in which uploaded files will be stored when parsing the HTTP request.</p>
+	 * <p><i>IMPORTANT 1:
+	 * 	Uploaded files should be then moved using {@link UploadFile#move(UWSJob)} when the job creation or update is validated.
+	 * </i></p>
+	 * <p><i>IMPORTANT 2:
+	 * 	As qualified above, this directory is <b>temporary</b>. It means that it should be emptied sometimes.
+	 * 	It is particularly important because when a delete or move operation fails on uploaded files, no log or error might
+	 * 	be published.
+	 * </i></p>
+	 * <p><i>Note:
+	 * 	The default value is the temporary directory of the system (i.e. \tmp or \var\tmp on Unix/Linux/MacOS, c:\temp on Windows).
+	 * </i></p>
+	 * @since 4.1 */
+	public static File TMP_UPLOAD_DIR = new File(System.getProperty("java.io.tmpdir"));
+
+	/**
+	 * Open a stream toward the specified file, submitted inline in an HTTP request.
+	 * 
+	 * @param upload	Description of the uploaded file.
+	 * 
+	 * @return	Input to the specified uploaded file.
+	 * 
+	 * @throws IOException	If any error occurs while opening the stream.
+	 * 
+	 * @since 4.1
+	 */
+	public InputStream getUploadInput(final UploadFile upload) throws IOException;
+
+	/**
+	 * <p>Open a stream toward the given URI.</p>
+	 * 
+	 * <p>
+	 * 	Most of the time, the given URI uses the protocol http, https or ftp, which makes
+	 * 	the URI perfectly understandable by {@link URL} which is then able to open easily
+	 * 	a stream (cf {@link URL#openStream()}). However, a different scheme/protocol could
+	 * 	be used ; particularly VO ones like "ivo" and "vos". It is for these particular
+	 * 	cases that this function has been designed: in order to provide an implementation
+	 * 	supporting additional protocols.
+	 * </p>
+	 * 
+	 * @param uri	URI of any resource to read.
+	 * 
+	 * @return	Input to the specified resource.
+	 * 
+	 * @throws UnsupporteURIProtocol	If the protocol is not supported by this implementation.
+	 * @throws IOException				If another error occurs while opening the stream.
+	 * 
+	 * @since 4.1
+	 */
+	public InputStream openURI(final URI uri) throws UnsupportedURIProtocolException, IOException;
+
+	/**
+	 * Delete definitely the specified file, submitted inline in an HTTP request.
+	 * 
+	 * @param upload	Description of the uploaded file.
+	 * 
+	 * @throws IOException	If any error occurs while deleting the file.
+	 * 
+	 * @since 4.1
+	 */
+	public void deleteUpload(final UploadFile upload) throws IOException;
+
+	/**
+	 * <p>Move the specified file from its current location to a location related to the given job.</p>
+	 * 
+	 * <p><i>Note:
+	 * 	This function is generally used only once: after the HTTP request parsing, when creating or updating a job and only if the action has been accepted.
+	 * </i></p>
+	 * 
+	 * <p><b>IMPORTANT:
+	 * 	This function might not be able to update the location inside the given {@link UploadFile}. For this reason,
+	 * 	it is strongly recommended to not call directly this function, but to use {@link UploadFile#move(UWSJob)}.
+	 * </b></p>
+	 * 
+	 * @param upload		Description of the uploaded file to move.
+	 * @param destination	Job in which the uploaded file will be used.
+	 * 
+	 * @return	The new location of the uploaded file.
+	 * 
+	 * @throws IOException	If any error occurs while moving the file.
+	 * 
+	 * @since 4.1
+	 */
+	public String moveUpload(final UploadFile upload, final UWSJob destination) throws IOException;
+
 	/* *********************** */
 	/* RESULT FILES MANAGEMENT */
 	/* *********************** */
diff --git a/src/uws/service/file/UnsupportedURIProtocolException.java b/src/uws/service/file/UnsupportedURIProtocolException.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc6a2da89862ef00d0a182a2c5822763a6a77623
--- /dev/null
+++ b/src/uws/service/file/UnsupportedURIProtocolException.java
@@ -0,0 +1,45 @@
+package uws.service.file;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.net.URI;
+
+import uws.UWSException;
+
+/**
+ * Error sent when trying to read a remote file using a URI whose the scheme/protocol is not supported.
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (11/2014)
+ * @since 4.1
+ */
+public class UnsupportedURIProtocolException extends UWSException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Build an {@link UnsupportedURIProtocolException}.
+	 * 
+	 * @param uri	The URI whose the scheme/protocol is incorrect.
+	 */
+	public UnsupportedURIProtocolException(final URI uri){
+		super(UWSException.BAD_REQUEST, "Unsupported protocol: \"" + (uri != null ? uri.getScheme() : "") + "\"! => can not open the resource \"" + uri + "\".");
+	}
+
+}
diff --git a/src/uws/service/log/UWSLog.java b/src/uws/service/log/UWSLog.java
index 11cfa0aa412bc7671dfea24c8ff037491b88a12a..c0771f624d0613eb8dd0fac6bbdbbe49c024b7cd 100644
--- a/src/uws/service/log/UWSLog.java
+++ b/src/uws/service/log/UWSLog.java
@@ -38,7 +38,7 @@ import uws.service.UWSUrl;
  * Let log any kind of message about a UWS service.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.1 (12/2014)
  */
 public interface UWSLog {
 
@@ -263,6 +263,7 @@ public interface UWSLog {
 	 * 	<li>NOTIFY</li>
 	 * 	<li>END</li>
 	 * 	<li>SERIALIZE</li>
+	 * 	<li>MOVE_UPLOAD</li>
 	 * 	<li>ADD_RESULT</li>
 	 * 	<li>SET_DESTRUCTION</li>
 	 * 	<li>SET_ERROR</li>
diff --git a/src/uws/service/request/FormEncodedParser.java b/src/uws/service/request/FormEncodedParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9882d3a2439a924e03b051737bd1b0b7ec6540a
--- /dev/null
+++ b/src/uws/service/request/FormEncodedParser.java
@@ -0,0 +1,178 @@
+package uws.service.request;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import javax.servlet.http.HttpServletRequest;
+
+import uws.UWSException;
+
+/**
+ * <p>Extract parameters encoded using the HTTP-GET method or the Content-type application/x-www-form-urlencoded
+ * with the HTTP-POST or HTTP-PUT method in an {@link HttpServletRequest}.</p>
+ * 
+ * <p>
+ * 	By default, this {@link RequestParser} overwrite parameter occurrences in the map: that's to say if a parameter is provided several times,
+ * 	only the last value will be kept. This behavior can be changed by overwriting the function {@link #consumeParameter(String, Object, Map)}
+ * 	of this class.
+ * </p>
+ * 
+ * <p><i>Note:
+ * 	When HTTP-POST is used, these parameters are actually already extracted by the server application (like Apache/Tomcat)
+ * 	and are available with {@link HttpServletRequest#getParameterMap()}.
+ * 	However, when using HTTP-PUT, the parameters are extracted manually from the request content.
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (11/2014)
+ * @since 4.1
+ */
+public class FormEncodedParser implements RequestParser {
+
+	/** HTTP content-type for HTTP request formated in url-form-encoded. */
+	public final static String EXPECTED_CONTENT_TYPE = "application/x-www-form-urlencoded";
+
+	@Override
+	public final Map<String,Object> parse(HttpServletRequest request) throws UWSException{
+		if (request == null)
+			return new HashMap<String,Object>();
+
+		HashMap<String,Object> params = new HashMap<String,Object>();
+
+		// Normal extraction for HTTP-POST and other HTTP methods:
+		if (request.getMethod() == null || !request.getMethod().equalsIgnoreCase("put")){
+			Enumeration<String> names = request.getParameterNames();
+			String paramName;
+			String[] values;
+			int i;
+			while(names.hasMoreElements()){
+				paramName = names.nextElement();
+				values = request.getParameterValues(paramName);
+				// search for the last non-null occurrence:
+				i = values.length - 1;
+				while(i >= 0 && values[i] == null)
+					i--;
+				// if there is one, keep it:
+				if (i >= 0)
+					consumeParameter(paramName, values[i], params);
+			}
+		}
+		/* Parameters are not extracted when using the HTTP-PUT method.
+		 * This block is doing this extraction manually. */
+		else{
+			InputStream input = null;
+			try{
+
+				// Get the character encoding:
+				String charEncoding = request.getCharacterEncoding();
+				try{
+					if (charEncoding == null || charEncoding.trim().length() == 0 || Charset.isSupported(charEncoding))
+						charEncoding = "UTF-8";
+				}catch(Exception ex){
+					charEncoding = "UTF-8";
+				}
+
+				// Get a stream on the request content:
+				input = new BufferedInputStream(request.getInputStream());
+				// Read the stream by iterating on each parameter pairs:
+				Scanner scanner = new Scanner(input);
+				scanner.useDelimiter("&");
+				String pair;
+				int indSep;
+				while(scanner.hasNext()){
+					// get the pair:
+					pair = scanner.next();
+					// split it between the parameter name and value:
+					indSep = pair.indexOf('=');
+					try{
+						if (indSep >= 0)
+							consumeParameter(URLDecoder.decode(pair.substring(0, indSep), charEncoding), URLDecoder.decode(pair.substring(indSep + 1), charEncoding), params);
+						else
+							consumeParameter(URLDecoder.decode(pair, charEncoding), "", params);
+					}catch(UnsupportedEncodingException uee){
+						if (indSep >= 0)
+							consumeParameter(pair.substring(0, indSep), pair.substring(indSep + 1), params);
+						else
+							consumeParameter(pair, "", params);
+					}
+				}
+
+			}catch(IOException ioe){}finally{
+				if (input != null){
+					try{
+						input.close();
+					}catch(IOException ioe2){}
+				}
+			}
+		}
+
+		return params;
+	}
+
+	/**
+	 * <p>Consume the specified parameter: add it inside the given map.</p>
+	 * 
+	 * <p>
+	 * 	By default, this function is just putting the given value inside the map. So, if the parameter already exists in the map,
+	 * 	its old value will be overwritten by the given one.
+	 * </p>
+	 * 
+	 * @param name		Name of the parameter to consume.
+	 * @param value		Its value.
+	 * @param allParams	The list of all parameters read until now.
+	 */
+	protected void consumeParameter(final String name, final Object value, final Map<String,Object> allParams){
+		allParams.put(name, value);
+	}
+
+	/**
+	 * <p>Utility method that determines whether the content of the given request is a application/x-www-form-urlencoded.</p>
+	 * 
+	 * <p><i>Important:
+	 * 	This function just test the content-type of the request. The HTTP method (e.g. GET, POST, ...) is not tested.
+	 * </i></p>
+	 *
+	 * @param request The servlet request to be evaluated. Must be non-null.
+	 *
+	 * @return	<i>true</i> if the request is url-form-encoded,
+	 *        	<i>false</i> otherwise.
+	 */
+	public final static boolean isFormEncodedRequest(final HttpServletRequest request){
+		// Extract the content type and determine if it is a url-form-encoded request:
+		String contentType = request.getContentType();
+		if (contentType == null)
+			return false;
+		else if (contentType.toLowerCase().equals(EXPECTED_CONTENT_TYPE))
+			return true;
+		else
+			return false;
+	}
+
+}
diff --git a/src/uws/service/request/MultipartParser.java b/src/uws/service/request/MultipartParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d4fc6a6b11581241a6fb43989bc386f1ac15e34
--- /dev/null
+++ b/src/uws/service/request/MultipartParser.java
@@ -0,0 +1,248 @@
+package uws.service.request;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import uws.UWSException;
+import uws.service.UWS;
+import uws.service.file.UWSFileManager;
+
+import com.oreilly.servlet.MultipartRequest;
+import com.oreilly.servlet.multipart.FileRenamePolicy;
+
+/**
+ * <p>Extract parameters encoded using the Content-type multipart/form-data
+ * in an {@link HttpServletRequest}.</p>
+ * 
+ * <p>
+ * 	The created file(s) is(are) stored in the temporary upload directory ({@link UWSFileManager#TMP_UPLOAD_DIR} ; this attribute can be modified if needed).
+ * 	This directory is supposed to be emptied regularly in case it is forgotten at any moment by the UWS service implementation to delete unused request files.
+ * </p>
+ * 
+ * <p>
+ * 	The size of the full request body is limited by the static attribute {@link #SIZE_LIMIT} before the creation of the file.
+ * 	Its default value is: {@link #DEFAULT_SIZE_LIMIT}={@value #DEFAULT_SIZE_LIMIT} bytes.
+ * </p>
+ * 
+ * <p>
+ * 	By default, this {@link RequestParser} overwrite parameter occurrences in the map: that's to say if a parameter is provided several times,
+ * 	only the last value will be kept. This behavior can be changed by overwriting the function {@link #consumeParameter(String, Object, Map)}
+ * 	of this class.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (12/2014)
+ * @since 4.1
+ */
+public class MultipartParser implements RequestParser {
+
+	/** HTTP content-type for HTTP request formated in multipart. */
+	public static final String EXPECTED_CONTENT_TYPE = "multipart/form-data";
+
+	/** Default maximum allowed size for an HTTP request content: 10 MiB. */
+	public static final int DEFAULT_SIZE_LIMIT = 10 * 1024 * 1024;
+
+	/** <p>Maximum allowed size for an HTTP request content. Over this limit, an exception is thrown and the request is aborted.</p>
+	 * <p><i>Note:
+	 * 	The default value is {@link #DEFAULT_SIZE_LIMIT} (= {@value #DEFAULT_SIZE_LIMIT} MiB).
+	 * </i></p>
+	 * <p><i>Note:
+	 * 	This limit is expressed in bytes and can not be negative.
+	 *  Its smallest possible value is 0. If the set value is though negative,
+	 *  it will be ignored and {@link #DEFAULT_SIZE_LIMIT} will be used instead.
+	 * </i></p> */
+	public static int SIZE_LIMIT = DEFAULT_SIZE_LIMIT;
+
+	/** Indicates whether this parser should allow inline files or not. */
+	public final boolean allowUpload;
+
+	/** File manager to use to create {@link UploadFile} instances.
+	 * It is required by this new object to execute open, move and delete operations whenever it could be asked. */
+	protected final UWSFileManager fileManager;
+
+	/**
+	 * <p>Build a {@link MultipartParser} forbidding uploads (i.e. inline files).</p>
+	 * 
+	 * <p>
+	 * 	With this parser, when an upload (i.e. submitted inline files) is detected, an exception is thrown by {@link #parse(HttpServletRequest)}
+	 * 	which cancels immediately the request.
+	 * </p>
+	 */
+	public MultipartParser(){
+		this(false, null);
+	}
+
+	/**
+	 * Build a {@link MultipartParser} allowing uploads (i.e. inline files).
+	 * 
+	 * @param fileManager	The file manager to use in order to store any eventual upload. <b>MUST NOT be NULL</b>
+	 */
+	public MultipartParser(final UWSFileManager fileManager){
+		this(true, fileManager);
+	}
+
+	/**
+	 * <p>Build a {@link MultipartParser}.</p>
+	 * 
+	 * <p>
+	 * 	If the first parameter is <i>false</i>, then when an upload (i.e. submitted inline files) is detected, an exception is thrown
+	 * 	by {@link #parse(HttpServletRequest)} which cancels immediately the request.
+	 * </p>
+	 * 
+	 * @param uploadEnabled					<i>true</i> to allow uploads (i.e. inline files), <i>false</i> otherwise.
+	 *                     					If <i>false</i>, the two other parameters are useless.
+	 * @param fileManager					The file manager to use in order to store any eventual upload. <b>MUST NOT be NULL</b>
+	 */
+	protected MultipartParser(final boolean uploadEnabled, final UWSFileManager fileManager){
+		if (uploadEnabled && fileManager == null)
+			throw new NullPointerException("Missing file manager although the upload capability is enabled => can not create a MultipartParser!");
+
+		this.allowUpload = uploadEnabled;
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public final Map<String,Object> parse(final HttpServletRequest request) throws UWSException{
+		LinkedHashMap<String,Object> parameters = new LinkedHashMap<String,Object>();
+		MultipartRequest multipart = null;
+
+		try{
+
+			// Parse the request body:
+			multipart = new MultipartRequest(request, UWSFileManager.TMP_UPLOAD_DIR.getPath(), (SIZE_LIMIT < 0 ? DEFAULT_SIZE_LIMIT : SIZE_LIMIT), new FileRenamePolicy(){
+				@Override
+				public File rename(File file){
+					Object reqID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID);
+					if (reqID == null || !(reqID instanceof String))
+						reqID = (new Date()).getTime();
+					char uniq = 'A';
+					File f = new File(file.getParentFile(), "UPLOAD_" + reqID + uniq + "_" + file.getName());
+					while(f.exists()){
+						uniq++;
+						f = new File(file.getParentFile(), "UPLOAD_" + reqID + "_" + file.getName());
+					}
+					return f;
+				}
+			});
+
+			// Extract all "normal" parameters:
+			String param;
+			Enumeration<String> e = multipart.getParameterNames();
+			while(e.hasMoreElements()){
+				param = e.nextElement();
+				for(String occurence : multipart.getParameterValues(param))
+					consumeParameter(param, occurence, parameters);
+			}
+
+			// Extract all inline files as additional parameters:
+			e = multipart.getFileNames();
+			if (!allowUpload && e.hasMoreElements())
+				throw new UWSException(UWSException.BAD_REQUEST, "Uploads are not allowed by this service!");
+			while(e.hasMoreElements()){
+				param = e.nextElement();
+
+				/*
+				 * TODO !!!POSSIBLE ISSUE!!!
+				 * MultipartRequest is not able to deal with multiple files having the same parameter name. However, all files are created/uploaded
+				 * but only the last one is accessible through this object....so only the last can be deleted, which could be a problem later
+				 * (hence the usage of the system temporary directory).
+				 */
+
+				// build its description/pointer:
+				UploadFile lob = new UploadFile(param, multipart.getOriginalFileName(param), multipart.getFile(param).toURI().toString(), fileManager);
+				lob.mimeType = multipart.getContentType(param);
+				lob.length = multipart.getFile(param).length();
+				// add it inside the parameters map:
+				consumeParameter(param, lob, parameters);
+			}
+
+		}catch(IOException ioe){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Internal Error => Impossible to extract parameters from the Multipart HTTP request!");
+		}catch(IllegalArgumentException iae){
+			String confError = iae.getMessage();
+			if (UWSFileManager.TMP_UPLOAD_DIR == null)
+				confError = "Missing upload directory!";
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, iae, "Internal Error: Incorrect UPLOAD configuration: " + confError);
+		}
+
+		return parameters;
+	}
+
+	/**
+	 * <p>Consume the specified parameter: add it inside the given map.</p>
+	 * 
+	 * <p>
+	 * 	By default, this function is just putting the given value inside the map. So, if the parameter already exists in the map,
+	 * 	its old value will be overwritten by the given one.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	If the old value was a file, it will be deleted from the file system before its replacement in the map.
+	 * </i></p>
+	 * 
+	 * @param name		Name of the parameter to consume.
+	 * @param value		Its value.
+	 * @param allParams	The list of all parameters read until now.
+	 */
+	protected void consumeParameter(final String name, final Object value, final Map<String,Object> allParams){
+		// If the old value was a file, delete it before replacing its value:
+		if (allParams.containsKey(name) && allParams.get(name) instanceof UploadFile){
+			try{
+				((UploadFile)allParams.get(name)).deleteFile();
+			}catch(IOException ioe){}
+		}
+
+		// Put the given value in the given map:
+		allParams.put(name, value);
+	}
+
+	/**
+	 * <p>Utility method that determines whether the content of the given request is a multipart/form-data.</p>
+	 * 
+	 * <p><i>Important:
+	 * 	This function just test the content-type of the request. The HTTP method (e.g. GET, POST, ...) is not tested.
+	 * </i></p>
+	 *
+	 * @param request The servlet request to be evaluated. Must be non-null.
+	 *
+	 * @return	<i>true</i> if the request is multipart,
+	 *        	<i>false</i> otherwise.
+	 */
+	public static final boolean isMultipartContent(final HttpServletRequest request){
+		// Extract the content type and determine if it is a multipart request (its content type should start by multipart/form-data"):
+		String contentType = request.getContentType();
+		if (contentType == null)
+			return false;
+		else if (contentType.toLowerCase().startsWith(EXPECTED_CONTENT_TYPE))
+			return true;
+		else
+			return false;
+	}
+
+}
diff --git a/src/uws/service/request/NoEncodingParser.java b/src/uws/service/request/NoEncodingParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ebcbd19e0cad6c5cc02dc3f8de98e4e8a975cee
--- /dev/null
+++ b/src/uws/service/request/NoEncodingParser.java
@@ -0,0 +1,165 @@
+package uws.service.request;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import uws.UWSException;
+import uws.service.UWS;
+import uws.service.file.UWSFileManager;
+
+/**
+ * <p>This parser merely copies the whole HTTP request content inside a file.
+ * It names this file: "JDL" (Job Description Language).</p>
+ * 
+ * <p>
+ * 	The created file is stored in the temporary upload directory ({@link UWSFileManager#TMP_UPLOAD_DIR} ; this attribute can be modified if needed).
+ * 	This directory is supposed to be emptied regularly in case it is forgotten at any moment by the UWS service implementation to delete unused request files.
+ * </p>
+ * 
+ * <p>
+ * 	The size of the JDL is limited by the static attribute {@link #SIZE_LIMIT} before the creation of the file.
+ * 	Its default value is: {@link #DEFAULT_SIZE_LIMIT}={@value #DEFAULT_SIZE_LIMIT} bytes.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (11/2014)
+ * @since 4.1
+ */
+public class NoEncodingParser implements RequestParser {
+
+	/** Default maximum allowed size for an HTTP request content: 2 MiB. */
+	public static final int DEFAULT_SIZE_LIMIT = 2 * 1024 * 1024;
+
+	/** <p>Maximum allowed size for an HTTP request content. Over this limit, an exception is thrown and the request is aborted.</p>
+	 * <p><i>Note:
+	 * 	The default value is {@link #DEFAULT_SIZE_LIMIT} (= {@value #DEFAULT_SIZE_LIMIT} MiB).
+	 * </i></p>
+	 * <p><i>Note:
+	 * 	This limit is expressed in bytes and can not be negative.
+	 *  Its smallest possible value is 0. If the set value is though negative,
+	 *  it will be ignored and {@link #DEFAULT_SIZE_LIMIT} will be used instead.
+	 * </i></p> */
+	public static int SIZE_LIMIT = DEFAULT_SIZE_LIMIT;
+
+	/** File manager to use to create {@link UploadFile} instances.
+	 * It is required by this new object to execute open, move and delete operations whenever it could be asked. */
+	protected final UWSFileManager fileManager;
+
+	/**
+	 * Build the request parser.
+	 * 
+	 * @param fileManager	A file manager. <b>MUST NOT be NULL</b>
+	 */
+	public NoEncodingParser(final UWSFileManager fileManager){
+		if (fileManager == null)
+			throw new NullPointerException("Missing file manager => can not create a SingleDataParser!");
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	public Map<String,Object> parse(final HttpServletRequest request) throws UWSException{
+		// Check the request size:
+		if (request.getContentLength() <= 0)
+			return new HashMap<String,Object>();
+		else if (request.getContentLength() > (SIZE_LIMIT < 0 ? DEFAULT_SIZE_LIMIT : SIZE_LIMIT))
+			throw new UWSException("JDL too big (>" + SIZE_LIMIT + " bytes) => Request rejected! You should see with the service administrator to extend this limit.");
+
+		// Build the parameter name:
+		String paramName;
+		if (request.getMethod() != null && request.getMethod().equalsIgnoreCase("put")){
+			paramName = request.getRequestURI();
+			if (paramName.lastIndexOf('/') + 1 > 0)
+				paramName = paramName.substring(paramName.lastIndexOf('/') + 1);
+		}else
+			paramName = "JDL";
+
+		// Build the file by copy of the whole request body:
+		Object reqID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID);
+		if (reqID == null || !(reqID instanceof String))
+			reqID = (new Date()).getTime();
+		File f = new File(UWSFileManager.TMP_UPLOAD_DIR, "REQUESTBODY_" + reqID);
+		OutputStream output = null;
+		InputStream input = null;
+		long totalLength = 0;
+		try{
+			output = new BufferedOutputStream(new FileOutputStream(f));
+			input = new BufferedInputStream(request.getInputStream());
+
+			byte[] buffer = new byte[2049];
+			int len = input.read(buffer);
+			if (len <= 0){
+				output.close();
+				f.delete();
+				HashMap<String,Object> params = new HashMap<String,Object>(1);
+				params.put(paramName, "");
+				return params;
+			}else if (len <= 2048 && request.getMethod() != null && request.getMethod().equalsIgnoreCase("put") && request.getContentType() != null && request.getContentType().toLowerCase().startsWith("text/plain")){
+				output.close();
+				f.delete();
+				HashMap<String,Object> params = new HashMap<String,Object>(1);
+				params.put(paramName, new String(buffer, 0, len));
+				return params;
+			}else{
+				do{
+					output.write(buffer, 0, len);
+					totalLength += len;
+				}while((len = input.read(buffer)) > 0);
+				output.flush();
+			}
+		}catch(IOException ioe){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Internal error => Impossible to get the JDL from the HTTP request!");
+		}finally{
+			if (input != null){
+				try{
+					input.close();
+				}catch(IOException ioe2){}
+			}
+			if (output != null){
+				try{
+					output.close();
+				}catch(IOException ioe2){}
+			}
+		}
+
+		// Build its description:
+		UploadFile lob = new UploadFile(paramName, f.toURI().toString(), fileManager);
+		lob.mimeType = request.getContentType();
+		lob.length = totalLength;
+
+		// Create the parameters map:
+		HashMap<String,Object> parameters = new HashMap<String,Object>();
+		parameters.put(paramName, lob);
+
+		return parameters;
+	}
+
+}
diff --git a/src/uws/service/request/RequestParser.java b/src/uws/service/request/RequestParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc1b23a3c61c1962b7beddaba929039aa14e9cd8
--- /dev/null
+++ b/src/uws/service/request/RequestParser.java
@@ -0,0 +1,93 @@
+package uws.service.request;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import uws.UWSException;
+import uws.job.parameters.InputParamController;
+import uws.job.parameters.UWSParameters;
+
+/**
+ * <p>This parser lets extract parameters from an {@link HttpServletRequest}.
+ * 
+ * <p>
+ * 	These parameters can be indeed provided in several ways. Among these ways,
+ * 	application/x-www-form-urlencoded and multipart/form-data are the most famous.
+ * 	Both are already fully supported by the UWS library by default in {@link UWSParameters}.
+ * </p>
+ * 
+ * <p><b>IMPORTANT:
+ * 	A {@link RequestParser} extension MUST NOT be used to check the parameters' value.
+ * 	It only aims to parse an {@link HttpServletRequest} in order to extract parameters.
+ * </b></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (11/2014)
+ * @since 4.1
+ * 
+ * @see UWSParameters
+ */
+public interface RequestParser {
+
+	/**
+	 * <p>Extract parameters from the given HTTP request.</p>
+	 * 
+	 * <p>
+	 * 	These parameters can be fetched from {@link HttpServletRequest#getParameterMap()}
+	 * 	or directly from the full request content. In this last case, a parsing is necessary ;
+	 * 	hence this function.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	In case a parameter is provided several times with the same time and the same case,
+	 * 	the request parser can choose to keep only the last occurrence or all occurrences.
+	 * 	If all occurrences are kept, this function MUST return an array of {@link Object}s
+	 * 	(in which types may be mixed), otherwise a map value MUST be an elementary object.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	A parameter item can be a simple value (e.g. String, integer, ...)
+	 * 	or a more complex object (e.g. File, InputStream, ...).
+	 * </i></p>
+	 * 
+	 * <p><b>IMPORTANT:</b>
+	 * 	This function MUST NOT be used to check the parameters' value.
+	 * 	It only aims to parse the given request in order to extract its embedded parameters.
+	 * 	<br/>
+	 * 	<b>Consequently, if this function throws an exception, it could be only because the request
+	 * 	can not be read, and not because a parameter format or value is incorrect.</b>
+	 * 	<br/>
+	 * 	Parameter checks should be done in {@link UWSParameters} and more particularly by
+	 * 	an {@link InputParamController}.
+	 * </b></p>
+	 * 
+	 * @param request	An HTTP request.
+	 * 
+	 * @return	A map listing all extracted parameters. Values are either an elementary object (whatever is the type),
+	 *        	or an array of {@link Object}s (in which types can be mixed).
+	 * 
+	 * @throws UWSException	If any error provides this function to read the parameters.
+	 */
+	public Map<String,Object> parse(final HttpServletRequest request) throws UWSException;
+
+}
diff --git a/src/uws/service/request/UWSRequestParser.java b/src/uws/service/request/UWSRequestParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..89ea7d9eb3d41d159d9e2d9dae1a9337077cf4b8
--- /dev/null
+++ b/src/uws/service/request/UWSRequestParser.java
@@ -0,0 +1,141 @@
+package uws.service.request;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import uws.UWSException;
+import uws.UWSToolBox;
+import uws.service.file.UWSFileManager;
+
+/**
+ * <p>This parser adapts the request parser to use in function of the request content-type:</p>
+ * <ul>
+ * 	<li><b>application/x-www-form-urlencoded</b>: {@link FormEncodedParser}</li>
+ * 	<li><b>multipart/form-data</b>: {@link MultipartParser}</li>
+ * 	<li><b>other</b>: {@link NoEncodingParser} (the whole request body will be stored as one single parameter)</li>
+ * </ul>
+ * 
+ * <p>
+ * 	The request body size is limited for the multipart AND the no-encoding parsers. If you want to change this limit,
+ * 	you MUST do it for each of these parsers, setting the following static attributes: resp. {@link MultipartParser#SIZE_LIMIT}
+ * 	and {@link NoEncodingParser#SIZE_LIMIT}.
+ * </p> 
+ * 
+ * <p><i>Note:
+ * 	If you want to change the support other request parsing, you will have to write your own {@link RequestParser} implementation.
+ * </i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (12/2014)
+ * @since 4.1
+ */
+public final class UWSRequestParser implements RequestParser {
+
+	/** File manager to use to create {@link UploadFile} instances.
+	 * It is required by this new object to execute open, move and delete operations whenever it could be asked. */
+	private final UWSFileManager fileManager;
+
+	/** {@link RequestParser} to use when a application/x-www-form-urlencoded request must be parsed. This attribute is set by {@link #parse(HttpServletRequest)}
+	 * only when needed, by calling the function {@link #getFormParser()}. */
+	private RequestParser formParser = null;
+
+	/** {@link RequestParser} to use when a multipart/form-data request must be parsed. This attribute is set by {@link #parse(HttpServletRequest)}
+	 * only when needed, by calling the function {@link #getMultipartParser()}. */
+	private RequestParser multipartParser = null;
+
+	/** {@link RequestParser} to use when none of the other parsers can be used ; it will then transform the whole request body in a parameter called "JDL"
+	 * (Job Description Language). This attribute is set by {@link #parse(HttpServletRequest)} only when needed, by calling the function
+	 * {@link #getNoEncodingParser()}. */
+	private RequestParser noEncodingParser = null;
+
+	/**
+	 * Build a {@link RequestParser} able to choose the most appropriate {@link RequestParser} in function of the request content-type.
+	 * 
+	 * @param fileManager					The file manager to use in order to store any eventual upload. <b>MUST NOT be NULL</b>
+	 */
+	public UWSRequestParser(final UWSFileManager fileManager){
+		if (fileManager == null)
+			throw new NullPointerException("Missing file manager => can not create a UWSRequestParser!");
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	public Map<String,Object> parse(final HttpServletRequest req) throws UWSException{
+		if (req == null)
+			return new HashMap<String,Object>();
+
+		// Get the method:
+		String method = (req.getMethod() == null) ? "" : req.getMethod().toLowerCase();
+
+		if (method.equals("post") || method.equals("put")){
+			Map<String,Object> params = null;
+
+			// Get the parameters:
+			if (FormEncodedParser.isFormEncodedRequest(req))
+				params = getFormParser().parse(req);
+			else if (MultipartParser.isMultipartContent(req))
+				params = getMultipartParser().parse(req);
+			else
+				params = getNoEncodingParser().parse(req);
+
+			// Only for POST requests, the parameters specified in the URL must be added:
+			if (method.equals("post"))
+				params = UWSToolBox.addGETParameters(req, (params == null) ? new HashMap<String,Object>() : params);
+
+			return params;
+		}else
+			return UWSToolBox.addGETParameters(req, new HashMap<String,Object>());
+	}
+
+	/**
+	 * Get the {@link RequestParser} to use for application/x-www-form-urlencoded HTTP requests.
+	 * This parser may be created if not already done.
+	 * 
+	 * @return	The {@link RequestParser} to use for application/x-www-form-urlencoded requests. <i>Never NULL</i>
+	 */
+	private synchronized final RequestParser getFormParser(){
+		return (formParser == null) ? (formParser = new FormEncodedParser()) : formParser;
+	}
+
+	/**
+	 * Get the {@link RequestParser} to use for multipart/form-data HTTP requests.
+	 * This parser may be created if not already done.
+	 * 
+	 * @return	The {@link RequestParser} to use for multipart/form-data requests. <i>Never NULL</i>
+	 */
+	private synchronized final RequestParser getMultipartParser(){
+		return (multipartParser == null) ? (multipartParser = new MultipartParser(fileManager)) : multipartParser;
+	}
+
+	/**
+	 * Get the {@link RequestParser} to use for HTTP requests whose the content type is neither application/x-www-form-urlencoded nor multipart/form-data.
+	 * This parser may be created if not already done.
+	 * 
+	 * @return	The {@link RequestParser} to use for requests whose the content-type is not supported. <i>Never NULL</i>
+	 */
+	private synchronized final RequestParser getNoEncodingParser(){
+		return (noEncodingParser == null) ? (noEncodingParser = new NoEncodingParser(fileManager)) : noEncodingParser;
+	}
+
+}
diff --git a/src/uws/service/request/UploadFile.java b/src/uws/service/request/UploadFile.java
new file mode 100644
index 0000000000000000000000000000000000000000..2808fe982b952d30e6f46fe468030ca112a09b72
--- /dev/null
+++ b/src/uws/service/request/UploadFile.java
@@ -0,0 +1,207 @@
+package uws.service.request;
+
+/*
+ * This file is part of UWSLibrary.
+ * 
+ * UWSLibrary is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * UWSLibrary is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with UWSLibrary.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Copyright 2014 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import uws.job.UWSJob;
+import uws.job.parameters.UWSParameters;
+import uws.service.file.UWSFileManager;
+
+/**
+ * <p>This class lets represent a file submitted inline in an HTTP request.</p>
+ * 
+ * <p>
+ * 	To read this special kind of parameter, an {@link InputStream} must be open. This class lets do it
+ * 	by its function {@link #open()}.
+ * </p>
+ * 
+ * <p>
+ * 	When not used any more this file should be deleted, in order to save server disk space.
+ * 	This can be easily done thanks to {@link #deleteFile()}. This function actually just call the corresponding function
+ * 	of the file manager, which is the only one to known how to deal with this file on the server. Indeed, even if most
+ * 	of the time this file is stored on the local file system, it could also be stored on a distant server by a VOSpace.
+ * 	In this case, the way to proceed is different, hence the use of the file manager.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.1 (11/2014)
+ * 
+ * @see UWSParameters
+ * @see MultipartParser
+ */
+public class UploadFile {
+	/** Name of the parameter in which the file was submitted. */
+	public final String paramName;
+
+	/** File name. It is the name provided in the HTTP request. */
+	public final String fileName;
+
+	/** Location at which the content of this upload has been stored.
+	 * It can be a local file path, but also any other path or ID allowing
+	 * the {@link UWSFileManager} to access its content. */
+	protected String location;
+
+	/** Jobs that owns this uploaded file. */
+	protected UWSJob owner = null;
+
+	/** Indicate whether this file has been or is used by a UWSJob.
+	 * In other words, it is <i>true</i> when an open, move or delete operation has been performed.
+	 * An unused {@link UploadFile} instance shall be physically deleted from the file system. */
+	protected boolean used = false;
+
+	/** MIME type of the file. */
+	public String mimeType = null;
+
+	/** Length in bytes of the file.
+	 * If negative, the length should be considered as unknown. */
+	public long length = -1;
+
+	/** File manager to use in order to open, move or delete this uploaded file. */
+	protected final UWSFileManager fileManager;
+
+	/**
+	 * Build the description of an uploaded file.
+	 * 
+	 * @param paramName		Name of the HTTP request parameter in which the uploaded content was stored. <b>MUST NOT be NULL</b>
+	 * @param location		Location of the file on the server. This String is then used by the given file manager in order to open,
+	 *                		move or delete the uploaded file. Thus, it can be a path, an ID or any other String meaningful to the file manager.
+	 * @param fileManager	File manager to use in order to open, move or delete this uploaded file from the server.
+	 */
+	public UploadFile(final String paramName, final String location, final UWSFileManager fileManager){
+		this(paramName, null, location, fileManager);
+	}
+
+	/**
+	 * Build the description of an uploaded file.
+	 * 
+	 * @param paramName		Name of the HTTP request parameter in which the uploaded content was stored. <b>MUST NOT be NULL</b>
+	 * @param fileName		Filename as provided by the HTTP request. <i>MAY be NULL</i>
+	 * @param location		Location of the file on the server. This String is then used by the given file manager in order to open,
+	 *                		move or delete the uploaded file. Thus, it can be a path, an ID or any other String meaningful to the file manager.
+	 * @param fileManager	File manager to use in order to open, move or delete this uploaded file from the server.
+	 */
+	public UploadFile(final String paramName, final String fileName, final String location, final UWSFileManager fileManager){
+		if (paramName == null)
+			throw new NullPointerException("Missing name of the parameter in which the uploaded file content was => can not create UploadFile!");
+		else if (location == null)
+			throw new NullPointerException("Missing server location of the uploaded file => can not create UploadFile!");
+		else if (fileManager == null)
+			throw new NullPointerException("Missing file manager => can not create the UploadFile!");
+
+		this.paramName = paramName;
+		this.fileName = (fileName == null) ? "" : fileName;
+		this.location = location;
+		this.fileManager = fileManager;
+	}
+
+	/**
+	 * <p>Get the location (e.g. URI, file path) of this file on the server.</p>
+	 * 
+	 * <p><i>Important note:
+	 * 	This function SHOULD be used only by the {@link UWSFileManager} when open, move and delete operations are executed.
+	 * 	The {@link RequestParser} provided by the library set this location to the file URI (i.e. "file://{local-file-path}")
+	 * 	since the default behavior is to store uploaded file on the system temporary directory.
+	 * </i></p>
+	 * 
+	 * @return	Location (e.g. URI) or ID or any other meaningful String used by the file manager to access to the uploaded file.
+	 */
+	public String getLocation(){
+		return location;
+	}
+
+	/**
+	 * Get the job that uses this uploaded file.
+	 * 
+	 * @return	The owner of this file.
+	 */
+	public UWSJob getOwner(){
+		return owner;
+	}
+
+	/**
+	 * <p>Tell whether this uploaded file has been or will be used.
+	 * That's to say, whether an open, delete or move operation has been executed (even if it failed) on this {@link UploadFile} instance.</p>
+	 * 
+	 * @return	<i>true</i> if the file must be preserved, <i>false</i> otherwise.
+	 */
+	public final boolean isUsed(){
+		return used;
+	}
+
+	/**
+	 * Open a stream toward this uploaded file.
+	 * 
+	 * @return	Stream toward this upload content.
+	 * 
+	 * @throws IOException	If an error occurs while opening the stream.
+	 * 
+	 * @see UWSFileManager#getUploadInput(UploadFile)
+	 */
+	public InputStream open() throws IOException{
+		used = true;
+		return fileManager.getUploadInput(this);
+	}
+
+	/**
+	 * Delete definitely this uploaded file from the server.
+	 * 
+	 * @throws IOException	If the delete operation can not be performed.
+	 *        
+	 * @see UWSFileManager#deleteUpload(UploadFile)
+	 */
+	public void deleteFile() throws IOException{
+		fileManager.deleteUpload(this);
+		used = true;
+	}
+
+	/**
+	 * <p>Move this uploaded file in a location related to the given {@link UWSJob}.
+	 * It is particularly useful if at reception of an HTTP request uploaded files are stored in a temporary
+	 * directory (e.g. /tmp on Unix/Linux systems).</p>
+	 * 
+	 * <p>
+	 * 	This function calls {@link UWSFileManager#move(UploadFile,UWSJob} to process to the physical
+	 * 	moving of the file, but it then, it updates its location in this {@link UploadFile} instance.
+	 * 	<b>The file manager does NOT update this location! That's why it must not be called directly, but
+	 * 	through {@link #move(UWSJob)}.</b>
+	 * </p>
+	 * 
+	 * @param destination	The job by which this uploaded file will be exclusively used.
+	 * 
+	 * @throws IOException	If the move operation can not be performed.
+	 * 
+	 * @see UWSFileManager#moveUpload(UploadFile, UWSJob)
+	 */
+	public void move(final UWSJob destination) throws IOException{
+		if (destination == null)
+			throw new NullPointerException("Missing move destination (i.e. the job in which the uploaded file must be stored)!");
+
+		location = fileManager.moveUpload(this, destination);
+		used = true;
+		owner = destination;
+	}
+
+	@Override
+	public String toString(){
+		return (owner != null && owner.getJobList() != null && owner.getUrl() != null) ? owner.getUrl().jobParameter(owner.getJobList().getName(), owner.getJobId(), paramName).toString() : fileName;
+	}
+}
diff --git a/test/tap/formatter/JSONFormatTest.java b/test/tap/formatter/JSONFormatTest.java
index 3e6a84522088cea570b458db650900e7ce0ced9d..fee9143f1473a5416cda1065c621f515a5228561 100644
--- a/test/tap/formatter/JSONFormatTest.java
+++ b/test/tap/formatter/JSONFormatTest.java
@@ -27,13 +27,13 @@ import tap.TAPFactory;
 import tap.TAPJob;
 import tap.data.ResultSetTableIterator;
 import tap.data.TableIterator;
-import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
 import tap.parameters.TAPParameters;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import uws.service.file.UWSFileManager;
 import adql.db.DBType;
 import adql.db.DBType.DBDatatype;
 import adql.db.FunctionDef;
@@ -245,7 +245,7 @@ public class JSONFormatTest {
 		}
 
 		@Override
-		public TAPFileManager getFileManager(){
+		public UWSFileManager getFileManager(){
 			return null;
 		}
 
diff --git a/test/tap/formatter/SVFormatTest.java b/test/tap/formatter/SVFormatTest.java
index d50f0fecae68fc6f5c282b144d98ed17851161c5..d32b3dc09a662828e3b6c3a84c1035082a72c2e9 100644
--- a/test/tap/formatter/SVFormatTest.java
+++ b/test/tap/formatter/SVFormatTest.java
@@ -24,7 +24,6 @@ import tap.TAPFactory;
 import tap.TAPJob;
 import tap.data.ResultSetTableIterator;
 import tap.data.TableIterator;
-import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
@@ -32,6 +31,7 @@ import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import uws.service.file.UWSFileManager;
 import adql.db.DBType;
 import adql.db.DBType.DBDatatype;
 import adql.db.FunctionDef;
@@ -239,7 +239,7 @@ public class SVFormatTest {
 		}
 
 		@Override
-		public TAPFileManager getFileManager(){
+		public UWSFileManager getFileManager(){
 			return null;
 		}
 
diff --git a/test/tap/formatter/TextFormatTest.java b/test/tap/formatter/TextFormatTest.java
index b763a29cc669f0580784a0c33c2499ceae68b19d..97145b23e947ea4b5c2e8fddb72fd5b0fec60cd2 100644
--- a/test/tap/formatter/TextFormatTest.java
+++ b/test/tap/formatter/TextFormatTest.java
@@ -24,7 +24,6 @@ import tap.TAPFactory;
 import tap.TAPJob;
 import tap.data.ResultSetTableIterator;
 import tap.data.TableIterator;
-import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
@@ -32,6 +31,7 @@ import tap.parameters.TAPParameters;
 import testtools.CommandExecute;
 import testtools.DBTools;
 import uws.service.UserIdentifier;
+import uws.service.file.UWSFileManager;
 import adql.db.DBType;
 import adql.db.DBType.DBDatatype;
 import adql.db.FunctionDef;
@@ -239,7 +239,7 @@ public class TextFormatTest {
 		}
 
 		@Override
-		public TAPFileManager getFileManager(){
+		public UWSFileManager getFileManager(){
 			return null;
 		}
 
diff --git a/test/tap/formatter/VOTableFormatTest.java b/test/tap/formatter/VOTableFormatTest.java
index 5812072dc47999bffa5d98e75886da7b6c893b7f..dca3751b977ae07ced1f388d74e97e9f24b03bdb 100644
--- a/test/tap/formatter/VOTableFormatTest.java
+++ b/test/tap/formatter/VOTableFormatTest.java
@@ -24,7 +24,6 @@ import tap.TAPFactory;
 import tap.TAPJob;
 import tap.data.ResultSetTableIterator;
 import tap.data.TableIterator;
-import tap.file.TAPFileManager;
 import tap.log.TAPLog;
 import tap.metadata.TAPColumn;
 import tap.metadata.TAPMetadata;
@@ -33,6 +32,7 @@ import testtools.CommandExecute;
 import testtools.DBTools;
 import uk.ac.starlink.votable.DataFormat;
 import uws.service.UserIdentifier;
+import uws.service.file.UWSFileManager;
 import adql.db.DBType;
 import adql.db.DBType.DBDatatype;
 import adql.db.FunctionDef;
@@ -244,7 +244,7 @@ public class VOTableFormatTest {
 		}
 
 		@Override
-		public TAPFileManager getFileManager(){
+		public UWSFileManager getFileManager(){
 			return null;
 		}
 
diff --git a/test/tap/parameters/ServiceConnectionOfTest.java b/test/tap/parameters/ServiceConnectionOfTest.java
index 30a56ee1a127c430203b60e5b03656c00ac43cc7..1d390ce84cf933eb737a3cb4966757a7eaf91d4d 100644
--- a/test/tap/parameters/ServiceConnectionOfTest.java
+++ b/test/tap/parameters/ServiceConnectionOfTest.java
@@ -8,7 +8,6 @@ import java.util.List;
 import tap.ServiceConnection;
 import tap.TAPFactory;
 import tap.TAPJob;
-import tap.file.TAPFileManager;
 import tap.formatter.FITSFormat;
 import tap.formatter.OutputFormat;
 import tap.formatter.SVFormat;
@@ -16,6 +15,7 @@ import tap.formatter.VOTableFormat;
 import tap.log.TAPLog;
 import tap.metadata.TAPMetadata;
 import uws.service.UserIdentifier;
+import uws.service.file.UWSFileManager;
 import adql.db.FunctionDef;
 
 public class ServiceConnectionOfTest implements ServiceConnection {
@@ -141,7 +141,7 @@ public class ServiceConnectionOfTest implements ServiceConnection {
 	}
 
 	@Override
-	public TAPFileManager getFileManager(){
+	public UWSFileManager getFileManager(){
 		return null;
 	}