diff --git a/src/org/json/Json4Uws.java b/src/org/json/Json4Uws.java
index 9e84863d5585d672a5166d1422d4473b2c3287a2..0dea60dcd9ee9eb63438ef6528af2ae56b29c89d 100644
--- a/src/org/json/Json4Uws.java
+++ b/src/org/json/Json4Uws.java
@@ -16,17 +16,19 @@ package org.json;
  * 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,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.Iterator;
 
 import uws.ISO8601Format;
+import uws.UWSException;
 import uws.job.ErrorSummary;
 import uws.job.JobList;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.jobInfo.JobInfo;
 import uws.job.user.JobOwner;
 import uws.service.UWS;
 import uws.service.UWSUrl;
@@ -35,7 +37,7 @@ import uws.service.UWSUrl;
  * Useful conversion functions from UWS to JSON.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (12/2014)
+ * @version 4.2 (06/2017)
  */
 public final class Json4Uws {
 
@@ -139,11 +141,46 @@ public final class Json4Uws {
 				json.put(UWSJob.PARAM_PARAMETERS, getJobParamsJson(job));
 				json.put(UWSJob.PARAM_RESULTS, getJobResultsJson(job));
 				json.put(UWSJob.PARAM_ERROR_SUMMARY, getJson(job.getErrorSummary()));
+				if (job.getJobInfo() != null)
+					json.put(UWSJob.PARAM_JOB_INFO, getJobInfoJson(job));
 			}
 		}
 		return json;
 	}
 
+	/**
+	 * Gets the JSON representation of the jobInfo of the given job.
+	 * 
+	 * <p><b>Important note:</b>
+	 * 	This function transforms the XML returned by
+	 * 	{@link JobInfo#getXML(String)} into a JSON object
+	 * 	(see {@link XML#toJSONObject(String)}).
+	 * </p>
+	 * 
+	 * @param job				The job whose the jobInfo must be represented
+	 *           				in JSON.
+	 * 
+	 * @return					The JSON representation of its jobInfo.
+	 * 
+	 * @throws JSONException	If there is an error while building the JSON
+	 *                      	object.
+	 * 
+	 * @see JobInfo#getXML(String)
+	 * @see XML#toJSONObject(String)
+	 * 
+	 * @since 4.2
+	 */
+	public final static JSONObject getJobInfoJson(final UWSJob job) throws JSONException{
+		if (job.getJobInfo() != null){
+			try{
+				return XML.toJSONObject(job.getJobInfo().getXML(null));
+			}catch(UWSException ue){
+				throw new JSONException(ue);
+			}
+		}else
+			return null;
+	}
+
 	/**
 	 * Gets the JSON representation of the parameters of the given job.
 	 * @param job				The job whose the parameters must be represented in JSON.
diff --git a/src/tap/TAPRequestParser.java b/src/tap/TAPRequestParser.java
index 91c24c6099e227ee1212fbdaccf8738ac06359ac..ba14f935b2428ad159c9f157d2e42fd81e34ffb4 100644
--- a/src/tap/TAPRequestParser.java
+++ b/src/tap/TAPRequestParser.java
@@ -16,7 +16,7 @@ package tap;
  * 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)
+ * Copyright 2014-2017 - Astronomisches Rechen Institut (ARI)
  */
 
 import java.io.IOException;
@@ -30,55 +30,58 @@ 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>
+ * This parser adapts the request parser to use in function of the request
+ * content-type:
+ * 
  * <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>
+ * 	<li><b>other</b>: no parameter is returned</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> 
+ * 	The request body size is limited for the multipart. If you want to change
+ * 	this limit, you MUST do it for each of these parsers, setting the following
+ * 	static attributes: {@link MultipartParser#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.
+ * 	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)
+ * @version 2.1 (06/2017)
  * @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. */
+	 * 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()}. */
+	/** {@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)}
+	/** {@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.
+	 * 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>
+	 * @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)
@@ -103,7 +106,7 @@ public class TAPRequestParser implements RequestParser {
 			else if (MultipartParser.isMultipartContent(req))
 				params = getMultipartParser().parse(req);
 			else
-				params = getNoEncodingParser().parse(req);
+				params = new HashMap<String,Object>(0);
 
 			// Only for POST requests, the parameters specified in the URL must be added:
 			if (method.equals("post"))
@@ -115,10 +118,12 @@ public class TAPRequestParser implements RequestParser {
 	}
 
 	/**
-	 * Get the {@link RequestParser} to use for application/x-www-form-urlencoded HTTP requests.
+	 * 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>
+	 * @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(){
@@ -142,10 +147,12 @@ public class TAPRequestParser implements RequestParser {
 	}
 
 	/**
-	 * Get the {@link RequestParser} to use for multipart/form-data HTTP requests.
+	 * 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>
+	 * @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){
@@ -176,25 +183,17 @@ public class TAPRequestParser implements RequestParser {
 	}
 
 	/**
-	 * 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.
+	 * 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.
+	 * @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: 
+		// Create the corresponding array of Strings:
 		// ...if the array already exists, extend it:
 		String[] newValue;
 		if (oldValue != null){
diff --git a/src/uws/config/ConfigurableUWSFactory.java b/src/uws/config/ConfigurableUWSFactory.java
index 078149805262348f62baa6fa0dfe79b53b1a4d9d..19bbb156e3654234aff23b2f61f542b9a1cc1f74 100644
--- a/src/uws/config/ConfigurableUWSFactory.java
+++ b/src/uws/config/ConfigurableUWSFactory.java
@@ -16,7 +16,7 @@ package uws.config;
  * 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 2016 - Astronomisches Rechen Institut (ARI)
+ * Copyright 2016-2017 - Astronomisches Rechen Institut (ARI)
  */
 
 import static uws.config.UWSConfiguration.KEY_REGEXP_MAX_DESTRUCTION_INTERVAL;
@@ -49,6 +49,7 @@ import uws.job.ErrorSummary;
 import uws.job.JobThread;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.jobInfo.JobInfo;
 import uws.job.parameters.DestructionTimeController;
 import uws.job.parameters.DurationParamController;
 import uws.job.parameters.ExecutionDurationController;
@@ -68,7 +69,7 @@ import uws.service.request.UWSRequestParser;
  * Concrete implementation of a {@link UWSFactory} which is parameterized by a UWS configuration file.
  * 
  * @author Gr&eacute;gory Mantelet (ARI)
- * @version 4.2 (09/2016)
+ * @version 4.2 (06/2017)
  * @since 4.2
  */
 public class ConfigurableUWSFactory implements UWSFactory {
@@ -620,7 +621,14 @@ public class ConfigurableUWSFactory implements UWSFactory {
 			requestID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID).toString();
 
 		// Create the job:
-		return new UWSJob(user, createUWSParameters(request), requestID);
+		UWSJob newJob = new UWSJob(user, createUWSParameters(request), requestID);
+
+		// Set the XML job description if any:
+		Object jobDesc = request.getAttribute(UWS.REQ_ATTRIBUTE_JOB_DESCRIPTION);
+		if (jobDesc != null && jobDesc instanceof JobInfo)
+			newJob.setJobInfo((JobInfo)jobDesc);
+
+		return newJob;
 	}
 
 	@Override
diff --git a/src/uws/job/UWSJob.java b/src/uws/job/UWSJob.java
index 598a5c222fd2b5e4481439dd31b07651da6ae1fa..885bb40e91d343b7b94a8a789dd701af61939dbb 100644
--- a/src/uws/job/UWSJob.java
+++ b/src/uws/job/UWSJob.java
@@ -16,7 +16,7 @@ package uws.job;
  * 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-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -37,6 +37,7 @@ import uws.ISO8601Format;
 import uws.UWSException;
 import uws.UWSExceptionFactory;
 import uws.UWSToolBox;
+import uws.job.jobInfo.JobInfo;
 import uws.job.manager.ExecutionManager;
 import uws.job.parameters.UWSParameters;
 import uws.job.serializer.UWSSerializer;
@@ -114,7 +115,7 @@ import uws.service.request.UploadFile;
  * </ul>
  * 
  * @author	Gr&eacute;gory Mantelet (CDS;ARI)
- * @version	4.2 (01/2016)
+ * @version	4.2 (06/2017)
  */
 public class UWSJob extends SerializableUWSObject {
 	private static final long serialVersionUID = 1L;
@@ -170,6 +171,10 @@ public class UWSJob extends SerializableUWSObject {
 	/** Name of the parameter <i>results</i>. */
 	public static final String PARAM_RESULTS = "results";
 
+	/** Name of the parameter <i>jobInfo</i>.
+	 * @since 4.2 */
+	public static final String PARAM_JOB_INFO = "jobInfo";
+
 	/** Default value of {@link #owner} if no ID are given at the job creation. */
 	public final static String ANONYMOUS_OWNER = "anonymous";
 
@@ -249,6 +254,10 @@ public class UWSJob extends SerializableUWSObject {
 	/** List of all input parameters (UWS standard and non-standard parameters). */
 	protected final UWSParameters inputParams;
 
+	/** Additional description of this job.
+	 * @since 4.2 */
+	protected JobInfo jobInfo = null;
+
 	/** The thread to start for executing the job. */
 	protected transient JobThread thread = null;
 
@@ -420,7 +429,7 @@ public class UWSJob extends SerializableUWSObject {
 			try{
 				setPhase(p, true);
 			}catch(UWSException ue){
-				// Can never append because the "force" parameter is true! 
+				// Can never append because the "force" parameter is true!
 			}
 		}
 
@@ -1015,7 +1024,7 @@ public class UWSJob extends SerializableUWSObject {
 	 * @see #applyPhaseParam(JobOwner)
 	 */
 	public boolean addOrUpdateParameters(UWSParameters params, final JobOwner user) throws UWSException{
-		// The job can be modified ONLY IF in PENDING phase: 
+		// The job can be modified ONLY IF in PENDING phase:
 		if (!phase.isJobUpdatable())
 			throw new UWSException(UWSException.FORBIDDEN, "Forbidden parameters modification: the job is not any more in the PENDING phase!");
 
@@ -1143,6 +1152,54 @@ public class UWSJob extends SerializableUWSObject {
 		}
 	}
 
+	/**
+	 * Get the additional information about this job.
+	 * 
+	 * @return	Additional info. about this job,
+	 *        	or NULL if there is none.
+	 * 
+	 * @since 4.2
+	 */
+	public final JobInfo getJobInfo(){
+		return jobInfo;
+	}
+
+	/**
+	 * Set the additional information about this job.
+	 * 
+	 * <p><i>Note:
+	 * 	By default, this function replaces the current {@link JobInfo}
+	 * 	of this job by the given one (even if NULL). This behavior
+	 * 	can be changed by overwriting this function and by returning the
+	 * 	extended {@link UWSJob} in the used {@link UWSFactory}.
+	 * </i></p>
+	 * 
+	 * <p><b>Important note:</b>
+	 * 	When attributing a {@link JobInfo} to a {@link UWSJob}, you
+	 * 	may have to call {@link JobInfo#setJob(UWSJob)} on the former
+	 * 	and the new jobInfo (see the default implementation for an example)
+	 * 	for some implementations of {@link JobInfo}.
+	 * </p>
+	 * 
+	 * @param newJobInfo	The new additional info. about this job.
+	 *                  	<i>NULL is allowed and should be used to remove a
+	 *                  	JobInfo from a job.</i>
+	 * 
+	 * @since 4.2
+	 */
+	public void setJobInfo(final JobInfo newJobInfo){
+		// Cut the link between the former jobInfo and this job:
+		if (this.jobInfo != null)
+			this.jobInfo.setJob(null);
+
+		// Establish a link between the new jobInfo and this job:
+		if (newJobInfo != null)
+			newJobInfo.setJob(this);
+
+		// Replace the former jobInfo by the given one:
+		this.jobInfo = newJobInfo;
+	}
+
 	/**
 	 * Gets the execution manager of this job, if any.
 	 * 
@@ -1288,7 +1345,7 @@ public class UWSJob extends SerializableUWSObject {
 	}
 
 	/**
-	 * Stop/Cancel this job when its maximum execution duration has been reached. 
+	 * Stop/Cancel this job when its maximum execution duration has been reached.
 	 * 
 	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 	 * @version 4.1 (09/2014)
@@ -1458,7 +1515,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, 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>
+	 * <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 and the jobInfo is destroyed.</i></p>
 	 */
 	public void clearResources(){
 		// If still running, abort/stop the job:
@@ -1507,6 +1564,15 @@ public class UWSJob extends SerializableUWSObject {
 			}
 		}
 
+		// Destroy the additional job info.:
+		if (jobInfo != null){
+			try{
+				jobInfo.destroy();
+			}catch(UWSException ue){
+				getLogger().logJob(LogLevel.ERROR, this, "CLEAR_RESOURCES", "Impossible to destroy the additional information about the job \"" + jobId + "\"", ue);
+			}
+		}
+
 		getLogger().logJob(LogLevel.INFO, this, "CLEAR_RESOURCES", "Resources associated with the job \"" + getJobId() + "\" have been successfully freed.", null);
 	}
 
@@ -1677,7 +1743,7 @@ public class UWSJob extends SerializableUWSObject {
 
 	@Override
 	public String toString(){
-		return "JOB {jobId: " + jobId + "; phase: " + phase + "; runId: " + getRunId() + "; ownerId: " + owner + "; executionDuration: " + getExecutionDuration() + "; destructionTime: " + getDestructionTime() + "; quote: " + quote + "; NbResults: " + results.size() + "; " + ((errorSummary != null) ? errorSummary.toString() : "No error") + " }";
+		return "JOB {jobId: " + jobId + "; phase: " + phase + "; runId: " + getRunId() + "; ownerId: " + owner + "; executionDuration: " + getExecutionDuration() + "; destructionTime: " + getDestructionTime() + "; quote: " + quote + "; NbResults: " + results.size() + "; " + ((errorSummary != null) ? errorSummary.toString() : "No error") + " ; HasJobInfo: \"" + ((jobInfo != null) ? "yes" : "no") + "\"  }";
 	}
 
 	@Override
diff --git a/src/uws/job/jobInfo/JobInfo.java b/src/uws/job/jobInfo/JobInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..7daea60a6c37a698d125c41de2a914045496dd41
--- /dev/null
+++ b/src/uws/job/jobInfo/JobInfo.java
@@ -0,0 +1,207 @@
+package uws.job.jobInfo;
+
+/*
+ * 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 2017 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import javax.servlet.http.HttpServletResponse;
+
+import uws.UWSException;
+import uws.job.UWSJob;
+import uws.job.serializer.XMLSerializer;
+
+/**
+ * API wrapping an object which provides more information about a job.
+ * 
+ * <p>
+ * 	It can be a simple information (e.g. job progress ;
+ * 	see {@link SingleValueJobInfo}) or a whole job description instead of
+ * 	parameters as described in REC-UWS-1.0,
+ * 	"1.3. Job description language, service contracts and universality"
+ * 	(e.g. an XML document ; see {@link XMLJobInfo}).
+ * </p>
+ * 
+ * <p><b>Representation</b></p>
+ * <p>
+ * 	As requested by REC-UWS-1.0, the function {@link #getXML(String)}
+ * 	must return an XML representation of this jobInfo, but that does not
+ * 	mean that the additional job information have to be in XML ; they can be
+ * 	an XML, a .txt, an image, ... The function {@link #getXML(String)} just
+ * 	needs to return a representation of this jobInfo:
+ * 	either the jobInfo content itself or a link to access it into details.
+ * </p>
+ * <p>
+ * 	The function {@link #write(HttpServletResponse)} is only used when
+ * 	ONLY the content of a job's jobInfo is requested:
+ * 	with the URL <code>{uws-root}/{job-list}/{job-id}/jobInfo</code>.
+ * 	It allows to return the real content of this jobInfo (if not already
+ * 	the XML returned by {@link #getXML(String)}).
+ * </p>
+ * 
+ * <p><b>Resource management</b></p>
+ * <p>
+ * 	In case the jobInfo is associated with other resources (e.g. memory, file,
+ * 	...), the function {@link #destroy()} must be able to discard them.
+ * 	This function is always called at job destruction.
+ * </p>
+ * 
+ * <p><b>Backup</b></p>
+ * <p>
+ * 	The implementation of a {@link JobInfo} being free, the only viable way to
+ * 	backup a such object is by Java Class Serialization (see
+ * 	{@link Serializable}, {@link ObjectOutputStream} and
+ * 	{@link ObjectInputStream}). A default serialization is already implemented,
+ * 	but it can be customized by overriding the following functions:
+ * </p>
+ * <ul>
+ * 	<li><code>private void writeObject(java.io.ObjectOutputStream out)
+ *              throws IOException</code></li>
+ * 	<li><code>private void readObject(java.io.ObjectInputStream in)
+ *              throws IOException, ClassNotFoundException;</code></li>
+ * 	<li><code>private void readObjectNoData()
+ *              throws ObjectStreamException;</code></li>
+ * </ul>
+ * 
+ * <p><i>See the Javadoc of {@link Serializable} for more details.</i></p>
+ * 
+ * <p><b>Link with {@link UWSJob}</b></p>
+ * 
+ * <p>
+ * 	Once a {@link JobInfo} is attached to a job (thanks to
+ * 	{@link UWSJob#setJobInfo(JobInfo)}), the function {@link #setJob(UWSJob)} is
+ * 	called. In some implementation, no action is needed (see
+ * 	{@link SingleValueJobInfo}), but in some others it may be required to either
+ * 	keep a link with the parent job or to execute some special action.
+ * </p>
+ * 
+ * <p><b>Warning:</b>
+ * 	Since a {@link JobInfo} must be {@link Serializable} it is recommended to
+ * 	flag complex objects like {@link UWSJob} as <i>transient</i> as much as
+ * 	possible in order to make the backup and restore processes lighter.
+ * 	Some objects like the parent job are naturally restored when
+ * 	{@link #setJob(UWSJob)} is called. See {@link XMLJobInfo} for a concrete
+ * 	example.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.2 (06/2017)
+ * @since 4.2
+ */
+public interface JobInfo extends Serializable {
+
+	/**
+	 * Get the XML representation of this {@link JobInfo}.
+	 * 
+	 * <p><i>Note 1:
+	 * 	This function does not force the jobInfo to be in
+	 * 	XML but asks for a piece of XML document to append
+	 * 	to the XML representation of a job and representing
+	 * 	this jobInfo. It may be a full serialization of it or
+	 * 	merely a link (see <a href="https://www.w3.org/TR/xlink11/">Xlink</a>)
+	 * 	toward a complete document (XML or not).
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	The returned piece of XML can refer to the following
+	 * 	XML schemas:
+	 * </i></p>
+	 * <ul><i>
+	 * 	<li>"http://www.ivoa.net/xml/UWS/v1.0" (no prefix),</li>
+	 * 	<li>"http://www.w3.org/1999/xlink" (prefix: xlink),</li>
+	 * 	<li>"http://www.w3.org/2001/XMLSchema" (prefix: xs),</li>
+	 * 	<li>"http://www.w3.org/2001/XMLSchema-instance" (prefix: xsi).</li>
+	 * </i></ul>
+	 * <p><i>
+	 * 	If more namespaces are needed they should be specified directly
+	 * 	at the root of the XML returned by this function (if possible
+	 * 	with a valid <code>xsi:schemaLocation</code>). An alternative
+	 * 	would be to extend {@link XMLSerializer} in order to append
+	 * 	the needed namespaces to the root XML node of any formatted XML
+	 * 	documents.
+	 * </i></p>
+	 * 
+	 * @param newLinePrefix	Characters (generally white-spaces) that should
+	 *                     	prefix all new line of the returned piece of
+	 *                     	XML. New line characters should also be
+	 *                     	included in this string ; if not, the
+	 *                     	returned XML should be on a single line.
+	 *                     	<i>This parameter may be NULL.</i>
+	 * 
+	 * @return	XML representation of this jobInfo.
+	 * 
+	 * @throws UWSException	If any error occurs while building the XML
+	 *                     	representation of this jobInfo.
+	 */
+	public String getXML(final String newLinePrefix) throws UWSException;
+
+	/**
+	 * Write the content of this jobInfo as a complete HTTP response
+	 * when the URL <code>{uws-root}/{job-list}/{job-id}/jobInfo</code> is
+	 * requested.
+	 * 
+	 * <p><b>Important:</b>
+	 * 	At least the Content-Type, the Content-Length and Character-Encoding
+	 * 	should be set in addition of the response content.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	If formatted into XML, the root node of the returned document
+	 * 	may be the UWS node "jobInfo" or not, depending on your
+	 * 	desired implementation. Since the UWS standard does not specify
+	 * 	any way to retrieve individually a jobInfo, this part is left
+	 * 	here totally free to the developer will.
+	 * </i></p>
+	 * 
+	 * @param response	HTTP response in which the jobInfo content must be
+	 *                	written.
+	 * 
+	 * @throws IOException	If there is any error while writing the jobInfo
+	 *                    	content.
+	 * @throws UWSException	If there is any error while formating the jobInfo
+	 *                     	content.
+	 */
+	public void write(final HttpServletResponse response) throws IOException, UWSException;
+
+	/**
+	 * Notify this {@link JobInfo} that it is now owned by the given job.
+	 * 
+	 * @param myJob	The new owner of this {@link JobInfo}.
+	 *             	<i>This parameter may be NULL.</i>
+	 */
+	public void setJob(final UWSJob myJob);
+
+	/**
+	 * Free/Discard any resource associated with this {@link JobInfo}.
+	 * 
+	 * <p><i>Note:</i>
+	 * 	This function should be called only at job destruction.
+	 * 	It particularly aims to delete any file containing the full
+	 * 	content of this JobInfo, but it should also be used for any
+	 * 	other kind of associated resource.
+	 * </p>
+	 * 
+	 * @throws UWSException	If all associated resources can not be freed.
+	 */
+	public void destroy() throws UWSException;
+
+}
diff --git a/src/uws/job/jobInfo/SingleValueJobInfo.java b/src/uws/job/jobInfo/SingleValueJobInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..f08be0b75eb25f4e121d62cb1a50835a985721d6
--- /dev/null
+++ b/src/uws/job/jobInfo/SingleValueJobInfo.java
@@ -0,0 +1,195 @@
+package uws.job.jobInfo;
+
+/*
+ * 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 2017 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletResponse;
+
+import uws.UWSException;
+import uws.job.UWSJob;
+import uws.job.serializer.XMLSerializer;
+
+/**
+ * Very simple implementation of {@link JobInfo}. It aims to represent a
+ * key-value pair.
+ * 
+ * <p>
+ * 	Both functions {@link #getXML(String)} and
+ * 	{@link #write(HttpServletResponse)} will return the following XML document:
+ * </p>
+ * 
+ * <pre>&lt;KEY&gt;VALUE&lt;/KEY&gt;</pre>
+ * 
+ * <p>, where:</p>
+ * <ul>
+ * 	<li><i><code>KEY</code></i> can be get with {@link #getName()} and can be
+ * 		set <b>only</b> at creation</li>
+ * 	<li><i><code>VALUE</code></i> can be get with {@link #getValue()} and set
+ * 		with {@link #setValue(String)}.</li>
+ * </ul>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.2 (06/2017)
+ * @since 4.2
+ */
+public class SingleValueJobInfo implements JobInfo {
+	private static final long serialVersionUID = 1L;
+
+	/** Name of the value stored inside this {@link JobInfo}.
+	 * 
+	 * <p><i><b>Warning:</b> By default, this name is not supposed to be
+	 * 	changed after initialization of this class. That's why only a public
+	 * 	getter function is provided.</i></p> */
+	protected String name = null;
+
+	/** Value stored inside this {@link JobInfo}. */
+	protected String value = null;
+
+	/** XML representation of this {@link JobInfo}, returned by
+	 * {@link #getXML(String)} and {@link #write(HttpServletResponse)}.
+	 * 
+	 * <p><i>Note:
+	 * 	It has to be updated each time the {@link #value} is changed. So by
+	 * 	default, it is rebuilt by {@link #setValue(String)}.
+	 * </i></p> */
+	protected String xmlRepresentation = null;
+
+	/**
+	 * Build a {@link JobInfo} representing a single value having the given
+	 * name.
+	 * 
+	 * <p><i>Note 1:
+	 * 	The name can not be changed after creation.
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	With this constructor, the represented value is NULL. To set a value,
+	 * 	you have to use the function {@link #setValue(String)}. An alternative
+	 * 	would be to use the constructor
+	 * 	{@link #SingleValueJobInfo(String, String)} so that setting immediately
+	 * 	the name and value.
+	 * </i></p>
+	 * 
+	 * @param name	Name of the value to represent.
+	 * 
+	 * @throws NullPointerException		If the given name is NULL or an empty
+	 *                             		string.
+	 * @throws IllegalArgumentException	If the given name is not a valid XML
+	 *                                 	node name according to the W3C (see
+	 *                                 	{@link XMLSerializer#isValidXMLNodeName(String)}
+	 *                                 	for more details).
+	 */
+	public SingleValueJobInfo(final String name) throws NullPointerException, IllegalArgumentException{
+		this(name, null);
+	}
+
+	/**
+	 * Build a {@link JobInfo} representing a single value having the given
+	 * name and initial value.
+	 * 
+	 * <p><i>Note 1:
+	 * 	The name can not be changed after creation.
+	 * </i></p>
+	 * 
+	 * <p><i>Note 2:
+	 * 	The value can change after object creation with the function
+	 * 	{@link #setValue(String)}.
+	 * </i></p>
+	 * 
+	 * @param name	Name of the value to represent. <i>Can not be NULL or an
+	 *            	empty string, and must be a valid XML node name.</i>
+	 * @param value	Value to represent. <i>May be NULL.</i>
+	 * 
+	 * @throws NullPointerException		If the given name is NULL or an empty
+	 *                             		string.
+	 * @throws IllegalArgumentException	If the given name is not a valid XML
+	 *                                 	node name according to the W3C (see
+	 *                                 	{@link XMLSerializer#isValidXMLNodeName(String)}
+	 *                                 	for more details).
+	 */
+	public SingleValueJobInfo(final String name, final String value) throws NullPointerException, IllegalArgumentException{
+		if (name == null || name.trim().length() == 0)
+			throw new NullPointerException("Missing SingleValueJobInfo name!");
+		else if (!XMLSerializer.isValidXMLNodeName(name))
+			throw new IllegalArgumentException("Invalid XML node name: \"" + name + "\"! You should choose a different name for your SingleValueJobInfo.");
+
+		this.name = name;
+		setValue(value);
+	}
+
+	/**
+	 * Get the name of the represented value.
+	 * 
+	 * @return	Value name. <i>Can NEVER be NULL.</i>
+	 */
+	public String getName(){
+		return name;
+	}
+
+	/**
+	 * Get the represented value.
+	 * 
+	 * @return	The represented value. <i>Can be NULL.</i>
+	 */
+	public String getValue(){
+		return value;
+	}
+
+	/**
+	 * Set the value represented by this {@link JobInfo}.
+	 * 
+	 * @param value	The new value to represent. <i>Can be NULL.</i>
+	 */
+	public void setValue(final String value){
+		this.value = value;
+
+		xmlRepresentation = "<" + name + ">" + XMLSerializer.escapeXMLData(this.value) + "</" + name + ">";
+	}
+
+	@Override
+	public String getXML(final String newLinePrefix){
+		return xmlRepresentation;
+	}
+
+	@Override
+	public void write(HttpServletResponse response) throws IOException, UWSException{
+		response.setCharacterEncoding("UTF-8");
+		response.setContentType("text/xml");
+		response.setContentLength(xmlRepresentation.getBytes("UTF-8").length);
+
+		PrintWriter writer = response.getWriter();
+		writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+		writer.println(xmlRepresentation);
+		writer.flush();
+	}
+
+	@Override
+	public void setJob(final UWSJob myJob){
+		// Nothing to do!
+	}
+
+	@Override
+	public void destroy() throws UWSException{
+		// Nothing to do!
+	}
+
+}
diff --git a/src/uws/job/jobInfo/XMLJobInfo.java b/src/uws/job/jobInfo/XMLJobInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..39ce547e60f154d8297990e386facec14f426744
--- /dev/null
+++ b/src/uws/job/jobInfo/XMLJobInfo.java
@@ -0,0 +1,390 @@
+package uws.job.jobInfo;
+
+/*
+ * 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 2017 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Serializable;
+
+import javax.servlet.http.HttpServletResponse;
+
+import uws.UWSException;
+import uws.UWSToolBox;
+import uws.job.UWSJob;
+import uws.job.serializer.XMLSerializer;
+import uws.service.UWS;
+import uws.service.file.UWSFileManager;
+import uws.service.log.UWSLog.LogLevel;
+import uws.service.request.UploadFile;
+
+/**
+ * A full XML document attached to a {@link UWSJob job}.
+ * 
+ * <p><b>XML representation</b></p>
+ * 
+ * <p>
+ * 	The document stored inside this {@link JobInfo} is considered formatted
+ * 	in XML. So the functions {@link #getXML(String)} and
+ * 	{@link #write(HttpServletResponse)} will return them as such.
+ * </p>
+ * 
+ * <p><i>Note 1:
+ * 	The represented document is supposed to be XML, but absolutely no
+ * 	verification is performed by {@link XMLJobInfo}.
+ * </i></p>
+ * 
+ * <p><i>Note 2:
+ * 	{@link #getXML(String)} will skip the XML declaration
+ * 	(e.g. <code>&lt;?xml version="1.0" encoding="utf-8"?&gt;</code>)
+ * 	if any is provided. On the contrary, {@link #write(HttpServletResponse)}
+ * 	will write an exact copy of the stored XML document.
+ * 	Both functions can be overwritten if a different behavior is needed.
+ * </i></p>
+ * 
+ * <p><i>Note 3:
+ * 	The stored XML document can refer to the following
+ * 	XML schemas:
+ * </i></p>
+ * <ul><i>
+ * 	<li>"http://www.ivoa.net/xml/UWS/v1.0" (no prefix),</li>
+ * 	<li>"http://www.w3.org/1999/xlink" (prefix: xlink),</li>
+ * 	<li>"http://www.w3.org/2001/XMLSchema" (prefix: xs),</li>
+ * 	<li>"http://www.w3.org/2001/XMLSchema-instance" (prefix: xsi).</li>
+ * </i></ul>
+ * <p><i>
+ * 	If more namespaces are needed they should be specified directly
+ * 	at the root of the stored XML document (if possible with a valid
+ * 	<code>xsi:schemaLocation</code>). An alternative would be to extend
+ * 	{@link XMLSerializer} in order to append the needed namespaces to the root
+ * 	XML node of any formatted XML documents.
+ * </i></p>
+ * 
+ * <p><b>Internal representation and Creation</b></p>
+ * 
+ * <p>
+ * 	This class proposes the two following constructors, each for a different
+ * 	internal representation:
+ * </p>
+ * <ul>
+ * 	<li><i>{@link #XMLJobInfo(String)} for an in-memory string.</i> The given
+ * 		string is supposed to contained the full XML document and will be
+ * 		stored as such in this class. This constructor should be used <b>only
+ * 		for small XML document</b>.</li>
+ * 	<li><i>{@link #XMLJobInfo(UploadFile)} for an XML file storage.</i>	The
+ * 		given {@link UploadFile} is supposed to give access to the complete
+ * 		XML document.This constructor should be used <b>for large XML
+ * 		document.</b></li>
+ * </ul>
+ * 
+ * <p><b>Modification</b></p>
+ * 
+ * <p>
+ * 	By default, this implementation of {@link JobInfo} does not allow the
+ * 	modification of its XML document. If needed, this class should be
+ * 	extended with the adequate functions.
+ * </p>
+ * 
+ * <p><b>Backup/Restoration</b></p>
+ * 
+ * <p>
+ * 	An {@link UploadFile} can not be serialized using the Java Class
+ * 	Serialization mechanism because it does not implement the
+ * 	{@link Serializable} interface. Consequently, the given {@link UploadFile}
+ * 	will be marked as <i>transient</i> and will have to be rebuilt when needed
+ * 	after a restoration process.
+ * </p>
+ * 
+ * <p>However, it can be rebuilt only if:</p>
+ * <ol>
+ * 	<li>an access to a {@link UWSFileManager} is
+ * 	possible.</li>
+ * 	<li>the location of the file is known.</li>
+ * </ol>
+ * 
+ * <p>
+ *  The first point (1) is fortunately possible through a {@link UWSJob} object.
+ * 	This object is known after attachment to a job thanks to the function
+ * 	{@link #setJob(UWSJob)}. So, a link toward the parent job should be kept ;
+ * 	also marked as <i>transient</i>: see the attribute {@link #job}.
+ * </p>
+ * 
+ * <p>For the second point (2), the location of the file must be kept as a
+ * 	non-transient attribute (see {@link #location}) so that being backuped with the
+ * 	other non-transient attributes of this class. In order to backup this
+ * 	location up-to-date, the function
+ * 	{@link #writeObject(java.io.ObjectOutputStream)} updates {@link #location}
+ * 	before the normal Java Class Serialization.
+ * 
+ * <p>
+ * 	So finally, the restoration of the {@link UploadFile} will be done by
+ * 	{@link #getXML(String)} and {@link #write(HttpServletResponse)} with the
+ * 	function {@link #restoreFile()}.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.2 (06/2017)
+ * @since 4.2
+ */
+public class XMLJobInfo implements JobInfo {
+	private static final long serialVersionUID = 1L;
+
+	/** XML file location.
+	 * 
+	 * <p><b>Warning:</b>
+	 * 	This field <b>ONLY</b> aims to contain the updated result of
+	 * 	{@link UploadFile#getLocation() file.getLocation()}.
+	 * </p> */
+	protected String location = null;
+
+	/** Link toward the XML file represented by this {@link JobInfo}.
+	 * 
+	 * <p><b>Important:</b>
+	 * 	This field must be used when a large XML document has to be represented
+	 * 	by this {@link JobInfo}.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 *  It can be set only by {@link #XMLJobInfo(UploadFile)}.
+	 *  If set, {@link #content} must be NULL.
+	 * </i></p>*/
+	protected transient UploadFile file = null;
+
+	/** XML document represented by this {@link JobInfo}.
+	 * 
+	 * <p><b>Important:</b>
+	 * 	This field MUST be used <b>ONLY</b> for small XML document in order to
+	 * 	keep enough memory free for the normal UWS service operations.
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	It can be set only by {@link #XMLJobInfo(String)}.
+	 * 	If set, {@link #file} must be NULL.
+	 * </i></p> */
+	protected String content = null;
+
+	/** Precise length (in bytes) of the represented XML document. */
+	protected int length = -1;
+
+	/** The job owning this {@link JobInfo}.
+	 * 
+	 * <p><i>Note:
+	 * 	This field is set only by {@link #setJob(UWSJob)} and is used
+	 * 	only by {@link #restoreFile()} in order to rebuild {@link #file}
+	 * 	after a UWS service restoration.
+	 * </i></p> */
+	protected transient UWSJob job = null;
+
+	/**
+	 * Build a {@link JobInfo} representing a <b>small</b> XML document.
+	 * 
+	 * <p><b>Important:</b>
+	 * 	This constructor should be used only for <b>small</b> XML document
+	 * 	because the given string will be kept as such in memory. If the given
+	 * 	string is too large, not enough memory will be available for normal
+	 * 	UWS service operations.<br/><br/>
+	 * 	<i>If you estimate the XML document is too big to stay in memory, you
+	 * 	should save it in a file and use the constructor
+	 * 	{@link #XMLJobInfo(UploadFile)}.</i>
+	 * </p>
+	 * 
+	 * @param smallXML	The small XML document to represent.
+	 * 
+	 * @throws NullPointerException	If the given string is NULL or empty.
+	 */
+	public XMLJobInfo(final String smallXML) throws NullPointerException{
+		if (smallXML == null || smallXML.trim().length() == 0)
+			throw new NullPointerException("Missing XML content!");
+
+		content = smallXML;
+		length = smallXML.getBytes().length;
+		file = null;
+		location = null;
+	}
+
+	/**
+	 * Build a {@link JobInfo} representing a <b>large</b> XML document stored
+	 * inside a file.
+	 * 
+	 * @param xmlFile	Link toward the large XML document to represent.
+	 * 
+	 * @throws NullPointerException	If the given file is NULL or empty.
+	 */
+	public XMLJobInfo(final UploadFile xmlFile) throws NullPointerException{
+		if (xmlFile == null || xmlFile.length <= 0)
+			throw new NullPointerException("Missing XML file!");
+
+		file = xmlFile;
+		location = file.getLocation();
+		length = (int)file.length;
+		content = null;
+	}
+
+	@Override
+	public String getXML(final String newLinePrefix) throws UWSException{
+		// CASE: SMALL XML DOCUMENT:
+		if (content != null){
+			if (content.trim().startsWith("<?"))
+				return content.substring(content.indexOf("?>") + 2);
+			else
+				return content;
+
+		}// CASE: XML FILE
+		else{
+			restoreFile();
+
+			StringBuffer xml = new StringBuffer();
+			BufferedReader input = null;
+			try{
+				// Open the XML file:
+				input = new BufferedReader(new InputStreamReader(file.open()));
+				String line;
+				// Read it line by line:
+				while((line = input.readLine()) != null){
+					// Ignore the XML declarative lines:
+					if (line.trim().startsWith("<?")){
+						line = line.substring(line.indexOf("?>") + 2);
+						if (line.trim().length() == 0)
+							continue;
+					}
+					// Append the line prefix (if any):
+					if (newLinePrefix != null && xml.length() > 0)
+						xml.append(newLinePrefix);
+					// Append the fetched line:
+					xml.append(line);
+				}
+			}catch(IOException ioe){
+				throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Impossible to get the XML representation of the JobInfo!");
+			}finally{
+				if (input != null){
+					try{
+						input.close();
+					}catch(IOException ioe){}
+				}
+			}
+			return xml.toString();
+		}
+	}
+
+	@Override
+	public void write(final HttpServletResponse response) throws IOException, UWSException{
+		// CASE: SMALL XML DOCUMENT:
+		if (content != null){
+			response.setCharacterEncoding("UTF-8");
+			response.setContentType("text/xml");
+			response.setContentLength(content.getBytes("UTF-8").length);
+
+			PrintWriter writer = response.getWriter();
+			writer.println(content);
+			writer.flush();
+		}
+
+		// CASE: XML FILE:
+		else{
+			restoreFile();
+			UWSToolBox.write(file.open(), "text/xml", file.length, response);
+		}
+	}
+
+	@Override
+	public void setJob(final UWSJob myJob){
+		job = myJob;
+
+		if (job != null && file != null){
+			try{
+				file.move(job);
+				location = file.getLocation();
+			}catch(IOException ioe){
+				if (job.getLogger() != null)
+					job.getLogger().logUWS(LogLevel.ERROR, job, "SET_JOB_INFO", "Error when moving the XML JobInfo file closer to the job " + job.getJobId() + "! Current file location: " + file.getLocation(), ioe);
+			}
+		}
+	}
+
+	@Override
+	public void destroy() throws UWSException{
+		try{
+			file.deleteFile();
+		}catch(IOException ioe){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Error when deleting a JobInfo file!");
+		}
+	}
+
+	/**
+	 * Serialize this {@link XMLJobInfo}.
+	 * 
+	 * <p><i>Note:</i>
+	 * 	This function will be called by the Java Class Serialization mechanism.
+	 * 	See the Javadoc of {@link Serializable} for more details.
+	 * </i></p>
+	 * 
+	 * <p>
+	 * 	This function just updates the XML file (if any) location before the
+	 * 	normal Java Class Serialization of this object.
+	 * </p>
+	 * 
+	 * @param out	The stream used to contained the serialization of this
+	 *           	{@link XMLJobInfo}.
+	 *
+	 * @throws IOException	If any error occurs while serializing this
+	 *                    	{@link XMLJobInfo}
+	 */
+	private void writeObject(java.io.ObjectOutputStream out) throws IOException{
+		// Ensure the location is up-to-date before performing the backup of this jobInfo:
+		if (file != null)
+			location = file.getLocation();
+
+		// Apply the default Java serialization method:
+		out.defaultWriteObject();
+	}
+
+	/**
+	 * Restore the link toward the XML file represented by this {@link JobInfo}.
+	 * 
+	 * <p>
+	 * 	This function has an effect only if {@link #file} is NULL but not
+	 * 	{@link #location} ; indeed, such configuration can be encountered only
+	 * 	if this {@link XMLJobInfo} has been de-serialized.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	Nothing can be done if the parent job is unknown. In other words,
+	 * 	this {@link JobInfo} has to be attached to a job first
+	 * 	(i.e. {@link #setJob(UWSJob)} has to be called first with a non-NULL
+	 * 	parameter). If not, an exception will be thrown.
+	 * </p>
+	 * 
+	 * @throws UWSException	If this {@link JobInfo} is not attached to a job.
+	 */
+	protected void restoreFile() throws UWSException{
+		/* If the file is NULL, it means a UWS restore has just occurred.
+		 * Because UploadFile is not Serializable, it was impossible to restore the file.
+		 * To solve this problem, the location has been saved.
+		 * So, the file can be and has to be restored. */
+		if (file == null && location != null){
+			if (job != null)
+				file = new UploadFile(UWS.REQ_ATTRIBUTE_JOB_DESCRIPTION, location, job.getFileManager());
+			else
+				throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Missing jobInfo's file: impossible to display its content! Cause: missing UWSJob parent.");
+		}
+	}
+
+}
diff --git a/src/uws/job/serializer/XMLSerializer.java b/src/uws/job/serializer/XMLSerializer.java
index 15fe433e8d66777a78a8544273d9e6e9e6172ef5..e59cdc3e88e768465e2432fce02ac695114e8f48 100644
--- a/src/uws/job/serializer/XMLSerializer.java
+++ b/src/uws/job/serializer/XMLSerializer.java
@@ -16,8 +16,8 @@ package uws.job.serializer;
  * 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-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
- *                       Astronomisches Rechen Institut (ARI) 
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.io.UnsupportedEncodingException;
@@ -25,10 +25,12 @@ import java.net.URLEncoder;
 import java.util.Iterator;
 
 import uws.ISO8601Format;
+import uws.UWSException;
 import uws.job.ErrorSummary;
 import uws.job.JobList;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.jobInfo.JobInfo;
 import uws.job.user.JobOwner;
 import uws.service.UWS;
 import uws.service.UWSUrl;
@@ -38,7 +40,7 @@ import uws.service.request.UploadFile;
  * Lets serializing any UWS resource in XML.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (02/2015)
+ * @version 4.2 (06/2017)
  */
 public class XMLSerializer extends UWSSerializer {
 	private static final long serialVersionUID = 1L;
@@ -90,9 +92,13 @@ public class XMLSerializer extends UWSSerializer {
 	}
 
 	/**
-	 * <p>Gets the XML file header (xml version, encoding and the xslt style-sheet link if any).</p>
-	 * <p>It is always called by the implementation of the UWSSerializer functions
-	 * if their boolean parameter (<i>root</i>) is <i>true</i>.</p>
+	 * Gets the XML file header (xml version, encoding and the xslt
+	 * style-sheet link if any).
+	 * 
+	 * <p>
+	 * 	It is always called by the implementation of the UWSSerializer functions
+	 * 	if their boolean parameter (<i>root</i>) is <i>true</i>.
+	 * </p>
 	 * 
 	 * @return	The XML file header.
 	 */
@@ -104,9 +110,12 @@ public class XMLSerializer extends UWSSerializer {
 	}
 
 	/**
-	 * Gets all UWS namespaces declarations needed for an XML representation of a UWS object.
+	 * Gets all UWS namespaces declarations needed for an XML representation of
+	 * a UWS object.
 	 * 
-	 * @return	The UWS namespaces: <br /> (i.e. <i>= "xmlns:uws=[...] xmlns:xlink=[...] xmlns:xs=[...] xmlns:xsi=[...] xsi:schemaLocation=[...]"</i>).
+	 * @return	The UWS namespaces: <br /> (i.e. <i>= "xmlns:uws=[...]
+	 *        	xmlns:xlink=[...] xmlns:xs=[...] xmlns:xsi=[...]
+	 *        	xsi:schemaLocation=[...]"</i>).
 	 */
 	public String getUWSNamespace(){
 		return "xmlns=\"http://www.ivoa.net/xml/UWS/v1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.ivoa.net/xml/UWS/v1.0 http://www.ivoa.net/xml/UWS/v1.0 http://www.w3.org/1999/xlink http://www.w3.org/1999/xlink.xsd http://www.w3.org/2001/XMLSchema http://www.w3.org/2001/XMLSchema.xsd\"";
@@ -116,9 +125,11 @@ public class XMLSerializer extends UWSSerializer {
 	 * Gets the node attributes which declare the UWS namespace.
 	 * 
 	 * @param root	<i>false</i> if the attribute to serialize will be included
-	 * 				in a top level serialization (for a job attribute: job), <i>true</i> otherwise.
+	 * 				in a top level serialization (for a job attribute: job),
+	 *            	<i>true</i> otherwise.
 	 * 
-	 * @return		"" if <i>root</i> is <i>false</i>, " "+UWSNamespace otherwise.
+	 * @return		"" if <i>root</i> is <i>false</i>, " "+UWSNamespace
+	 *        		otherwise.
 	 * 
 	 * @see #getUWSNamespace()
 	 */
@@ -185,7 +196,7 @@ public class XMLSerializer extends UWSSerializer {
 	}
 
 	@Override
-	public String getJob(final UWSJob job, final boolean root){
+	public String getJob(final UWSJob job, final boolean root) throws UWSException{
 		StringBuffer xml = new StringBuffer(root ? getHeader() : "");
 		String newLine = "\n\t";
 
@@ -212,7 +223,12 @@ public class XMLSerializer extends UWSSerializer {
 		xml.append(newLine).append(getResults(job, false));
 
 		// errorSummary:
-		xml.append(newLine).append(getErrorSummary(job.getErrorSummary(), false));
+		if (job.getErrorSummary() != null)
+			xml.append(newLine).append(getErrorSummary(job.getErrorSummary(), false));
+
+		// jobInfo:
+		if (job.getJobInfo() != null)
+			xml.append(newLine).append(getJobInfo(job));
 
 		tabPrefix = "";
 		return xml.append("\n</job>").toString();
@@ -411,7 +427,7 @@ public class XMLSerializer extends UWSSerializer {
 		}
 
 		/* NOTE: THE FOLLOWING ATTRIBUTES MAY PROVIDE USEFUL INFORMATION TO USERS, BUT THEY ARE NOT ALLOWED BY THE CURRENT UWS STANDARD.
-		 *       HOWEVER, IF, ONE DAY, THEY ARE, THE FOLLOWING LINES SHOULD BE UNCOMNENTED. 
+		 *       HOWEVER, IF, ONE DAY, THEY ARE, THE FOLLOWING LINES SHOULD BE UNCOMNENTED.
 		 *
 		 * if (result.getMimeType() != null)
 		 * 	xml.append(" mime=\"").append(escapeXMLAttribute(result.getMimeType())).append("\"");
@@ -422,6 +438,33 @@ public class XMLSerializer extends UWSSerializer {
 		return xml.append(" />").toString();
 	}
 
+	/**
+	 * Serialize into XML the {@link JobInfo} of the given job, if any.
+	 * 
+	 * <p><b>Important note:</b>
+	 * 	By default, this function wrap the XML content returned by
+	 * 	{@link JobInfo#getXML(String)} inside an XML node "jobInfo".
+	 * 	To change this behavior, you should overwrite this function.
+	 * </p>
+	 * 
+	 * @param job	The job whose the jobInfo must be serialized into XML.
+	 * 
+	 * @return	The XML serialization of the given job's jobInfo,
+	 *        	or an empty string if the given job has no jobInfo.
+	 * 
+	 * @since 4.2
+	 */
+	public String getJobInfo(final UWSJob job) throws UWSException{
+		if (job.getJobInfo() != null){
+			StringBuffer xml = new StringBuffer();
+			xml.append(tabPrefix).append("<jobInfo>");
+			xml.append("\n\t").append(tabPrefix).append(job.getJobInfo().getXML("\n\t" + tabPrefix));
+			xml.append('\n').append(tabPrefix).append("</jobInfo>");
+			return xml.toString();
+		}else
+			return "";
+	}
+
 	/* ************** */
 	/* ESCAPE METHODS */
 	/* ************** */
@@ -506,10 +549,12 @@ 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 
+	 * 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>
+	 * <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
@@ -520,4 +565,44 @@ public class XMLSerializer extends UWSSerializer {
 		return ((c >= '\u0020' && c <= '\uD7FF') || (c >= '\uE000' && c <= '\uFFFD') || ((c) == 0x09 || (c) == 0x0A || (c) == 0x0D)) ? c : '\u00BF';
 	}
 
+	/** Regular expression for the first character of a valid XML node name.
+	 * <p><i>Note:
+	 * 	This rule comes from the XML 1.1 standard by the W3C:
+	 * 		<a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-NameStartChar">https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-NameStartChar</a>
+	 * </i></p>
+	 * @since 4.2 */
+	private final static String XML_START_NODE_NAME_REGEX = ":A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\x{10000}-\\x{EFFFF}";
+
+	/**
+	 * Regular expression of a whole valid XML node name.
+	 * <p><i>Note:
+	 * 	This rule comes from the XML 1.1 standard by the W3C:
+	 * 		<a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-Name">https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-Name</a>
+	 * </i></p>
+	 * @since 4.2 */
+	private final static String XML_NODE_NAME_REGEX = "[" + XML_START_NODE_NAME_REGEX + "][" + XML_START_NODE_NAME_REGEX + "\\-.0-9\\xB7\\u0300-\\u036F\\u203F-\\u2040]*";
+
+	/**
+	 * Determine whether the given name is a valid XML node name
+	 * according to the W3C (XML 1.1).
+	 * 
+	 * <p><i>Note:
+	 * 	In addition of validating the given name against the regular expression
+	 * 	provided by the W3C (see {@link #XML_NODE_NAME_REGEX}), this function
+	 * 	ensures the given name does not start with "XML" according to the
+	 * 	following W3C note:
+	 * 		<a href="https://www.w3.org/TR/2006/REC-xml11-20060816/#dt-name">https://www.w3.org/TR/2006/REC-xml11-20060816/#dt-name</a>
+	 * </i></p>
+	 * 
+	 * @param nodeName	XML node name to test.
+	 * 
+	 * @return	<code>true</code> if the given node name is valid,
+	 *        	<code>false</code> otherwise.
+	 * 
+	 * @since 4.2
+	 */
+	public static boolean isValidXMLNodeName(final String nodeName){
+		return nodeName.matches(XML_NODE_NAME_REGEX) && !nodeName.toLowerCase().startsWith("xml");
+	}
+
 }
diff --git a/src/uws/service/AbstractUWSFactory.java b/src/uws/service/AbstractUWSFactory.java
index 332254a9014638dbc5c35795765ed544b394986d..d066712ee3c6afe8fcbebdde7a0fe65b1164ca0c 100644
--- a/src/uws/service/AbstractUWSFactory.java
+++ b/src/uws/service/AbstractUWSFactory.java
@@ -16,7 +16,7 @@ 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-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -33,6 +33,7 @@ import uws.job.ErrorSummary;
 import uws.job.JobThread;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.jobInfo.JobInfo;
 import uws.job.parameters.DestructionTimeController;
 import uws.job.parameters.DestructionTimeController.DateField;
 import uws.job.parameters.ExecutionDurationController;
@@ -48,7 +49,7 @@ import uws.service.request.UWSRequestParser;
  * Only the function which creates a {@link JobThread} from a {@link UWSJob} needs to be implemented.</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.2 (01/2016)
+ * @version 4.2 (06/2017)
  */
 public abstract class AbstractUWSFactory implements UWSFactory {
 
@@ -84,13 +85,20 @@ public abstract class AbstractUWSFactory implements UWSFactory {
 
 	@Override
 	public UWSJob createJob(final HttpServletRequest request, final JobOwner user) throws UWSException{
-		// Extract the HTTP request ID (the job ID should be the same, if not already used by another job): 
+		// Extract the HTTP request ID (the job ID should be the same, if not already used by another job):
 		String requestID = null;
 		if (request != null && request.getAttribute(UWS.REQ_ATTRIBUTE_ID) != null && request.getAttribute(UWS.REQ_ATTRIBUTE_ID) instanceof String)
 			requestID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID).toString();
 
 		// Create the job:
-		return new UWSJob(user, createUWSParameters(request), requestID);
+		UWSJob newJob = new UWSJob(user, createUWSParameters(request), requestID);
+
+		// Set the XML job description if any:
+		Object jobDesc = request.getAttribute(UWS.REQ_ATTRIBUTE_JOB_DESCRIPTION);
+		if (jobDesc != null && jobDesc instanceof JobInfo)
+			newJob.setJobInfo((JobInfo)jobDesc);
+
+		return newJob;
 	}
 
 	@Override
diff --git a/src/uws/service/UWS.java b/src/uws/service/UWS.java
index 09d5e2456b4ab128bd2b56f1f6ff0ac7f046d658..a8d8bab6daafb61d5b7dde3795bc5bd7c7188985 100644
--- a/src/uws/service/UWS.java
+++ b/src/uws/service/UWS.java
@@ -16,7 +16,7 @@ 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-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -64,7 +64,7 @@ import uws.service.request.UWSRequestParser;
  * </b></p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (02/2015)
+ * @version 4.2 (06/2017)
  */
 public interface UWS extends Iterable<JobList> {
 
@@ -80,6 +80,11 @@ public interface UWS extends Iterable<JobList> {
 	 * @since 4.1 */
 	public static final String REQ_ATTRIBUTE_USER = "UWS_USER";
 
+	/** Attribute of the HttpServletRequest to set and to get in order to access the Job-Description (generally in XML)
+	 * sent instead of the "normal" HTTP-POST/-PUT parameters in the HTTP request body.
+	 * @since 4.2 */
+	public static final String REQ_ATTRIBUTE_JOB_DESCRIPTION = "UWS_JOB_DESCRIPTION";
+
 	/**
 	 * Gets the name of this UWS.
 	 * 
@@ -105,7 +110,7 @@ public interface UWS extends Iterable<JobList> {
 	 * 	In brief, this function should release all used resources.
 	 * </p>
 	 * 
-	 * <p><b>IMPORTANT: This function should be called only when the JVM or the Web Application Server is stopping.</b></p> 
+	 * <p><b>IMPORTANT: This function should be called only when the JVM or the Web Application Server is stopping.</b></p>
 	 * 
 	 * <p><i>Note:
 	 * 	A call to this function may prevent this instance of {@link UWS} to execute any subsequent HTTP request, or the behavior
diff --git a/src/uws/service/UWSServlet.java b/src/uws/service/UWSServlet.java
index 2b1fd209d98906162961926afeaf021f0ee3d5a4..d7660548f7c7713c6952c2b067240f38190818aa 100644
--- a/src/uws/service/UWSServlet.java
+++ b/src/uws/service/UWSServlet.java
@@ -16,7 +16,7 @@ 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-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -48,6 +48,7 @@ import uws.job.JobList;
 import uws.job.JobThread;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.jobInfo.JobInfo;
 import uws.job.parameters.DestructionTimeController;
 import uws.job.parameters.DestructionTimeController.DateField;
 import uws.job.parameters.ExecutionDurationController;
@@ -102,7 +103,7 @@ import uws.service.request.UploadFile;
  * 		addJobList(new JobList("jobList"));
  * 	}
  * 
- * 	// Create the job process corresponding to the job to execute ; generally, the process identification can be merely done by checking the job list name. 
+ * 	// Create the job process corresponding to the job to execute ; generally, the process identification can be merely done by checking the job list name.
  * 	public JobThread createJobThread(UWSJob job) throws UWSException {
  * 		if (job.getJobList().getName().equals("jobList"))
  * 			return new MyJobThread(job);
@@ -149,7 +150,7 @@ import uws.service.request.UploadFile;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.2 (02/2016)
+ * @version 4.2 (06/2017)
  */
 public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory {
 	private static final long serialVersionUID = 1L;
@@ -467,7 +468,7 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 			/*
 			 *   Any known/"expected" UWS exception is logged but also returned to the HTTP client in an error document.
 			 *   Since the error is known, it is supposed to have already been logged with a full stack trace. Thus, there
-			 * is no need to log again its stack trace...just its message is logged. 
+			 * is no need to log again its stack trace...just its message is logged.
 			 *   Besides, this error may also be just a redirection and not a true error. In such case, the error message
 			 * is not logged.
 			 */
@@ -480,7 +481,7 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 			 *   If this exception happens, the library tried to rewrite the HTTP response body with a message or a result,
 			 * while this body has already been partially sent to the client. It is then no longer possible to change its content.
 			 *   Consequently, the error is logged as FATAL and a message will be appended at the end of the already submitted response
-			 * to alert the HTTP client that an error occurs and the response should not be considered as complete and reliable. 
+			 * to alert the HTTP client that an error occurs and the response should not be considered as complete and reliable.
 			 */
 			// Write the error in the response and return the appropriate HTTP status code:
 			errorWriter.writeError(ise, resp, req, reqID, user, uwsAction);
@@ -643,7 +644,8 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 						input.close();
 				}
 			}
-		}// ERROR DETAILS CASE: Display the full stack trace of the error:
+		}
+		// ERROR DETAILS CASE: Display the full stack trace of the error:
 		else if (attributes[0].equalsIgnoreCase(UWSJob.PARAM_ERROR_SUMMARY) && attributes.length > 1 && attributes[1].equalsIgnoreCase("details")){
 			ErrorSummary error = job.getErrorSummary();
 			if (error == null)
@@ -661,7 +663,16 @@ 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):
+		}
+		// JOB INFO: Display the content of the JobInfo field (if any):
+		else if (attributes[0].equalsIgnoreCase(UWSJob.PARAM_JOB_INFO)){
+
+			if (job.getJobInfo() == null)
+				resp.sendError(HttpServletResponse.SC_NO_CONTENT);
+			else
+				job.getJobInfo().write(resp);
+		}
+		// 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)?://"))
@@ -752,7 +763,15 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory
 
 	@Override
 	public UWSJob createJob(HttpServletRequest request, JobOwner user) throws UWSException{
-		return new UWSJob(user, createUWSParameters(request));
+		// Create the job:
+		UWSJob newJob = new UWSJob(user, createUWSParameters(request));
+
+		// Set the XML job description if any:
+		Object jobDesc = request.getAttribute(UWS.REQ_ATTRIBUTE_JOB_DESCRIPTION);
+		if (jobDesc != null && jobDesc instanceof JobInfo)
+			newJob.setJobInfo((JobInfo)jobDesc);
+
+		return newJob;
 	}
 
 	@Override
diff --git a/src/uws/service/actions/GetJobParam.java b/src/uws/service/actions/GetJobParam.java
index be56b1ad5074d577a4d56c65d74867d497537257..ca38cfcd265eb8f19101ee5ba3039de3852f48ed 100644
--- a/src/uws/service/actions/GetJobParam.java
+++ b/src/uws/service/actions/GetJobParam.java
@@ -16,7 +16,7 @@ package uws.service.actions;
  * 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-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -32,6 +32,7 @@ import uws.UWSToolBox;
 import uws.job.ErrorSummary;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.jobInfo.JobInfo;
 import uws.job.serializer.UWSSerializer;
 import uws.job.user.JobOwner;
 import uws.service.UWSService;
@@ -50,7 +51,7 @@ import uws.service.request.UploadFile;
  * The serializer is choosen in function of the HTTP Accept header.</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (04/2015)
+ * @version 4.2 (06/2017)
  */
 public class GetJobParam extends UWSAction {
 	private static final long serialVersionUID = 1L;
@@ -99,6 +100,7 @@ public class GetJobParam extends UWSAction {
 	 * @see #getJob(UWSUrl)
 	 * @see UWSService#getSerializer(String)
 	 * @see UWSJob#serialize(ServletOutputStream, UWSSerializer)
+	 * @see JobInfo#write(HttpServletResponse)
 	 * 
 	 * @see uws.service.actions.UWSAction#apply(UWSUrl, JobOwner, HttpServletRequest, HttpServletResponse)
 	 */
@@ -129,7 +131,8 @@ public class GetJobParam extends UWSAction {
 						input.close();
 				}
 			}
-		}// ERROR DETAILS CASE: Display the full stack trace of the error:
+		}
+		// ERROR DETAILS CASE: Display the full stack trace of the error:
 		else if (attributes[0].equalsIgnoreCase(UWSJob.PARAM_ERROR_SUMMARY) && attributes.length > 1 && attributes[1].equalsIgnoreCase("details")){
 			ErrorSummary error = job.getErrorSummary();
 			if (error == null)
@@ -147,7 +150,15 @@ public class GetJobParam extends UWSAction {
 						input.close();
 				}
 			}
-		}// REFERENCE FILE: Display the content of the uploaded file or redirect to the URL (if it is a URL):
+		}
+		// JOB INFO: Display the content of the JobInfo field (if any):
+		else if (attributes[0].equalsIgnoreCase(UWSJob.PARAM_JOB_INFO)){
+			if (job.getJobInfo() == null)
+				response.sendError(HttpServletResponse.SC_NO_CONTENT);
+			else
+				job.getJobInfo().write(response);
+		}
+		// 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)?://"))
@@ -165,7 +176,8 @@ public class GetJobParam extends UWSAction {
 						input.close();
 				}
 			}
-		}// DEFAULT CASE: Display the serialization of the selected UWS object:
+		}
+		// 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/backup/DefaultUWSBackupManager.java b/src/uws/service/backup/DefaultUWSBackupManager.java
index 22393f81868e0b9e3e9b9901db83f4580be38e76..d749be5f45692539b4cc2d3aee527560a77dc785 100644
--- a/src/uws/service/backup/DefaultUWSBackupManager.java
+++ b/src/uws/service/backup/DefaultUWSBackupManager.java
@@ -16,14 +16,19 @@ package uws.service.backup;
  * 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,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.PrintWriter;
+import java.io.Serializable;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Date;
@@ -41,6 +46,9 @@ import org.json.JSONTokener;
 import org.json.JSONWriter;
 import org.json.Json4Uws;
 
+import com.oreilly.servlet.Base64Decoder;
+import com.oreilly.servlet.Base64Encoder;
+
 import uws.ISO8601Format;
 import uws.UWSException;
 import uws.UWSToolBox;
@@ -49,6 +57,7 @@ import uws.job.ErrorType;
 import uws.job.JobList;
 import uws.job.Result;
 import uws.job.UWSJob;
+import uws.job.jobInfo.JobInfo;
 import uws.job.parameters.UWSParameters;
 import uws.job.user.JobOwner;
 import uws.service.UWS;
@@ -77,7 +86,7 @@ import uws.service.request.UploadFile;
  * <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 (12/2014)
+ * @version 4.2 (06/2017)
  */
 public class DefaultUWSBackupManager implements UWSBackupManager {
 
@@ -571,9 +580,92 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 		// Add the name of the job list owning the given job:
 		jsonJob.put("jobListName", jlName);
 
+		// ReSet jobInfo to a boolean field:
+		if (job.getJobInfo() != null)
+			jsonJob.put(UWSJob.PARAM_JOB_INFO, getJSONJobInfo(job.getJobInfo()));
+		else
+			jsonJob.remove(UWSJob.PARAM_JOB_INFO);
+
 		return jsonJob;
 	}
 
+	/**
+	 * Serialize the given {@link JobInfo} so that being able later to restore this exact object as provided.
+	 * 
+	 * <p><i>
+	 * 	By default, this function use the Java Class serialization (see {@link Serializable})
+	 * 	and save the corresponding bytes into a Base-64 string.
+	 * </i></p>
+	 * 
+	 * @param jobInfo	The jobInfo to backup.
+	 * 
+	 * @return	The string to use in order to restore the given jobInfo
+	 *        	(e.g. a Base-64 serialization of the Java Object, a URL, ...).
+	 * 
+	 * @throws UWSException		If any error occurs while representing the given {@link JobInfo}.
+	 * @throws JSONException	If any error occurs while manipulating a JSON object or array.
+	 * 
+	 * @since 4.2
+	 */
+	protected Object getJSONJobInfo(final JobInfo jobInfo) throws UWSException, JSONException{
+		ByteArrayOutputStream bArray = null;
+		ObjectOutputStream oOutput = null;
+		try{
+			bArray = new ByteArrayOutputStream();
+			oOutput = new ObjectOutputStream(bArray);
+			oOutput.writeObject(jobInfo);
+			oOutput.flush();
+			return Base64Encoder.encode(bArray.toByteArray());
+		}catch(IOException ioe){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Unexpected error while serializing the given JobInfo!");
+		}finally{
+			if (oOutput != null){
+				try{
+					oOutput.close();
+				}catch(IOException ioe){}
+			}
+			if (bArray != null){
+				try{
+					bArray.close();
+				}catch(IOException ioe){}
+			}
+		}
+	}
+
+	/**
+	 * Restore the JobInfo referenced or represented by the given JSON value.
+	 * 
+	 * <p><i>
+	 * 	By default, this function considers that the given value is a Base-64 string encoding
+	 * 	the Java Class serialization (see {@link Serializable}) of the {@link JobInfo} to restore.
+	 * </i></p>
+	 * 
+	 * @param jsonValue	The reference or backup representation of the {@link JobInfo} to restore.
+	 * 
+	 * @return	The restored {@link JobInfo}.
+	 * 
+	 * @throws UWSException		If any error occurs while restoring the {@link JobInfo}.
+	 * @throws JSONException	If any error occurs while manipulating a JSON object or array.
+	 * 
+	 * @since 4.2
+	 */
+	protected JobInfo restoreJobInfo(final Object jsonValue) throws UWSException, JSONException{
+		ObjectInputStream oInput = null;
+		try{
+			byte[] bArray = Base64Decoder.decodeToBytes((String)jsonValue);
+			oInput = new ObjectInputStream(new ByteArrayInputStream(bArray));
+			return (JobInfo)oInput.readObject();
+		}catch(Exception ex){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ex, "Unexpected error while restoring a JobInfo!");
+		}finally{
+			if (oInput != null){
+				try{
+					oInput.close();
+				}catch(IOException ioe){}
+			}
+		}
+	}
+
 	/**
 	 * Get the JSON representation of the given {@link UploadFile}.
 	 * 
@@ -817,12 +909,15 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 
 		String jobListName = null, jobId = null, ownerID = null, tmp;
 		//Date destruction=null;
-		long quote = UWSJob.UNLIMITED_DURATION, /*duration = UWSJob.UNLIMITED_DURATION, */startTime = -1, endTime = -1;
+		long quote = UWSJob.UNLIMITED_DURATION,
+				/*duration = UWSJob.UNLIMITED_DURATION, */startTime = -1,
+				endTime = -1;
 		HashMap<String,Object> inputParams = new HashMap<String,Object>(10);
 		//Map<String, Object> params = null;
 		ArrayList<Result> results = null;
 		ErrorSummary error = null;
 		JSONArray uploads = null;
+		JobInfo jobInfo = null;
 
 		String[] keys = JSONObject.getNames(json);
 		for(String key : keys){
@@ -902,6 +997,11 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 				else if (key.equalsIgnoreCase(UWSJob.PARAM_ERROR_SUMMARY)){
 					error = getError(json.getJSONObject(key));
 
+				}
+				// key=JOB_INFO:
+				else if (key.equalsIgnoreCase(UWSJob.PARAM_JOB_INFO)){
+					jobInfo = restoreJobInfo(json.get(key));
+
 				}// Ignore any other key but with a warning message:
 				else
 					getLogger().logUWS(LogLevel.WARNING, json, "RESTORATION", "The job attribute '" + key + "' has been ignored because unknown! A job may be not completely restored!", null);
@@ -962,6 +1062,10 @@ public class DefaultUWSBackupManager implements UWSBackupManager {
 			// Create the job:
 			UWSJob job = uws.getFactory().createJob(jobId, owner, uwsParams, quote, startTime, endTime, results, error);
 
+			// Set its jobInfo, if any:
+			if (jobInfo != null)
+				job.setJobInfo(jobInfo);
+
 			// Restore other job params if needed:
 			restoreOtherJobParams(json, job);
 
diff --git a/src/uws/service/request/UWSRequestParser.java b/src/uws/service/request/UWSRequestParser.java
index 89ea7d9eb3d41d159d9e2d9dae1a9337077cf4b8..18832ade54cfffffec4a46f9491001e2ab788e3f 100644
--- a/src/uws/service/request/UWSRequestParser.java
+++ b/src/uws/service/request/UWSRequestParser.java
@@ -16,7 +16,7 @@ package uws.service.request;
  * 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)
+ * Copyright 2014-2017 - Astronomisches Rechen Institut (ARI)
  */
 
 import java.util.HashMap;
@@ -29,50 +29,64 @@ 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>
+ * This parser adapts the request parser to use in function of the request
+ * content-type:
+ * 
  * <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>
+ * 	<li><b>(text|application)/(.+-)?xml</b>: {@link XMLRequestParser}
+ * 			(the whole request body is an XML document)</li>
+ * 	<li><b>other</b>: no parameter is returned</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> 
+ * 	The request body size is limited for the multipart AND the XML-Request
+ * 	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 XMLRequestParser#SIZE_LIMIT}
+ * 	(and also {@link XMLRequestParser#SMALL_XML_THRESHOLD}).
+ * </p>
  * 
  * <p><i>Note:
- * 	If you want to change the support other request parsing, you will have to write your own {@link RequestParser} implementation.
+ * 	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)
+ * @version 4.2 (06/2017)
  * @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. */
+	 * 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()}. */
+	/** {@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)}
+	/** {@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;
+	/** {@link RequestParser} to use for XML request (i.e. a HTTP request
+	 * containing just an XML document). This attribute is set by
+	 * {@link #parse(HttpServletRequest)} only when needed, by calling the
+	 * function {@link #getXMLRequestParser()}. */
+	private RequestParser xmlRequestParser = null;
 
 	/**
-	 * Build a {@link RequestParser} able to choose the most appropriate {@link RequestParser} in function of the request content-type.
+	 * 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>
+	 * @param fileManager	The file manager to use in order to store any
+	 *                   	eventual upload. <i>Must NOT be NULL.</i>
 	 */
 	public UWSRequestParser(final UWSFileManager fileManager){
 		if (fileManager == null)
@@ -96,8 +110,10 @@ public final class UWSRequestParser implements RequestParser {
 				params = getFormParser().parse(req);
 			else if (MultipartParser.isMultipartContent(req))
 				params = getMultipartParser().parse(req);
+			else if (XMLRequestParser.isXMLRequest(req))
+				params = getXMLRequestParser().parse(req);
 			else
-				params = getNoEncodingParser().parse(req);
+				params = new HashMap<String,Object>(0);
 
 			// Only for POST requests, the parameters specified in the URL must be added:
 			if (method.equals("post"))
@@ -109,33 +125,41 @@ public final class UWSRequestParser implements RequestParser {
 	}
 
 	/**
-	 * Get the {@link RequestParser} to use for application/x-www-form-urlencoded HTTP requests.
+	 * 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>
+	 * @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.
+	 * 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>
+	 * @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.
+	 * Get the {@link RequestParser} to use for HTTP requests whose the content
+	 * is an XML document.
 	 * 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>
+	 * @return	The {@link RequestParser} to use for XML requests.
+	 *        	<i>Never NULL</i>
+	 * 
+	 * @since 4.2
 	 */
-	private synchronized final RequestParser getNoEncodingParser(){
-		return (noEncodingParser == null) ? (noEncodingParser = new NoEncodingParser(fileManager)) : noEncodingParser;
+	private synchronized final RequestParser getXMLRequestParser(){
+		return (xmlRequestParser == null) ? (xmlRequestParser = new XMLRequestParser(fileManager)) : xmlRequestParser;
 	}
 
 }
diff --git a/src/uws/service/request/XMLRequestParser.java b/src/uws/service/request/XMLRequestParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..448ebbcbbfa295bb07b55e03448d87217c544d74
--- /dev/null
+++ b/src/uws/service/request/XMLRequestParser.java
@@ -0,0 +1,372 @@
+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 2017 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+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 javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import uws.UWSException;
+import uws.job.jobInfo.XMLJobInfo;
+import uws.service.UWS;
+import uws.service.file.UWSFileManager;
+
+/**
+ * This parser aims to copy the full content of an HTTP request if it is
+ * identified as an XML document.
+ * 
+ * <p><b>UWS's Job Description</b></p>
+ * 
+ * <p>
+ * 	Actually, this parser implements the possibility defined in the UWS 1.0
+ * 	standard to provide an XML document describing the parameters of a UWS job.
+ * 	This XML document is then called "Job Description".
+ * </p>
+ * 
+ * <p><b>Validation</b></p>
+ * 
+ * <p>
+ * 	In the UWS 1.0 standard, it is said that this Job Description has to follow
+ * 	a Job Description Language (JDL ; that's to say a known pattern describing
+ * 	the expected job parameters) dependent of the UWS service implementation.
+ * </p>
+ * 
+ * <p>
+ * 	By default, this parser copies the request content and checks it is an XML
+ * 	document. Nothing else is done, and particularly not the validation of
+ * 	its content. To do so, a particular service implementation can extend this
+ * 	class and overwrite its function {@link #validate(InputStream)}. By default
+ * 	this function just ensures the request content is a valid XML document.
+ * </p>
+ * 
+ * <p><b>Document access</b></p>
+ * 
+ * <p>
+ * 	Once parsed, the request content will be made accessible through an
+ * 	{@link HttpServletRequest} attribute under the name
+ * 	<b>{@value uws.service.UWS#REQ_ATTRIBUTE_JOB_DESCRIPTION}</b>.
+ * 	The associated object <b>is typed as an {@link XMLJobInfo}</b>.
+ * </p>
+ * 
+ * <p><i>Note:
+ * 	Afterwards, it is intended to be attached to a {@link uws.job.UWSJob} and
+ * 	then made accessible through its function
+ * 	{@link uws.job.UWSJob#getJobInfo()}.
+ * </i></p>
+ * 
+ * <p><b>Document storage</b></p>
+ * 
+ * <p>{@link XMLJobInfo} gives two storage possibility:</p>
+ * <ol>
+ * 	<li><i>in memory</i> with the constructor
+ * 		{@link XMLJobInfo#XMLJobInfo(String) XMLJobInfo(String)}</li>
+ * 	<li><i>in a file</i> with the constructor
+ * 		{@link XMLJobInfo#XMLJobInfo(UploadFile) XMLJobInfo(UploadFile)}</li>
+ * </ol>
+ * 
+ * <p>
+ * 	The storage chosen by this parser depends on the size of the input document.
+ * 	If it exceeds {@link #SMALL_XML_THRESHOLD} (expressed in bytes), then
+ * 	the document will be stored inside a file. Otherwise it will be kept in
+ * 	memory. To change this threshold, it is just needed to set the static
+ * 	field {@link #SMALL_XML_THRESHOLD} to the desired value (in bytes).
+ * 	By default, it is set to {@value #DEFAULT_SMALL_XML_THRESHOLD} bytes.
+ * </p>
+ * 
+ * <p><b>Important:</b>
+ * 	It is possible to prevent the unwanted storage of a very large document
+ * 	by setting the limit {@link #SIZE_LIMIT} to a different value (in bytes).
+ * 	If the input document exceeds this size, the request will be rejected with
+ * 	an 413 (REQUEST ENTITY TOO LARGE) error.
+ * 	By default this limit is set to {@value #DEFAULT_SIZE_LIMIT} bytes.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.2 (06/2017)
+ * @since 4.2
+ */
+public class XMLRequestParser implements RequestParser {
+
+	/** Default maximum allowed size for an HTTP request content: 200 kiB. */
+	public static final int DEFAULT_SIZE_LIMIT = 200 * 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} Bytes).
+	 * </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;
+
+	/** Default threshold for XML document that can be kept entirely in memory: 2 kiB. */
+	public static final int DEFAULT_SMALL_XML_THRESHOLD = 2 * 1024;
+
+	/** This threshold determines whether an XML request content should be
+	 * stored in memory or inside a file.
+	 * <p><i>In short: between 0 and this value, the
+	 * XML document will be stored in memory ; above this value, it will be
+	 * stored in a file.</i></p> */
+	public static int SMALL_XML_THRESHOLD = DEFAULT_SMALL_XML_THRESHOLD;
+
+	/** 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. <i>Must NOT be NULL.</i>
+	 */
+	public XMLRequestParser(final UWSFileManager fileManager){
+		if (fileManager == null)
+			throw new NullPointerException("Missing file manager => can not create an XMLRequestParser!");
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	public Map<String,Object> parse(final HttpServletRequest request) throws UWSException{
+		// Result of the request parsing => a JobInfo containing or pointing toward the sent request content:
+		XMLJobInfo jobDesc = null;
+
+		// Prepare to write a file if the XML is too large to fit in memory:
+		// (note: this file has to be deleted if not used or in case or error)
+		Object reqID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID);
+		if (reqID == null || !(reqID instanceof String))
+			reqID = (new Date()).getTime();
+		File xmlFile = new File(UWSFileManager.TMP_UPLOAD_DIR, "JOB_DESCRIPTION_" + reqID);
+
+		OutputStream output = null;
+		InputStream input = null;
+		long totalLength = 0;
+		try{
+			// prepare the reading of the HTTP request body:
+			input = new BufferedInputStream(request.getInputStream());
+
+			// open in WRITE access the output file:
+			output = new BufferedOutputStream(new FileOutputStream(xmlFile));
+
+			// compute the maximum limit and the memory size threshold:
+			final int maxSize = (SIZE_LIMIT < 0 ? DEFAULT_SIZE_LIMIT : SIZE_LIMIT);
+			final int memoryThreshold = (SMALL_XML_THRESHOLD < 0 ? DEFAULT_SMALL_XML_THRESHOLD : SMALL_XML_THRESHOLD);
+			final String tooLargeErrorMsg = "XML document too large (>" + maxSize + " bytes) => Request rejected! You should see with the service administrator to extend this limit.";
+
+			// Start reading the HTTP request body:
+			byte[] buffer = new byte[memoryThreshold + 1];
+			int len = input.read(buffer);
+
+			// If nothing, no body and no parameter => stop here immediately:
+			if (len <= 0){
+				output.close();
+				output = null;
+				xmlFile.delete();
+			}
+			// If the HTTP request body is already finished => small document => memory storage:
+			else if (len <= memoryThreshold){
+				output.close();
+				output = null;
+				xmlFile.delete();
+				if (len > maxSize)
+					throw new UWSException(UWSException.REQUEST_ENTITY_TOO_LARGE, tooLargeErrorMsg);
+				else{
+					// Build the corresponding String:
+					String smallXML = new String(buffer, 0, len, (request.getCharacterEncoding() != null ? request.getCharacterEncoding() : "UTF-8"));
+
+					// Check it is really an XML document:
+					validate(smallXML);
+
+					// Finally build the corresponding Job-Description:
+					jobDesc = new XMLJobInfo(smallXML);
+				}
+			}
+			// Otherwise....
+			else{
+				// ...store the full content inside the temporary file
+				// until the EOF or a length exceed:
+				do{
+					output.write(buffer, 0, len);
+					totalLength += len;
+					// if content too large => stop here with an error:
+					if (totalLength > maxSize){
+						output.close();
+						output = null;
+						xmlFile.delete();
+						throw new UWSException(UWSException.REQUEST_ENTITY_TOO_LARGE, tooLargeErrorMsg);
+					}
+				}while((len = input.read(buffer)) > 0);
+				output.flush();
+				output.close();
+				output = null;
+
+				// Check the file is really an XML document:
+				validate(xmlFile);
+
+				// Create a UWS wrapping for this uploaded file:
+				UploadFile xmlUpload = new UploadFile(UWS.REQ_ATTRIBUTE_JOB_DESCRIPTION, xmlFile.toURI().toString(), fileManager);
+				xmlUpload.mimeType = request.getContentType();
+				xmlUpload.length = totalLength;
+
+				// And create the corresponding jobInfo:
+				jobDesc = new XMLJobInfo(xmlUpload);
+			}
+		}catch(IOException ioe){
+			xmlFile.delete();
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe, "Internal error => Impossible to get the XML document from the HTTP request!");
+		}catch(UWSException ue){
+			xmlFile.delete();
+			throw ue;
+		}finally{
+			if (output != null){
+				try{
+					output.close();
+				}catch(IOException ioe2){}
+			}
+			if (input != null){
+				try{
+					input.close();
+				}catch(IOException ioe2){}
+			}
+		}
+
+		// Put the job description in a HttpServletRequest attribute:
+		if (jobDesc != null)
+			request.setAttribute(UWS.REQ_ATTRIBUTE_JOB_DESCRIPTION, jobDesc);
+
+		// Return an empty map => no parameter has been directly provided:
+		return new HashMap<String,Object>(0);
+	}
+
+	/**
+	 * Validate the given XML document.
+	 * 
+	 * <p>
+	 * 	By default, it is only ensured this document is an XML one.
+	 * </p>
+	 * 
+	 * @param smallXML		The document to check.
+	 * 
+	 * @throws UWSException	If the given document is not valid.
+	 * 
+	 * @see {@link #validate(InputStream)}
+	 */
+	protected void validate(final String smallXML) throws UWSException{
+		validate(new ByteArrayInputStream(smallXML.getBytes()));
+	}
+
+	/**
+	 * Validate the specified XML document.
+	 * 
+	 * <p>
+	 * 	By default, it is only ensured this document is an XML one.
+	 * </p>
+	 * 
+	 * @param xmlFile		The file containing the document to check.
+	 * 
+	 * @throws UWSException	If the specified document is not valid.
+	 * 
+	 * @see {@link #validate(InputStream)}
+	 */
+	protected void validate(final File xmlFile) throws UWSException{
+		InputStream input = null;
+		try{
+			input = new FileInputStream(xmlFile);
+			validate(input);
+		}catch(IOException ioe){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ioe);
+		}finally{
+			if (input != null){
+				try{
+					input.close();
+				}catch(IOException ioe){}
+			}
+		}
+	}
+
+	/**
+	 * Validate the given XML document.
+	 * 
+	 * <p>
+	 * 	By default, it is only ensured this document is an XML one.
+	 * </p>
+	 * 
+	 * @param input			Stream toward the document to check.
+	 * 
+	 * @throws UWSException	If the given document is not valid.
+	 */
+	protected void validate(final InputStream input) throws UWSException{
+		SAXParserFactory spf = SAXParserFactory.newInstance();
+		spf.setNamespaceAware(true); // why not :)
+		spf.setValidating(false);    // no need to check the DTD or XSDs
+		SAXParser saxParser = null;
+		try{
+			saxParser = spf.newSAXParser();
+			saxParser.parse(input, new DefaultHandler());
+		}catch(SAXParseException spe){
+			throw new UWSException(UWSException.BAD_REQUEST, "Incorrect XML input! ERROR at [l." + spe.getLineNumber() + ", c." + spe.getColumnNumber() + "]: " + spe.getMessage() + ".");
+		}catch(Exception se){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, se);
+		}
+	}
+
+	/**
+	 * Utility method that determines whether the content of the given
+	 * request is an XML (or XML-derived) document.
+	 * 
+	 * <p><b>Important:</b>
+	 * 	This function just tests the content-type of the request.
+	 * 	Neither the HTTP method (e.g. GET, POST, ...) nor the content is tested.
+	 * </p>
+	 *
+	 * @param request	The servlet request to be evaluated.
+	 *               	<i>Must NOT be NULL.</i>
+	 *
+	 * @return	<i>true</i> if the request is an XML document,
+	 *        	<i>false</i> otherwise.
+	 */
+	public final static boolean isXMLRequest(final HttpServletRequest request){
+		// Extract the content type and determine if it is an XML request:
+		String contentType = request.getContentType();
+		if (contentType == null)
+			return false;
+		else if (contentType.toLowerCase().matches("(text|application)/(.+\\+)?xml"))
+			return true;
+		else
+			return false;
+	}
+
+}