diff --git a/src/tap/config/tap_configuration_file.html b/src/tap/config/tap_configuration_file.html
index a6e8cad0317edc1794d62a014a213350460a0cbe..979b08c35e1241b3006eaa4c91da8ed08d1b07d3 100644
--- a/src/tap/config/tap_configuration_file.html
+++ b/src/tap/config/tap_configuration_file.html
@@ -311,7 +311,7 @@
 				<td>
 					<p>Type of the file manager.</p>
 					<p>Accepted values are: local (to manage files on the local system).
-					You can also add another way to manage files by providing the name (within brackets: {...}) of a class implementing TAPFileManager and having at least one constructor with only a java.util.Properties parameter.</p>
+					You can also add another way to manage files by providing the name (within brackets: {...}) of a class implementing UWSFileManager and having at least one constructor with only a java.util.Properties parameter.</p>
 				</td>
 				<td><ul><li>local</li><li>{apackage.MyTAPFileManager}</li></ul></td>
 			</tr>
@@ -415,7 +415,7 @@
 	 				</em></p>
 	 				<p><em>Default: <code>D 0 0</code> (daily at midnight)</em></p>
 				</td>
-				<td><ul><li>D 6 30</li><li>W 2 6 30</li><li>M 2 6 30</li><li>H 10</li><li>m</li></ul></td>
+				<td><ul><li>D 6 30</li><li>W 2 6 30</li><li>M 2 6 30</li><li>h 10</li><li>m</li></ul></td>
 			</tr>
 			
 			<tr><td colspan="5">UWS Backup (only if tap_factory = ø)</td></tr>
diff --git a/src/tap/parameters/TAPParameters.java b/src/tap/parameters/TAPParameters.java
index e59109297780ef8b554347e69a0f85da0fab1d08..dc0503326d7aff917faa93194a8f2b10b69ea487 100644
--- a/src/tap/parameters/TAPParameters.java
+++ b/src/tap/parameters/TAPParameters.java
@@ -16,7 +16,7 @@ package tap.parameters;
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2012,2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -42,7 +42,7 @@ import uws.job.parameters.UWSParameters;
  * submitted by a TAP client to this TAP service.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (12/2014)
+ * @version 2.1 (06/2016)
  */
 public class TAPParameters extends UWSParameters {
 
@@ -87,7 +87,7 @@ public class TAPParameters extends UWSParameters {
 			// Deal with the UPLOAD parameter(s):
 			DALIUpload.getDALIUploads(params, true, service.getFileManager());
 
-			// Load all parameters:			
+			// Load all parameters:
 			Iterator<Entry<String,Object>> it = params.entrySet().iterator();
 			Entry<String,Object> entry;
 			try{
@@ -121,7 +121,7 @@ public class TAPParameters extends UWSParameters {
 		controllers.put(TAPJob.PARAM_EXECUTION_DURATION, new TAPExecutionDurationController(service));
 		controllers.put(TAPJob.PARAM_DESTRUCTION_TIME, new TAPDestructionTimeController(service));
 		controllers.put(TAPJob.PARAM_REQUEST, new StringParamController(TAPJob.PARAM_REQUEST, null, new String[]{TAPJob.REQUEST_DO_QUERY,TAPJob.REQUEST_GET_CAPABILITIES}, true));
-		controllers.put(TAPJob.PARAM_LANGUAGE, new StringParamController(TAPJob.PARAM_LANGUAGE, TAPJob.LANG_ADQL, null, true));
+		controllers.put(TAPJob.PARAM_LANGUAGE, new StringParamController(TAPJob.PARAM_LANGUAGE, TAPJob.LANG_ADQL, (String[])null, true));
 		controllers.put(TAPJob.PARAM_VERSION, new StringParamController(TAPJob.PARAM_VERSION, TAPJob.VERSION_1_0, new String[]{TAPJob.VERSION_1_0}, true));
 		controllers.put(TAPJob.PARAM_QUERY, new StringParamController(TAPJob.PARAM_QUERY));
 		controllers.put(TAPJob.PARAM_FORMAT, new FormatController(service));
@@ -136,7 +136,7 @@ public class TAPParameters extends UWSParameters {
 	 * <ul>
 	 * 	<li><b>NULL</b>: NULL is returned.</li>
 	 * 	<li><b>An array (of whatever is the items' type)</b>: a string in which each Object.toString() are concatenated ; each item is separated by a semicolon</li>
-	 * 	<li><b>Anything else</b>: Object.toString()</li> 
+	 * 	<li><b>Anything else</b>: Object.toString()</li>
 	 * </ul>
 	 * 
 	 * @param paramName	Name of the parameter whose the value must be returned as a String.
@@ -220,7 +220,7 @@ public class TAPParameters extends UWSParameters {
 	}
 
 	/**
-	 * Get the list of all tables uploaded and defined by the standard TAP parameter "UPLOAD". 
+	 * Get the list of all tables uploaded and defined by the standard TAP parameter "UPLOAD".
 	 * 
 	 * @return	Tables to upload in database at query execution.
 	 */
diff --git a/src/uws/config/ConfigurableUWSFactory.java b/src/uws/config/ConfigurableUWSFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6fc32571612b3d9d0980df8e8cc4ae9156eb124
--- /dev/null
+++ b/src/uws/config/ConfigurableUWSFactory.java
@@ -0,0 +1,670 @@
+package uws.config;
+
+/*
+ * 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 2016 - Astronomisches Rechen Institut (ARI)
+ */
+
+import static uws.config.UWSConfiguration.KEY_REGEXP_MAX_DESTRUCTION_INTERVAL;
+import static uws.config.UWSConfiguration.KEY_REQUEST_PARSER;
+import static uws.config.UWSConfiguration.REGEXP_DEFAULT_DESTRUCTION_INTERVAL;
+import static uws.config.UWSConfiguration.REGEXP_DEFAULT_EXEC_DURATION;
+import static uws.config.UWSConfiguration.REGEXP_JOB_THREAD;
+import static uws.config.UWSConfiguration.REGEXP_MAX_EXEC_DURATION;
+import static uws.config.UWSConfiguration.REGEXP_PARAMETERS;
+import static uws.config.UWSConfiguration.extractJobListName;
+import static uws.config.UWSConfiguration.fetchConstructor;
+import static uws.config.UWSConfiguration.getProperty;
+import static uws.config.UWSConfiguration.newInstance;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.text.ParseException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import uws.UWSException;
+import uws.job.ErrorSummary;
+import uws.job.JobThread;
+import uws.job.Result;
+import uws.job.UWSJob;
+import uws.job.parameters.DestructionTimeController;
+import uws.job.parameters.DurationParamController;
+import uws.job.parameters.ExecutionDurationController;
+import uws.job.parameters.InputParamController;
+import uws.job.parameters.NumericParamController;
+import uws.job.parameters.StringParamController;
+import uws.job.parameters.UWSParameters;
+import uws.job.user.JobOwner;
+import uws.service.UWS;
+import uws.service.UWSFactory;
+import uws.service.UWSUrl;
+import uws.service.file.UWSFileManager;
+import uws.service.request.RequestParser;
+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 (06/2016)
+ * @since 4.2
+ */
+public class ConfigurableUWSFactory implements UWSFactory {
+
+	/** Constructor of the {@link RequestParser} to create for the whole UWS service.
+	 * <p><i>If <code>null</code>, the default UWS request parser (i.e. {@link UWSRequestParser}) will be used.</i></p> */
+	protected Constructor<? extends RequestParser> constructorRequestParser = null;
+
+	/** Map associating the name of a job list with the constructor of the class of the jobs to create inside this job list. */
+	protected Map<String,Constructor<? extends JobThread>> jobThreads = new HashMap<String,Constructor<? extends JobThread>>(2);
+
+	/** List of parameters (and eventually how to control them) for each job-list/job. */
+	protected Map<String,Map<String,InputParamController>> jobParams = new HashMap<String,Map<String,InputParamController>>(2);
+
+	/** Create an empty factory.
+	 * 
+	 * <p>
+	 * 	Using this constructor, no {@link JobThread} can be created.
+	 * 	You have to add some in {@link #jobThreads} for each job list you have to support.
+	 * 	The parameters of each job/job-list can be listed in {@link #jobParams}.
+	 * </p>
+	 */
+	protected ConfigurableUWSFactory(){}
+
+	/**
+	 * Create and initialize a UWS factory thanks to properties coming from a UWS configuration file.
+	 * 
+	 * <p>The following properties are supported by this constructor:</p>
+	 * <ul>
+	 * 	<li>{@link UWSConfiguration#KEY_REQUEST_PARSER  KEY_REQUEST_PARSER}</li>
+	 * 	<li>{@link UWSConfiguration#REGEXP_JOB_THREAD  KEY_REGEXP_JOB_THREAD}</li>
+	 * 	<li>{@link UWSConfiguration#REGEXP_PARAMETERS  KEY_REGEXP_PARAMETERS}</li>
+	 * 	<li>{@link UWSConfiguration#REGEXP_DEFAULT_EXEC_DURATION  KEY_REGEXP_DEFAULT_EXEC_DURATION}</li>
+	 * 	<li>{@link UWSConfiguration#REGEXP_MAX_EXEC_DURATION  KEY_REGEXP_MAX_EXEC_DURATION}</li>
+	 * 	<li>{@link UWSConfiguration#REGEXP_DEFAULT_DESTRUCTION_INTERVAL  KEY_REGEXP_DEFAULT_DESTRUCTION_INTERVAL}</li>
+	 * 	<li>{@link UWSConfiguration#KEY_REGEXP_MAX_DESTRUCTION_INTERVAL  KEY_REGEXP_MAX_DESTRUCTION_INTERVAL}</li>
+	 * </ul>
+	 * 
+	 * @param uwsConfig	Configuration of this factory.
+	 * 
+	 * @throws UWSException	If the initialization fails.
+	 */
+	public ConfigurableUWSFactory(final Properties uwsConfig) throws UWSException{
+
+		/* 1. Extract the RequestParser */
+
+		String propValue = getProperty(uwsConfig, KEY_REQUEST_PARSER);
+		if (propValue != null)
+			constructorRequestParser = fetchConstructor(propValue, KEY_REQUEST_PARSER, RequestParser.class, new Class<?>[]{UWSFileManager.class});
+
+		/* 2. Extract information (job thread + job parameters + exec duration + destruction time) about all job lists */
+
+		String propName, jlName = null;
+		DurationParamController durationController = new DurationParamController();
+
+		@SuppressWarnings("unchecked")
+		Enumeration<String> propNames = (Enumeration<String>)uwsConfig.propertyNames();
+		while(propNames.hasMoreElements()){
+
+			propName = propNames.nextElement();
+
+			// Set job thread:
+			if (propName.matches(REGEXP_JOB_THREAD)){
+				jlName = extractJobListName(propName);
+				propValue = getProperty(uwsConfig, propName);
+
+				jobThreads.put(jlName, fetchConstructor(propValue, propName, JobThread.class, new Class<?>[]{UWSJob.class}));
+
+			}
+			// Set expected job parameters:
+			else if (propName.matches(REGEXP_PARAMETERS)){
+				jlName = extractJobListName(propName);
+				propValue = getProperty(uwsConfig, propName);
+
+				initParameters(jlName, propValue);
+
+			}
+			// Set the default execution duration:
+			else if (propName.matches(REGEXP_DEFAULT_EXEC_DURATION)){
+				jlName = extractJobListName(propName);
+				propValue = getProperty(uwsConfig, propName);
+
+				try{
+					// Get map of all controllers for this job list:
+					Map<String,InputParamController> mapControllers;
+					if (jobParams.containsKey(jlName))
+						mapControllers = jobParams.get(jlName);
+					else
+						mapControllers = new HashMap<String,InputParamController>();
+
+					// Get its execution duration controller:
+					ExecutionDurationController controller;
+					if (mapControllers.get(UWSJob.PARAM_EXECUTION_DURATION) != null && mapControllers.get(UWSJob.PARAM_EXECUTION_DURATION) instanceof ExecutionDurationController)
+						controller = (ExecutionDurationController)mapControllers.get(UWSJob.PARAM_EXECUTION_DURATION);
+					else
+						controller = new ExecutionDurationController();
+
+					// Set the default execution duration:
+					controller.setDefaultExecutionDuration(durationController.parseDuration(propValue));
+
+					// Update the map of controllers for this job list:
+					mapControllers.put(UWSJob.PARAM_EXECUTION_DURATION, controller);
+
+					// Update the map of all job lists' controllers:
+					jobParams.put(jlName, mapControllers);
+
+				}catch(NumberFormatException nfe){
+					throw new UWSException("Wrong numeric format for the default execution duration: \"" + propValue + "\"!");
+				}catch(ParseException pe){
+					throw new UWSException("Incorrect syntax for the default execution duration! Cause: " + pe.getMessage());
+				}
+
+			}
+			// Set the maximum execution duration:
+			else if (propName.matches(REGEXP_MAX_EXEC_DURATION)){
+				jlName = extractJobListName(propName);
+				propValue = getProperty(uwsConfig, propName);
+
+				try{
+					// Get map of all controllers for this job list:
+					Map<String,InputParamController> mapControllers;
+					if (jobParams.containsKey(jlName))
+						mapControllers = jobParams.get(jlName);
+					else
+						mapControllers = new HashMap<String,InputParamController>();
+
+					// Get its execution duration controller:
+					ExecutionDurationController controller;
+					if (mapControllers.get(UWSJob.PARAM_EXECUTION_DURATION) != null && mapControllers.get(UWSJob.PARAM_EXECUTION_DURATION) instanceof ExecutionDurationController)
+						controller = (ExecutionDurationController)mapControllers.get(UWSJob.PARAM_EXECUTION_DURATION);
+					else
+						controller = new ExecutionDurationController();
+
+					// Set the maximum execution duration:
+					controller.setMaxExecutionDuration(durationController.parseDuration(propValue));
+
+					// Update the map of controllers for this job list:
+					mapControllers.put(UWSJob.PARAM_EXECUTION_DURATION, controller);
+
+					// Update the map of all job lists' controllers:
+					jobParams.put(jlName, mapControllers);
+
+				}catch(NumberFormatException nfe){
+					throw new UWSException("Wrong numeric format for the maximum execution duration: \"" + propValue + "\"!");
+				}catch(ParseException pe){
+					throw new UWSException("Incorrect syntax for the maximum execution duration! Cause: " + pe.getMessage());
+				}
+
+			}
+			// Set the default destruction time:
+			else if (propName.matches(REGEXP_DEFAULT_DESTRUCTION_INTERVAL)){
+				jlName = extractJobListName(propName);
+				propValue = getProperty(uwsConfig, propName);
+
+				try{
+					// Get map of all controllers for this job list:
+					Map<String,InputParamController> mapControllers;
+					if (jobParams.containsKey(jlName))
+						mapControllers = jobParams.get(jlName);
+					else
+						mapControllers = new HashMap<String,InputParamController>();
+
+					// Get its destruction time controller:
+					DestructionTimeController controller;
+					if (mapControllers.get(UWSJob.PARAM_DESTRUCTION_TIME) != null && mapControllers.get(UWSJob.PARAM_DESTRUCTION_TIME) instanceof DestructionTimeController)
+						controller = (DestructionTimeController)mapControllers.get(UWSJob.PARAM_DESTRUCTION_TIME);
+					else
+						controller = new DestructionTimeController();
+
+					// Set the default destruction time:
+					controller.setDefaultDestructionInterval((int)durationController.parseDuration(propValue));
+
+					// Update the map of controllers for this job list:
+					mapControllers.put(UWSJob.PARAM_DESTRUCTION_TIME, controller);
+
+					// Update the map of all job lists' controllers:
+					jobParams.put(jlName, mapControllers);
+
+				}catch(NumberFormatException nfe){
+					throw new UWSException("Wrong numeric format for the default destruction time: \"" + propValue + "\"!");
+				}catch(ParseException pe){
+					throw new UWSException("Incorrect syntax for the default destruction time! Cause: " + pe.getMessage());
+				}
+
+			}
+			// Set the maximum destruction time:
+			else if (propName.matches(KEY_REGEXP_MAX_DESTRUCTION_INTERVAL)){
+				jlName = extractJobListName(propName);
+				propValue = getProperty(uwsConfig, propName);
+
+				try{
+					// Get map of all controllers for this job list:
+					Map<String,InputParamController> mapControllers;
+					if (jobParams.containsKey(jlName))
+						mapControllers = jobParams.get(jlName);
+					else
+						mapControllers = new HashMap<String,InputParamController>();
+
+					// Get its destruction time controller:
+					DestructionTimeController controller;
+					if (mapControllers.get(UWSJob.PARAM_DESTRUCTION_TIME) != null && mapControllers.get(UWSJob.PARAM_DESTRUCTION_TIME) instanceof DestructionTimeController)
+						controller = (DestructionTimeController)mapControllers.get(UWSJob.PARAM_DESTRUCTION_TIME);
+					else
+						controller = new DestructionTimeController();
+
+					// Set the maximum destruction time:
+					controller.setMaxDestructionInterval((int)durationController.parseDuration(propValue));
+
+					// Update the map of controllers for this job list:
+					mapControllers.put(UWSJob.PARAM_DESTRUCTION_TIME, controller);
+
+					// Update the map of all job lists' controllers:
+					jobParams.put(jlName, mapControllers);
+
+				}catch(NumberFormatException nfe){
+					throw new UWSException("Wrong numeric format for the maximum destruction time: \"" + propValue + "\"!");
+				}catch(ParseException pe){
+					throw new UWSException("Incorrect syntax for the maximum destruction time! Cause: " + pe.getMessage());
+				}
+
+			}
+		}
+	}
+
+	/**
+	 * Regular Expression of a job parameters list.
+	 * 
+	 * <p>The following parameter types are supported:</p>
+	 * <ul>
+	 * 	<li><em>Just a parameter name (i.e. no controller):</em>
+	 * 		<pre>\s*([^,\[\]]*)\s*(,(.*))?</pre>
+	 * 		<p>Example: <code>param1</code></p>
+	 * 		<ul>
+	 * 			<li><em>group(16) = </em>parameter name</li>
+	 * 			<li><em>group(18) = </em>all other parameters (without the leading comma)</li>
+	 * 		</ul>
+	 * 	</li>
+	 * 	<li><em>A parameter name and the class of an {@link InputParamController}:</em>
+	 * 		<pre>\s*\[([^,]+),(\{[^\}]*\})\s*\]\s*(,(.*))?</pre>
+	 * 		<p>Example: <code>[param1, {aPackage.MyCustomController}]</code></p>
+	 * 		<ul>
+	 * 			<li><em>group(2) = </em>parameter name</li>
+	 * 			<li><em>group(4) = </em>controller class name</li>
+	 * 		</ul>
+	 * 	</li>
+	 * 	<li><em>A parameter name and description of a string controller:</em>
+	 * 		<pre>\s*\[([^,]+),([^,]*),\s*(string)\s*,\s*"([^,]*)"\s*,\s* /(([^\/]|//)*)/(i)?\s*\]\s*(,(.*))?</pre>
+	 * 		<p>Example: <code>[param1, true, string, "default", /mySuperRegexp/i]</code></p>
+	 * 		<ul>
+	 * 			<li><em>group(2) = </em>parameter name</li>
+	 * 			<li><em>group(5) = </em>a boolean expression (yes, y, true, t) to indicate whether the parameter can be modified by the user</li>
+	 * 			<li><em>group(11) = </em><code>string</code></li>
+	 * 			<li><em>group(12) = </em>default value (it must be provided between brackets as in the example above)</li>
+	 * 			<li><em>group(13) = </em>the regular expression</li>
+	 * 			<li><em>group(15) = </em><code>i</code> if the regular expression must be evaluated in a case insensitive manner</li>
+	 * 		</ul>
+	 * 	</li>
+	 * 	<li><em>A parameter name and description of a numeric controller:</em>
+	 * 		<pre>\s*\[([^,]+),([^,]*),\s*(numeric)\s*,([^,]*),([^,]*),([^,]*)\s*\]\s*(,(.*))?</pre>
+	 * 		<p>Example: <code>[param1, true, numeric, 10, 0, 20]</code></p>
+	 * 		<ul>
+	 * 			<li><em>group(2) = </em>parameter name</li>
+	 * 			<li><em>group(5) = </em>a boolean expression (yes, y, true, t) to indicate whether the parameter can be modified by the user</li>
+	 * 			<li><em>group(7) = </em><code>numeric</code></li>
+	 * 			<li><em>group(8) = </em>default value ; only a numeric value (floating number or not ; negative or not) will be allowed after the expression has been parsed</li>
+	 * 			<li><em>group(9) = </em>minimum value ; only a numeric value (floating number or not ; negative or not) will be allowed after the expression has been parsed</li>
+	 * 			<li><em>group(10) = </em>maximum value ; only a numeric value (floating number or not ; negative or not) will be allowed after the expression has been parsed</li>
+	 * 		</ul>
+	 * 	</li>
+	 * 	<li><em>A parameter name and description of a duration controller:</em>
+	 * 		<pre>\s*\[([^,]+),([^,]*),\s*(duration)\s*,([^,]*),([^,]*),([^,]*)\s*\]\s*(,(.*))?</pre>
+	 * 		<p>Example: <code>[param1, true, numeric, 10, 0, 20]</code></p>
+	 * 		<ul>
+	 * 			<li><em>group(2) = </em>parameter name</li>
+	 * 			<li><em>group(5) = </em>a boolean expression (yes, y, true, t) to indicate whether the parameter can be modified by the user</li>
+	 * 			<li><em>group(7) = </em><code>numeric</code></li>
+	 * 			<li><em>group(8) = </em>default value</li>
+	 * 			<li><em>group(9) = </em>minimum value</li>
+	 * 			<li><em>group(10) = </em>maximum value</li>
+	 * 		</ul>
+	 * 		<p>The default, minimum and maximum value must a positive integer value followed eventually by a unit. If no unit is specified, the default is <code>milliseconds</code>.
+	 * 			See below for all supported units:</p>
+	 * 		<ul>
+	 * 			<li>milliseconds, ms</li>
+	 * 			<li>seconds, sec, s</li>
+	 * 			<li>minutes, min, m</li>
+	 * 			<li>hours, h</li>
+	 * 			<li>days, D</li>
+	 * 			<li>weeks, W</li>
+	 * 			<li>months, M</li>
+	 * 			<li>years, Y</li>
+	 * 		</ul>
+	 * 	</li>
+	 * </ul>
+	 */
+	protected static final Pattern PATTERN_PARAMETER = Pattern.compile("\\s*(\\[([^,]+),(\\s*(\\{[^\\}]*\\})|([^,]*),(\\s*(numeric|duration)\\s*,([^,]*),([^,]*),([^,]*)|\\s*(string)\\s*,\\s*\"([^\"]*)\"\\s*,\\s*/(([^/]|//)*)/(i)?))\\s*\\]|([^,\\[\\]]*))\\s*(,(.*))?", Pattern.CASE_INSENSITIVE);
+
+	/**
+	 * Parse the given list of parameters + their limits/controller and update the list of parameters for the specified job list.
+	 * 
+	 * <p>
+	 * 	The given string is parsed using {@link #PATTERN_PARAMETER}. The expected syntax of one item of this list is explained
+	 * 	in the javadoc of {@link #PATTERN_PARAMETER}.
+	 * </p>
+	 * 
+	 * <p>
+	 * 	If just a parameter name is given, it is anyway added to the map of parameters of the specified job list, but with a <code>null</code>
+	 * 	{@link InputParamController}. The idea is to indicate the parameter is expected with this name, but there is no specific restriction on its value.
+	 * </p>
+	 * 
+	 * @param jlName		Name of the job list for which the given list of parameters is expected.
+	 * @param propValue		String expressing the list of expected parameters (and eventually their limits/controller).
+	 * 
+	 * @throws UWSException	If the syntax of a parameter description is incorrect.
+	 */
+	protected void initParameters(final String jlName, final String propValue) throws UWSException{
+		// Get the existing list of parameters, if any;
+		/* Note: it is possible if the same property key is found more than once in a property file.
+		 *       In this case, the second list of parameters should update the first one. */
+		Map<String,InputParamController> jlParameters = jobParams.get(jlName);
+		
+		// If no list of controllers exists, create an empty one:
+		if (jlParameters == null)
+			jlParameters = new HashMap<String,InputParamController>();
+
+		int indParameter = 1;
+
+		Matcher matcher;
+		String remaining = propValue;
+		String paramName, temp;
+		boolean modif = true;
+		while(remaining != null && remaining.trim().length() > 0){
+			// Apply the regular expression:
+			matcher = PATTERN_PARAMETER.matcher(remaining);
+			if (!matcher.matches())
+				throw new UWSException("Incorrect parameter list syntax from the parameter N°" + indParameter + ": " + remaining);
+
+			// CASE: no controller specified
+			if (matcher.group(16) != null){
+
+				// Fetch the parameter name:
+				paramName = matcher.group(16).trim();
+
+				// Check the name:
+				/* a parameter name must NOT be empty */
+				if (paramName.length() > 0){
+					/* a parameter name must NOT contain any space character */
+					if (!paramName.replaceFirst("\\s", "_").equals(paramName))
+						throw new UWSException("Incorrect syntax for the parameter name \"" + paramName + "\"! Space characters are forbidden.");
+					else
+						jlParameters.put(paramName, null);
+				}
+			}else{
+
+				// Fetch the parameter name:
+				paramName = matcher.group(2).trim();
+
+				// Check the name:
+				/* a parameter name must NOT be empty */
+				if (paramName.length() == 0)
+					throw new UWSException("Missing name for the parameter N°" + indParameter + "!");
+				/* a parameter name must NOT contain any space character */
+				else if (!paramName.replaceFirst("\\s", "_").equals(paramName))
+					throw new UWSException("Incorrect syntax for the parameter name \"" + paramName + "\"! Space characters are forbidden.");
+				
+				// CASE: CUSTOM CONTROLLER
+				if (matcher.group(4) != null){
+					jlParameters.put(paramName, newInstance(matcher.group(4), jlName+"."+UWSConfiguration.KEY_PARAMETERS, InputParamController.class, new Class<?>[0], new Object[0]));
+				}
+				// CASE: STRING/NUMERIC/DURATION
+				else{
+					// Fetch the modification flag:
+					temp = matcher.group(5).trim();
+					if (temp.length() == 0 || temp.matches("(?i)(true|t|yes|y)"))
+						modif = true;
+					else
+						modif = false;
+	
+					// CASE: STRING
+					if (matcher.group(11) != null){
+	
+						// Create the controller:
+						StringParamController controller = new StringParamController(paramName);
+	
+						// Set its modification flag:
+						controller.allowModification(modif);
+	
+						// Set its default value, if any:
+						if (matcher.group(12).length() > 0)
+							controller.setDefaultValue(matcher.group(12));
+	
+						// Set the regular expression:
+						if (matcher.group(13).length() > 0)
+							controller.setRegExp((matcher.group(15) != null ? "(?i)" : "") + matcher.group(13));
+	
+						// Add the controller:
+						jlParameters.put(paramName, controller);
+	
+					}
+	
+					// CASE: NUMERIC/DURATION
+					else if (matcher.group(7) != null){
+	
+						// CASE: NUMERIC
+						if (matcher.group(7).trim().equalsIgnoreCase("numeric")){
+	
+							// Create the controller:
+							NumericParamController controller = new NumericParamController();
+	
+							// Set its modification flag:
+							controller.allowModification(modif);
+	
+							// Set the default value:
+							if (matcher.group(8).trim().length() > 0){
+								try{
+									controller.setDefault(Double.parseDouble(matcher.group(8).trim()));
+								}catch(NumberFormatException nfe){
+									throw new UWSException("Wrong numeric format for the default value of the parameter \"" + paramName + "\": \"" + matcher.group(8).trim() + "\"!");
+								}
+							}
+	
+							// Set the minimum value:
+							if (matcher.group(9).trim().length() > 0){
+								try{
+									controller.setMinimum(Double.parseDouble(matcher.group(9).trim()));
+								}catch(NumberFormatException nfe){
+									throw new UWSException("Wrong numeric format for the minimum value of the parameter \"" + paramName + "\": \"" + matcher.group(9).trim() + "\"!");
+								}
+							}
+	
+							// Set the maximum value:
+							if (matcher.group(10).trim().length() > 0){
+								try{
+									controller.setMaximum(Double.parseDouble(matcher.group(10).trim()));
+								}catch(NumberFormatException nfe){
+									throw new UWSException("Wrong numeric format for the maximum value of the parameter \"" + paramName + "\": \"" + matcher.group(10).trim() + "\"!");
+								}
+							}
+	
+							// Add the controller:
+							jlParameters.put(paramName, controller);
+	
+						}
+						// CASE: DURATION
+						else{
+	
+							// Create the controller:
+							DurationParamController controller = new DurationParamController();
+	
+							// Set its modification flag:
+							controller.allowModification(modif);
+	
+							// Set the default value:
+							if (matcher.group(8).trim().length() > 0){
+								try{
+									controller.setDefault(controller.parseDuration(matcher.group(8).trim()));
+								}catch(NumberFormatException nfe){
+									throw new UWSException("Wrong numeric format for the default value of the parameter \"" + paramName + "\": \"" + matcher.group(8).trim() + "\"!");
+								}catch(ParseException pe){
+									throw new UWSException("Incorrect syntax for the default duration of the parameter \"" + paramName + "\"! Cause: " + pe.getMessage());
+								}
+							}
+	
+							// Set the minimum value:
+							if (matcher.group(9).trim().length() > 0){
+								try{
+									controller.setMinimum(controller.parseDuration(matcher.group(9).trim()));
+								}catch(NumberFormatException nfe){
+									throw new UWSException("Wrong numeric format for the minimum value of the parameter \"" + paramName + "\": \"" + matcher.group(9).trim() + "\"!");
+								}catch(ParseException pe){
+									throw new UWSException("Incorrect syntax for the minimu duration of the parameter \"" + paramName + "\"! Cause: " + pe.getMessage());
+								}
+							}
+	
+							// Set the maximum value:
+							if (matcher.group(10).trim().length() > 0){
+								try{
+									controller.setMaximum(controller.parseDuration(matcher.group(10).trim()));
+								}catch(NumberFormatException nfe){
+									throw new UWSException("Wrong numeric format for the maximum value of the parameter \"" + paramName + "\": \"" + matcher.group(10).trim() + "\"!");
+								}catch(ParseException pe){
+									throw new UWSException("Incorrect syntax for the maximum duration of the parameter \"" + paramName + "\"! Cause: " + pe.getMessage());
+								}
+							}
+	
+							// Add the controller:
+							jlParameters.put(paramName, controller);
+						}
+					}
+				}
+			}
+
+			// Read the other parameters:
+			remaining = matcher.group(18);
+			indParameter++;
+			modif = true;
+		}
+
+		// Add this new list of parameters:
+		if (jlParameters.size() > 0)
+			jobParams.put(jlName, jlParameters);
+	}
+
+	@Override
+	public JobThread createJobThread(final UWSJob jobDescription) throws UWSException{
+		// No job => No thread ^^
+		if (jobDescription == null)
+			return null;
+		// If the job is not inside a job list, it is impossible to know which instance of JobThread to create => Error!
+		else if (jobDescription.getJobList() == null)
+			throw new UWSException("Job without job list! Impossible to create a thread for the job \"" + jobDescription.getJobId() + "\".");
+
+		// Extract the job list name:
+		String jlName = jobDescription.getJobList().getName();
+
+		// If no class is associated with this job list name, no JobThread can be created => Error!
+		if (jlName == null || jobThreads.get(jlName) == null)
+			throw new UWSException("No thread associated with jobs of the job list \"" + jlName + "\"! Impossible to create a thread for the job \"" + jobDescription.getJobId() + "\".");
+
+		// Get the constructor:
+		Constructor<? extends JobThread> threadConstructor = jobThreads.get(jlName);
+		try{
+
+			// Create the JobThread:
+			return threadConstructor.newInstance(jobDescription);
+
+		}catch(InstantiationException ie){
+			throw new UWSException("Impossible to create an instance of an abstract class: \"" + threadConstructor.getDeclaringClass().getName() + "\"!");
+
+		}catch(InvocationTargetException ite){
+			if (ite.getCause() != null){
+				if (ite.getCause() instanceof UWSException)
+					throw (UWSException)ite.getCause();
+				else
+					throw new UWSException(ite.getCause());
+			}else
+				throw new UWSException(ite);
+
+		}catch(Exception ex){
+			throw new UWSException(UWSException.NOT_FOUND, ex, "Impossible to create the thread " + threadConstructor.getDeclaringClass().getName() + " for the job \"" + jobDescription.getJobId() + "\"!");
+		}
+	}
+
+	@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):
+		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);
+	}
+
+	@Override
+	public UWSJob createJob(String jobID, JobOwner owner, UWSParameters params, long quote, long startTime, long endTime, List<Result> results, ErrorSummary error) throws UWSException{
+		return new UWSJob(jobID, owner, params, quote, startTime, endTime, results, error);
+	}
+
+	@Override
+	public UWSParameters createUWSParameters(final Map<String,Object> params) throws UWSException{
+		return new UWSParameters(params);
+	}
+
+	@Override
+	public UWSParameters createUWSParameters(final HttpServletRequest req) throws UWSException{
+		UWSUrl url = new UWSUrl(req);
+		if (url.getJobListName() != null && jobParams.get(url.getJobListName()) != null){
+			return new UWSParameters(req, jobParams.get(url.getJobListName()).keySet(), jobParams.get(url.getJobListName()));
+		}else
+			return new UWSParameters(req);
+	}
+
+	@Override
+	public RequestParser createRequestParser(final UWSFileManager fileManager) throws UWSException{
+		if (constructorRequestParser == null)
+			return new UWSRequestParser(fileManager);
+		else{
+			try{
+				return constructorRequestParser.newInstance(fileManager);
+			}catch(InstantiationException ie){
+				throw new UWSException("Impossible to create an instance of an abstract class: \"" + constructorRequestParser.getDeclaringClass().getName() + "\"!");
+
+			}catch(InvocationTargetException ite){
+				if (ite.getCause() != null){
+					if (ite.getCause() instanceof UWSException)
+						throw (UWSException)ite.getCause();
+					else
+						throw new UWSException(ite.getCause());
+				}else
+					throw new UWSException(ite);
+
+			}catch(Exception ex){
+				throw new UWSException(UWSException.NOT_FOUND, ex, "Impossible to create the request parser " + constructorRequestParser.getDeclaringClass().getName() + "\"!");
+			}
+		}
+	}
+
+}
diff --git a/src/uws/config/ConfigurableUWSServlet.java b/src/uws/config/ConfigurableUWSServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..b31761218a54a53c81c6e15ee754bbed438efa6a
--- /dev/null
+++ b/src/uws/config/ConfigurableUWSServlet.java
@@ -0,0 +1,693 @@
+package uws.config;
+
+/*
+ * 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 2016 - Astronomisches Rechen Institut (ARI)
+ */
+
+import static uws.config.UWSConfiguration.DEFAULT_BACKUP_BY_USER;
+import static uws.config.UWSConfiguration.DEFAULT_BACKUP_FREQUENCY;
+import static uws.config.UWSConfiguration.DEFAULT_DIRECTORY_PER_USER;
+import static uws.config.UWSConfiguration.DEFAULT_GROUP_USER_DIRECTORIES;
+import static uws.config.UWSConfiguration.DEFAULT_UWS_CONF_FILE;
+import static uws.config.UWSConfiguration.KEY_ADD_SERIALIZERS;
+import static uws.config.UWSConfiguration.KEY_ADD_UWS_ACTIONS;
+import static uws.config.UWSConfiguration.KEY_BACKUP_BY_USER;
+import static uws.config.UWSConfiguration.KEY_BACKUP_FREQUENCY;
+import static uws.config.UWSConfiguration.KEY_DESTRUCTION_MANAGER;
+import static uws.config.UWSConfiguration.KEY_DIRECTORY_PER_USER;
+import static uws.config.UWSConfiguration.KEY_ERROR_WRITER;
+import static uws.config.UWSConfiguration.KEY_EXECUTION_MANAGER;
+import static uws.config.UWSConfiguration.KEY_FILE_MANAGER;
+import static uws.config.UWSConfiguration.KEY_FILE_ROOT_PATH;
+import static uws.config.UWSConfiguration.KEY_GROUP_USER_DIRECTORIES;
+import static uws.config.UWSConfiguration.KEY_HOME_PAGE;
+import static uws.config.UWSConfiguration.KEY_HOME_PAGE_MIME_TYPE;
+import static uws.config.UWSConfiguration.KEY_JOB_LISTS;
+import static uws.config.UWSConfiguration.KEY_LOG_ROTATION;
+import static uws.config.UWSConfiguration.KEY_MAX_RUNNING_JOBS;
+import static uws.config.UWSConfiguration.KEY_MIN_LOG_LEVEL;
+import static uws.config.UWSConfiguration.KEY_SERVICE_DESCRIPTION;
+import static uws.config.UWSConfiguration.KEY_SERVICE_NAME;
+import static uws.config.UWSConfiguration.KEY_USER_IDENTIFIER;
+import static uws.config.UWSConfiguration.KEY_UWS_FACTORY;
+import static uws.config.UWSConfiguration.KEY_XSLT_STYLESHEET;
+import static uws.config.UWSConfiguration.REGEXP_JOB_LIST_NAME;
+import static uws.config.UWSConfiguration.UWS_CONF_PARAMETER;
+import static uws.config.UWSConfiguration.VALUE_LOCAL;
+import static uws.config.UWSConfiguration.VALUE_NEVER;
+import static uws.config.UWSConfiguration.VALUE_USER_ACTION;
+import static uws.config.UWSConfiguration.getProperty;
+import static uws.config.UWSConfiguration.hasConstructor;
+import static uws.config.UWSConfiguration.isClassName;
+import static uws.config.UWSConfiguration.newInstance;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Properties;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import uws.UWSException;
+import uws.job.JobList;
+import uws.job.manager.DefaultDestructionManager;
+import uws.job.manager.DefaultExecutionManager;
+import uws.job.manager.DestructionManager;
+import uws.job.manager.ExecutionManager;
+import uws.job.manager.QueuedExecutionManager;
+import uws.job.serializer.UWSSerializer;
+import uws.job.serializer.XMLSerializer;
+import uws.service.UWSFactory;
+import uws.service.UWSService;
+import uws.service.UserIdentifier;
+import uws.service.actions.ShowHomePage;
+import uws.service.actions.UWSAction;
+import uws.service.backup.DefaultUWSBackupManager;
+import uws.service.backup.UWSBackupManager;
+import uws.service.error.ServiceErrorWriter;
+import uws.service.file.LocalUWSFileManager;
+import uws.service.file.UWSFileManager;
+import uws.service.log.DefaultUWSLog;
+import uws.service.log.UWSLog;
+import uws.service.log.UWSLog.LogLevel;
+
+/**
+ * <p>HTTP servlet fully configured with a UWS configuration file.</p>
+ * 
+ * <p>
+ * 	This configuration file may be specified in the initial parameter named {@link UWSConfiguration#UWS_CONF_PARAMETER}
+ * 	of this servlet inside the WEB-INF/web.xml file. If none is specified, the file {@link UWSConfiguration#DEFAULT_UWS_CONF_FILE}
+ * 	will be searched inside the directories of the classpath, and inside WEB-INF and META-INF.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.2 (06/2016)
+ * @since 4.2
+ */
+public class ConfigurableUWSServlet extends HttpServlet {
+	private static final long serialVersionUID = 1L;
+
+	/** UWS object representing the UWS service. */
+	private UWSService uws = null;
+
+	@Override
+	public void init(final ServletConfig config) throws ServletException{
+		// Nothing to do, if UWS is already initialized:
+		if (uws != null)
+			return;
+
+		/* 1. GET THE FILE PATH OF THE UWS CONFIGURATION FILE */
+		String uwsConfPath = config.getInitParameter(UWS_CONF_PARAMETER);
+		if (uwsConfPath == null || uwsConfPath.trim().length() == 0)
+			uwsConfPath = null;
+		//throw new ServletException("Configuration file path missing! You must set a servlet init parameter whose the name is \"" + UWS_CONF_PARAMETER + "\".");
+
+		/* 2. OPEN THE CONFIGURATION FILE */
+		InputStream input = null;
+		// CASE: No file specified => search in the classpath for a file having the default name "uws.properties".
+		if (uwsConfPath == null)
+			input = searchFile(DEFAULT_UWS_CONF_FILE, config);
+		else{
+			File f = new File(uwsConfPath);
+			// CASE: The given path matches to an existing local file.
+			if (f.exists()){
+				try{
+					input = new FileInputStream(f);
+				}catch(IOException ioe){
+					throw new ServletException("Impossible to read the UWS configuration file (" + uwsConfPath + ")!", ioe);
+				}
+			}
+			// CASE: The given path seems to be relative to the servlet root directory.
+			else
+				input = searchFile(uwsConfPath, config);
+		}
+		// If no file has been found, cancel the servlet loading:
+		if (input == null)
+			throw new ServletException("Configuration file not found with the path: \"" + ((uwsConfPath == null) ? DEFAULT_UWS_CONF_FILE : uwsConfPath) + "\"! Please provide a correct file path in servlet init parameter (\"" + UWS_CONF_PARAMETER + "\") or put your configuration file named \"" + DEFAULT_UWS_CONF_FILE + "\" in a directory of the classpath or in WEB-INF or META-INF.");
+
+		/* 3. PARSE IT INTO A PROPERTIES SET */
+		Properties uwsConf = new Properties();
+		try{
+			uwsConf.load(input);
+		}catch(IOException ioe){
+			throw new ServletException("Impossible to read the UWS configuration file (" + uwsConfPath + ")!", ioe);
+		}finally{
+			try{
+				input.close();
+			}catch(IOException ioe2){}
+		}
+
+		/* 4. CREATE THE FILE MANAGER */
+		UWSFileManager fileManager = null;
+		try{
+			fileManager = createFileManager(uwsConf, config.getServletContext().getRealPath(""));
+		}catch(Exception ex){
+			if (ex instanceof UWSException)
+				throw new ServletException(ex.getMessage(), ex.getCause());
+			else
+				throw new ServletException("Impossible to initialize the UWS file manager!", ex);
+		}
+
+		/* 5. CREATE THE LOGGER */
+		UWSLog logger = null;
+		try{
+			logger = createLogger(uwsConf, fileManager);
+		}catch(Exception ex){
+			if (ex instanceof UWSException)
+				throw new ServletException(ex.getMessage(), ex.getCause());
+			else
+				throw new ServletException("Impossible to initialize the UWS logger!", ex);
+		}
+
+		/* 6. CREATE THE UWS SERVICE */
+		try{
+			uws = new UWSService(createFactory(uwsConf), fileManager, logger);
+		}catch(Exception ex){
+			uws = null;
+			if (ex instanceof UWSException)
+				throw new ServletException(ex.getMessage(), ex.getCause());
+			else
+				throw new ServletException("Impossible to initialize the UWS service!", ex);
+		}
+
+		/* 6Bis. SET THE HOME PAGE */
+		String propValue = getProperty(uwsConf, KEY_HOME_PAGE);
+		if (propValue != null){
+			// If it is a class path, replace the current home page by an instance of this class:
+			if (isClassName(propValue)){
+				try{
+					uws.replaceUWSAction(newInstance(propValue, KEY_HOME_PAGE, ShowHomePage.class, new Class<?>[]{UWSService.class}, new Object[]{uws}));
+				}catch(UWSException te){
+					throw new ServletException(te.getMessage(), te.getCause());
+				}
+			}
+			// If it is a file URI (null, file inside WebContent, file://..., http://..., etc...):
+			else{
+				// ...set the given URI:
+				uws.setHomePage(propValue);
+				// ...and its MIME type (if any):
+				propValue = getProperty(uwsConf, KEY_HOME_PAGE_MIME_TYPE);
+				if (propValue != null)
+					uws.setHomePageMimeType(propValue);
+			}
+		}
+
+		/* 6Ter. SET ALL GENERAL SERVICE CONNECTION INFORMATION */
+		uws.setName(getProperty(uwsConf, KEY_SERVICE_NAME));
+		uws.setDescription(getProperty(uwsConf, KEY_SERVICE_DESCRIPTION));
+
+		/* 7. CONFIGURE THE BACKUP MANAGER */
+		initBackup(uwsConf);
+
+		/* 8. CONFIGURE THE USER IDENTIFICATION */
+		initUserIdentifier(uwsConf);
+
+		/* 9. CREATE THE JOB LISTS */
+		initJobLists(uwsConf);
+
+		/* 10. INITIALIZE ADDITIONAL ACTIONS */
+		addCustomActions(uwsConf);
+
+		/* 11. INITIALIZE THE SERIALIZATION */
+		addCustomSerializers(uwsConf);
+		initXSLTStylesheet(uwsConf);
+		initErrorWriter(uwsConf);
+
+		/* 12. DEFAULT SERVLET INITIALIZATION */
+		super.init(config);
+	}
+
+	/**
+	 * Initialize the management of UWS service files using the given UWS configuration file.
+	 * 
+	 * @param uwsConfig		The content of the UWS configuration file.
+	 * @param webAppRootDir	The directory of the Web Application running this UWS service.
+	 *                     	<em>This directory may be used only to search the root UWS directory
+	 *                     	if specified with a relative path in the UWS configuration file.</em>
+	 * 
+	 * @return	The created file manager.
+	 * 
+	 * @throws UWSException	If a property is wrong or missing, or if an error occurs while creating the file manager.
+	 */
+	private UWSFileManager createFileManager(final Properties uwsConfig, final String webAppRootDir) throws UWSException{
+		// Read the desired file manager:
+		String fileManagerType = getProperty(uwsConfig, KEY_FILE_MANAGER);
+		if (fileManagerType == null)
+			throw new UWSException("The property \"" + KEY_FILE_MANAGER + "\" is missing! It is required to create a TAP Service. Two possible values: " + VALUE_LOCAL + " or a class name between {...}.");
+		else
+			fileManagerType = fileManagerType.trim();
+
+		// LOCAL file manager:
+		if (fileManagerType.equalsIgnoreCase(VALUE_LOCAL)){
+			// Read the desired root path:
+			String rootPath = getProperty(uwsConfig, KEY_FILE_ROOT_PATH);
+			if (rootPath == null)
+				throw new UWSException("The property \"" + KEY_FILE_ROOT_PATH + "\" is missing! It is required to create a TAP Service. Please provide a path toward a directory which will contain all files related to the service.");
+			File rootFile = getFile(rootPath, webAppRootDir, KEY_FILE_ROOT_PATH);
+
+			// Determine whether there should be one directory for each user:
+			String propValue = getProperty(uwsConfig, KEY_DIRECTORY_PER_USER);
+			boolean oneDirectoryPerUser = (propValue == null) ? DEFAULT_DIRECTORY_PER_USER : Boolean.parseBoolean(propValue);
+
+			// Determine whether there should be one directory for each user:
+			propValue = getProperty(uwsConfig, KEY_GROUP_USER_DIRECTORIES);
+			boolean groupUserDirectories = (propValue == null) ? DEFAULT_GROUP_USER_DIRECTORIES : Boolean.parseBoolean(propValue);
+
+			// Build the Local TAP File Manager:
+			try{
+				return new LocalUWSFileManager(rootFile, oneDirectoryPerUser, groupUserDirectories);
+			}catch(UWSException e){
+				throw new UWSException("The property \"" + KEY_FILE_ROOT_PATH + "\" (" + rootPath + ") is incorrect: " + e.getMessage());
+			}
+		}
+		// CUSTOM file manager:
+		else
+			return newInstance(fileManagerType, KEY_FILE_MANAGER, UWSFileManager.class, new Class<?>[]{Properties.class}, new Object[]{uwsConfig});
+	}
+
+	/**
+	 * Initialize the UWS logger with the given UWS configuration file.
+	 * 
+	 * @param uwsConfig		The content of the UWS configuration file.
+	 * @param fileManager	The file manager to access the log file(s).
+	 */
+	private UWSLog createLogger(final Properties uwsConfig, final UWSFileManager fileManager){
+		// Create the logger:
+		UWSLog logger = new DefaultUWSLog(fileManager);
+
+		StringBuffer buf = new StringBuffer("Logger initialized");
+
+		// Set the minimum log level:
+		String propValue = getProperty(uwsConfig, KEY_MIN_LOG_LEVEL);
+		if (propValue != null){
+			try{
+				((DefaultUWSLog)logger).setMinLogLevel(LogLevel.valueOf(propValue.toUpperCase()));
+			}catch(IllegalArgumentException iae){}
+		}
+		buf.append(" (minimum log level: ").append(((DefaultUWSLog)logger).getMinLogLevel());
+
+		// Set the log rotation period, if any:
+		if (fileManager instanceof LocalUWSFileManager){
+			propValue = getProperty(uwsConfig, KEY_LOG_ROTATION);
+			if (propValue != null)
+				((LocalUWSFileManager)fileManager).setLogRotationFreq(propValue);
+			buf.append(", log rotation: ").append(((LocalUWSFileManager)fileManager).getLogRotationFreq());
+		}
+
+		// Log the successful initialization with set parameters:
+		buf.append(").");
+		logger.info(buf.toString());
+
+		return logger;
+	}
+
+	/**
+	 * <p>Initialize the {@link UWSFactory} to use.</p>
+	 * 
+	 * <p>
+	 * 	The built factory is either a {@link ConfigurableUWSFactory} instance (by default) or
+	 * 	an instance of the class specified in the UWS configuration file.
+	 * </p>
+	 * 
+	 * @param uwsConfig		The content of the UWS configuration file.
+	 * 
+	 * @throws UWSException	If an error occurs while building the specified {@link UWSFactory}.
+	 * 
+	 * @see ConfigurableUWSFactory
+	 */
+	private UWSFactory createFactory(final Properties uwsConfig) throws UWSException{
+		String propValue = getProperty(uwsConfig, KEY_UWS_FACTORY);
+		if (propValue == null)
+			return new ConfigurableUWSFactory(uwsConfig);
+		else if (hasConstructor(propValue, KEY_UWS_FACTORY, UWSFactory.class, new Class<?>[]{Properties.class}))
+			return newInstance(propValue, KEY_UWS_FACTORY, UWSFactory.class, new Class<?>[]{Properties.class}, new Object[]{uwsConfig});
+		else
+			return newInstance(propValue, KEY_UWS_FACTORY, UWSFactory.class, new Class<?>[]{}, new Object[]{});
+	}
+
+	/**
+	 * Create a {@link UWSBackupManager} if needed, thanks to the configuration provided in the UWS configuration file.
+	 * 
+	 * @param uwsConf	List of properties set in the UWS configuration file.
+	 * 
+	 * @throws ServletException	If any error occurs when initializing the {@link UWSBackupManager}.
+	 */
+	private void initBackup(final Properties uwsConf) throws ServletException{
+		try{
+			/* Set the backup frequency: */
+			String propValue = getProperty(uwsConf, KEY_BACKUP_FREQUENCY);
+
+			// determine whether the value is a time period ; if yes, set the frequency:
+			long backupFrequency;
+			boolean backupByUser;
+			if (propValue != null){
+				try{
+					backupFrequency = Long.parseLong(propValue);
+					if (backupFrequency <= 0)
+						backupFrequency = DEFAULT_BACKUP_FREQUENCY;
+				}catch(NumberFormatException nfe){
+					// if the value was not a valid numeric time period, try to identify the different textual options:
+					if (propValue.equalsIgnoreCase(VALUE_NEVER))
+						backupFrequency = DefaultUWSBackupManager.MANUAL;
+					else if (propValue.equalsIgnoreCase(VALUE_USER_ACTION))
+						backupFrequency = DefaultUWSBackupManager.AT_USER_ACTION;
+					else
+						throw new UWSException("Long expected for the property \"" + KEY_BACKUP_FREQUENCY + "\", instead of: \"" + propValue + "\"!");
+				}
+			}else
+				backupFrequency = DEFAULT_BACKUP_FREQUENCY;
+
+			// Specify whether the backup must be organized by user or not:
+			propValue = getProperty(uwsConf, KEY_BACKUP_BY_USER);
+			backupByUser = (propValue == null) ? DEFAULT_BACKUP_BY_USER : Boolean.parseBoolean(propValue);
+
+			// Finally create and set the backup manager:
+			uws.setBackupManager(new DefaultUWSBackupManager(uws, backupByUser, backupFrequency));
+
+		}catch(UWSException ue){
+			throw new ServletException("Impossible to initialize the Backup system (and so to restore all the last backuped jobs)!", ue);
+		}
+	}
+
+	/**
+	 * Initialize the UWS user identification method.
+	 * 
+	 * @param uwsConfig	The content of the UWS configuration file.
+	 * 
+	 * @throws ServletException	If the corresponding UWS configuration property is wrong.
+	 */
+	private void initUserIdentifier(final Properties uwsConfig) throws ServletException{
+		// Get the property value:
+		String propValue = getProperty(uwsConfig, KEY_USER_IDENTIFIER);
+		if (propValue != null){
+			try{
+				uws.setUserIdentifier(newInstance(propValue, KEY_USER_IDENTIFIER, UserIdentifier.class));
+			}catch(UWSException ue){
+				throw new ServletException("Impossible to initialize the user identification!", ue);
+			}
+		}
+	}
+
+	/**
+	 * Initialize all the specified job lists.
+	 * 
+	 * @param uwsConfig	The content of the UWS configuration file.
+	 * 
+	 * @throws ServletException	If the corresponding UWS configuration property is wrong.
+	 */
+	private void initJobLists(final Properties uwsConf) throws ServletException{
+		String propValue = getProperty(uwsConf, KEY_JOB_LISTS);
+		if (propValue != null){
+			// split the list of job list names:
+			String[] jlNames = propValue.split(",");
+			if (jlNames == null || jlNames.length == 0)
+				throw new ServletException("Missing job list name! At least one job list name must be provided. See property \"" + KEY_JOB_LISTS + "\".");
+
+			// for each job list name:
+			int nbMaxRunningJobs;
+			ExecutionManager execManager;
+			DestructionManager destManager;
+			for(String jlName : jlNames){
+				// normalize and test the name:
+				jlName = jlName.trim();
+				if (!jlName.matches(REGEXP_JOB_LIST_NAME))
+					throw new ServletException("Incorrect job list name: \"" + jlName + "\"! It must not contain space characters, point and equal sign.");
+
+				// configure the execution manager, if any is specified in the configuration:
+				nbMaxRunningJobs = -1;
+				execManager = null;
+				try{
+					// if an execution manager is provided, set it:
+					propValue = getProperty(uwsConf, jlName + "." + KEY_EXECUTION_MANAGER);
+					if (propValue != null)
+						execManager = newInstance(propValue, jlName + "." + KEY_EXECUTION_MANAGER, ExecutionManager.class, new Class<?>[]{UWSLog.class}, new Object[]{uws.getLogger()});
+
+					/* if none is provided, the default execution manager will be used
+					 * EXCEPT if a maximum number of running jobs is specified ; in such case a QueuedExecutionManager will be used. */
+					else{
+						propValue = getProperty(uwsConf, jlName + "." + KEY_MAX_RUNNING_JOBS);
+						if (propValue != null){
+							try{
+								nbMaxRunningJobs = Integer.parseInt(propValue);
+								if (nbMaxRunningJobs > 0)
+									execManager = new QueuedExecutionManager(uws.getLogger(), nbMaxRunningJobs);
+							}catch(NumberFormatException nfe){
+								uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Incorrect value for the property \"" + jlName + "." + KEY_MAX_RUNNING_JOBS + "\": \"" + propValue + "\"! It should be a positive integer value. No execution queue is set for this job list.", nfe);
+							}
+						}
+					}
+				}catch(UWSException ue){
+					uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Impossible to set a custom execution manager to the job list \"" + jlName + "\"! The default one will be used.", ue);
+				}
+
+				// configure the destruction manager, if any is specified in the configuration:
+				destManager = null;
+				try{
+					propValue = getProperty(uwsConf, jlName + "." + KEY_DESTRUCTION_MANAGER);
+					if (propValue != null)
+						destManager = newInstance(propValue, jlName + "." + KEY_DESTRUCTION_MANAGER, DestructionManager.class, new Class<?>[]{}, new Object[]{});
+				}catch(UWSException ue){
+					uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Impossible to set a custom destruction manager to the job list \"" + jlName + "\"! The default one will be used.", ue);
+				}
+
+				// add the job list to this UWS service:
+				if (execManager == null)
+					execManager = new DefaultExecutionManager(uws.getLogger());
+				if (destManager == null)
+					destManager = new DefaultDestructionManager();
+				uws.addJobList(new JobList(jlName, execManager, destManager));
+			}
+		}else
+			throw new ServletException("Missing job list name! At least one job list name must be provided. See property \"" + KEY_JOB_LISTS + "\".");
+	}
+
+	/**
+	 * Add all additional custom actions listed in the configuration file.
+	 * 
+	 * @param uwsConfig	The content of the UWS configuration file.
+	 * 
+	 * @throws ServletException	If the corresponding UWS configuration property is wrong.
+	 */
+	private void addCustomActions(final Properties uwsConfig) throws ServletException{
+		// Get the property value:
+		String propValue = getProperty(uwsConfig, KEY_ADD_UWS_ACTIONS);
+		if (propValue != null){
+			// Tokenise the list of classes:
+			String[] actionClasses = propValue.split(",");
+			if (actionClasses == null || actionClasses.length == 0)
+				return;
+
+			// For each item:
+			for(String actionClass : actionClasses){
+				try{
+					// extract the action index, if any is provided:
+					int actionIndex = actionClass.indexOf(':');
+					if (actionIndex > 0){
+						actionIndex = Integer.parseInt(actionClass.substring(0, actionIndex));
+						actionClass = actionClass.substring(actionIndex + 1);
+					}
+					// create an instance of the specified action:
+					UWSAction action = newInstance(actionClass, KEY_ADD_UWS_ACTIONS, UWSAction.class, new Class<?>[]{UWSService.class}, new Object[]{uws});
+					// add or replacing depending if an action with the same name already exists or not:
+					boolean added = false;
+					if (uws.getUWSAction(action.getName()) != null)
+						added = (uws.replaceUWSAction(action) != null);
+					else if (actionIndex >= 0)
+						added = uws.addUWSAction(actionIndex, action);
+					else
+						added = uws.addUWSAction(action);
+					// log an error if the addition is not successful:
+					if (!added)
+						uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Failed to add the UWS action \"" + action.getName() + "\" implemented with the class \"" + actionClass + "\"! See property \"" + KEY_ADD_UWS_ACTIONS + "\".", null);
+				}catch(UWSException ue){
+					uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Impossible to create the UWS action \"" + actionClass + "\" specified in the property \"" + KEY_ADD_UWS_ACTIONS + "\"!", ue);
+				}catch(NumberFormatException nfe){
+					uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Impossible to extract the given action index for the UWS action \"" + actionClass + "\" specified in the property \"" + KEY_ADD_UWS_ACTIONS + "\"! A positive integer value is expected.", nfe);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Add all additional UWS serialized listed in the configuration file.
+	 * 
+	 * @param uwsConfig	The content of the UWS configuration file.
+	 * 
+	 * @throws ServletException	If the corresponding UWS configuration property is wrong.
+	 */
+	private void addCustomSerializers(final Properties uwsConfig) throws ServletException{
+		// Get the property value:
+		String propValue = getProperty(uwsConfig, KEY_ADD_SERIALIZERS);
+		if (propValue != null){
+			// Tokenise the list of classes:
+			String[] serializerClasses = propValue.split(",");
+			if (serializerClasses == null || serializerClasses.length == 0)
+				return;
+
+			// For each item:
+			for(String serializerClass : serializerClasses){
+				try{
+					// create and add the specified serializer to this UWS service:
+					uws.addSerializer(newInstance(serializerClass, KEY_ADD_SERIALIZERS, UWSSerializer.class, new Class<?>[]{}, new Object[]{}));
+				}catch(UWSException ue){
+					uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Impossible to create the UWS serializer \"" + serializerClass + "\" specified in the property \"" + KEY_ADD_SERIALIZERS + "\"!", ue);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Initialize the error writer of the UWS service.
+	 * 
+	 * @param uwsConfig	The content of the UWS configuration file.
+	 * 
+	 * @throws ServletException	If the corresponding UWS configuration property is wrong.
+	 */
+	private void initXSLTStylesheet(final Properties uwsConfig) throws ServletException{
+		// Get the property value:
+		String propValue = getProperty(uwsConfig, KEY_XSLT_STYLESHEET);
+		if (propValue != null){
+			try{
+				if (uws.getSerializer(XMLSerializer.MIME_TYPE_XML) instanceof XMLSerializer)
+					((XMLSerializer)uws.getSerializer(XMLSerializer.MIME_TYPE_XML)).setXSLTPath(propValue);
+			}catch(UWSException ue){
+				uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Impossible to set the specified XSLT stylesheet: \"" + propValue + "\"! Then, no XSLT stylesheet is used.", ue);
+			}
+		}
+	}
+
+	/**
+	 * Initialize the error writer of the UWS service.
+	 * 
+	 * @param uwsConfig	The content of the UWS configuration file.
+	 * 
+	 * @throws ServletException	If the corresponding UWS configuration property is wrong.
+	 */
+	private void initErrorWriter(final Properties uwsConfig) throws ServletException{
+		// Get the property value:
+		String propValue = getProperty(uwsConfig, KEY_ERROR_WRITER);
+		if (propValue != null){
+			try{
+				uws.setErrorWriter(newInstance(propValue, KEY_ERROR_WRITER, ServiceErrorWriter.class));
+			}catch(UWSException ue){
+				uws.getLogger().logUWS(LogLevel.ERROR, uws, "INIT", "Impossible to initialize the error writer! Then, the default one will be used.", ue);
+			}
+		}
+	}
+
+	/**
+	 * Search the given file name/path in the directories of the classpath, then inside WEB-INF and finally inside META-INF.
+	 * 
+	 * @param filePath	A file name/path.
+	 * @param config	Servlet configuration (containing also the context class loader - link with the servlet classpath).
+	 * 
+	 * @return	The input stream toward the specified file, or NULL if no file can be found.
+	 * 
+	 * @since 2.0
+	 */
+	protected final InputStream searchFile(String filePath, final ServletConfig config){
+		InputStream input = null;
+
+		// Try to search in the classpath (with just a file name or a relative path):
+		input = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
+
+		// If not found, try searching in WEB-INF and META-INF (as this fileName is a file path relative to one of these directories):
+		if (input == null){
+			if (filePath.startsWith("/"))
+				filePath = filePath.substring(1);
+			// ...try at the root of WEB-INF:
+			input = config.getServletContext().getResourceAsStream("/WEB-INF/" + filePath);
+			// ...and at the root of META-INF:
+			if (input == null)
+				input = config.getServletContext().getResourceAsStream("/META-INF/" + filePath);
+		}
+
+		return input;
+	}
+
+	/**
+	 * <p>Resolve the given file name/path.</p>
+	 * 
+	 * <p>Only the URI protocol "file:" is allowed. If the protocol is different a {@link UWSException} is thrown.</p>
+	 * 
+	 * <p>
+	 * 	If not an absolute URI, the given path may be either relative or absolute. A relative path is always considered
+	 * 	as relative from the Web Application directory (supposed to be given in 2nd parameter).
+	 * </p>
+	 * 
+	 * @param filePath			URI/Path/Name of the file to get.
+	 * @param webAppRootPath	Web Application directory local path.
+	 * @param propertyName		Name of the property which gives the given file path.
+	 * 
+	 * @return	The specified File instance.
+	 * 
+	 * @throws UWSException	If the given URI is malformed or if the used URI scheme is different from "file:".
+	 */
+	protected final File getFile(final String filePath, final String webAppRootPath, final String propertyName) throws UWSException{
+		if (filePath == null)
+			return null;
+
+		try{
+			URI uri = new URI(filePath);
+			if (uri.isAbsolute()){
+				if (uri.getScheme().equalsIgnoreCase("file"))
+					return new File(uri);
+				else
+					throw new UWSException("Incorrect file URI for the property \"" + propertyName + "\": \"" + filePath + "\"! Only URI with the protocol \"file:\" are allowed.");
+			}else{
+				File f = new File(filePath);
+				if (f.isAbsolute())
+					return f;
+				else
+					return new File(webAppRootPath, filePath);
+			}
+		}catch(URISyntaxException use){
+			throw new UWSException(UWSException.NOT_FOUND, use, "Incorrect file URI for the property \"" + propertyName + "\": \"" + filePath + "\"! Bad syntax for the given file URI.");
+		}
+	}
+
+	@Override
+	public void destroy(){
+		// Free all resources used by UWS:
+		if (uws != null){
+			uws.destroy();
+			uws = null;
+		}
+		super.destroy();
+	}
+
+	@Override
+	protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException{
+		if (uws != null){
+			try{
+				uws.executeRequest(req, resp);
+			}catch(Throwable t){
+				resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t.getMessage());
+			}
+		}else
+			resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "UWS service not yet initialized!");
+	}
+
+}
diff --git a/src/uws/config/UWSConfiguration.java b/src/uws/config/UWSConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..41124202a681235179ef1b1fcf217f3cb4883500
--- /dev/null
+++ b/src/uws/config/UWSConfiguration.java
@@ -0,0 +1,592 @@
+package uws.config;
+
+/*
+ * 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 2016 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+
+import uws.UWSException;
+import uws.job.JobThread;
+import uws.job.manager.DestructionManager;
+import uws.job.manager.ExecutionManager;
+import uws.service.UWSFactory;
+import uws.service.backup.DefaultUWSBackupManager;
+import uws.service.request.RequestParser;
+import uws.service.request.UWSRequestParser;
+
+/**
+ * <p>Utility class gathering tool functions and properties' names useful to deal with a UWS configuration file.</p>
+ * 
+ * <p><i>This class implements the Design Pattern "Utility": no instance of this class can be created, it can not be extended,
+ * and it must be used only thanks to its static classes and attributes.</i></p>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.2 (06/2016)
+ * @since 4.2
+ */
+public final class UWSConfiguration {
+
+	/** Name of the initial parameter to set in the WEB-INF/web.xml file
+	 * in order to specify the location and the name of the UWS configuration file to load. */
+	public final static String UWS_CONF_PARAMETER = "uwsconf";
+	/** Default UWS configuration file. This file is research automatically
+	 * if none is specified in the WEB-INF/web.xml initial parameter {@value #UWS_CONF_PARAMETER}. */
+	public final static String DEFAULT_UWS_CONF_FILE = "uws.properties";
+
+	/* HOME PAGE KEY */
+
+	/** Name/Key of the property specifying the UWS home page to use.
+	 * It can be a file, a URL or a class. If null, the default UWS home page of the library is used.
+	 * By default the default library home page is used. */
+	public final static String KEY_HOME_PAGE = "home_page";
+	/** Name/Key of the property specifying the MIME type of the set home page.
+	 * By default, "text/html" is set. */
+	public final static String KEY_HOME_PAGE_MIME_TYPE = "home_page_mime_type";
+
+	/* SERVICE KEYS */
+
+	/** Name/Key of the property specifying the name of this UWS service. */
+	public final static String KEY_SERVICE_NAME = "service_name";
+	/** Name/Key of the property specifying the description of the UWS service. */
+	public final static String KEY_SERVICE_DESCRIPTION = "service_description";
+
+	/* JOB LISTS */
+
+	/** Name/Key of the property listing all the job lists to have in the UWS service. */
+	public final static String KEY_JOB_LISTS = "joblists";
+
+	/** Regular Expression of a job list name supposed to represent a job list name.
+	 * This name MUST contain NO point, NO equal sign (=) and NO space character. */
+	public final static String REGEXP_JOB_LIST_NAME = "[^\\.=\\s]+";
+
+	/* JOB ATTRIBUTES */
+
+	/** Name/Key of the property specifying the {@link JobThread} instance to use for a specific job list. */
+	public final static String KEY_JOB_THREAD = "job_thread";
+	/** Regular Expression of the name/key of the property specifying the {@link JobThread} instance to use for a given job list.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_JOB_THREAD} ends the regular expression.</i></p> */
+	public final static String REGEXP_JOB_THREAD = REGEXP_JOB_LIST_NAME + "\\." + KEY_JOB_THREAD;
+
+	/** Name/Key of the property listing all input parameters of jobs of a specific job list. */
+	public final static String KEY_PARAMETERS = "job_parameters";
+	/** Regular Expression of the name/key of the property listing all parameters expected for jobs of a given job list.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_PARAMETERS} ends the regular expression.</i></p> */
+	public final static String REGEXP_PARAMETERS = REGEXP_JOB_LIST_NAME + "\\." + KEY_PARAMETERS;
+
+	/* EXECUTION MANAGEMENT */
+
+	/** Name/Key of the property specifying the default execution duration (in milliseconds) set automatically to a job
+	 * if none has been specified by the user. */
+	public final static String KEY_DEFAULT_EXECUTION_DURATION = "default_execution_duration";
+	/** Regular Expression of the name/key of the property specifying the default execution duration for jobs of a given job list.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_DEFAULT_EXECUTION_DURATION} ends the regular expression.</i></p> */
+	public final static String REGEXP_DEFAULT_EXEC_DURATION = REGEXP_JOB_LIST_NAME + "\\." + KEY_DEFAULT_EXECUTION_DURATION;
+
+	/** Name/Key of the property specifying the maximum execution duration (in milliseconds) that can be set on a job. */
+	public final static String KEY_MAX_EXECUTION_DURATION = "max_execution_duration";
+	/** Regular Expression of the name/key of the property specifying the maximum execution duration for jobs of a given job list.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_MAX_EXECUTION_DURATION} ends the regular expression.</i></p> */
+	public final static String REGEXP_MAX_EXEC_DURATION = REGEXP_JOB_LIST_NAME + "\\." + KEY_MAX_EXECUTION_DURATION;
+
+	/** Default value of the property {@link #KEY_DEFAULT_EXECUTION_DURATION} and {@link #KEY_MAX_EXECUTION_DURATION}: {@value #DEFAULT_EXECUTION_DURATION}. */
+	public final static int DEFAULT_EXECUTION_DURATION = 0;
+
+	/** Name/Key of the property specifying the maximum number of jobs that can run in parallel inside a specific job list. */
+	public final static String KEY_MAX_RUNNING_JOBS = "max_running_jobs";
+	/** Regular Expression of the name/key of the property specifying maximum number of jobs that can run in parallel inside
+	 * the specified job list.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_MAX_RUNNING_JOBS} ends the regular expression.</i></p> */
+	public final static String REGEXP_MAX_RUNNING_JOBS = REGEXP_JOB_LIST_NAME + "\\." + KEY_MAX_RUNNING_JOBS;
+
+	/** Name/Key of the property specifying the {@link ExecutionManager} instance that a specific job list must use. */
+	public final static String KEY_EXECUTION_MANAGER = "execution_manager";
+	/** Regular Expression of the name/key of the property specifying the {@link ExecutionManager} instance that a given job list must use.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_EXECUTION_MANAGER} ends the regular expression.</i></p> */
+	public final static String REGEXP_EXECUTION_MANAGER = REGEXP_JOB_LIST_NAME + "\\." + KEY_EXECUTION_MANAGER;
+
+	/* DESTRUCTION MANAGEMENT */
+
+	/** Name/Key of the property specifying the default destruction interval (actually a duration between the creation and the destruction
+	 * of the job) set automatically to a job if none has been specified by the user. */
+	public final static String KEY_DEFAULT_DESTRUCTION_INTERVAL = "default_destruction_interval";
+	/** Regular Expression of the name/key of the property specifying the default destruction interval for jobs of a given job list.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_DEFAULT_DESTRUCTION_INTERVAL} ends the regular expression.</i></p> */
+	public final static String REGEXP_DEFAULT_DESTRUCTION_INTERVAL = REGEXP_JOB_LIST_NAME + "\\." + KEY_DEFAULT_DESTRUCTION_INTERVAL;
+
+	/** Name/Key of the property specifying the maximum destruction interval (actually a duration between the creation and the destruction
+	 * of the job) set automatically to a job if none has been specified by the user. */
+	public final static String KEY_MAX_DESTRUCTION_INTERVAL = "max_destruction_interval";
+	/** Regular Expression of the name/key of the property specifying the maximum destruction interval for jobs of a given job list.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.</i></p> */
+	public final static String KEY_REGEXP_MAX_DESTRUCTION_INTERVAL = REGEXP_JOB_LIST_NAME + "\\." + KEY_MAX_DESTRUCTION_INTERVAL;
+
+	/** Name/Key of the property specifying the {@link DestructionManager} instance that a specific job list must use. */
+	public final static String KEY_DESTRUCTION_MANAGER = "destruction_manager";
+	/** Regular Expression of the name/key of the property specifying the {@link DestructionManager} instance that a given job list must use.
+	 * <p><i>The first part of this regular expression ({@link #REGEXP_JOB_LIST_NAME}) is supposed to be the job list name.
+	 * Then a point is appended and finally {@link #KEY_DESTRUCTION_MANAGER} ends the regular expression.</i></p> */
+	public final static String REGEXP_DESTRUCTION_MANAGER = REGEXP_JOB_LIST_NAME + "\\." + KEY_DESTRUCTION_MANAGER;
+
+	/* FILE MANAGER KEYS */
+
+	/** Name/Key of the property setting the file manager to use in the UWS service. */
+	public final static String KEY_FILE_MANAGER = "file_manager";
+	/** Value of the property {@link #KEY_FILE_MANAGER} specifying a local file manager. */
+	public final static String VALUE_LOCAL = "local";
+	/** Default value of the property {@link #KEY_FILE_MANAGER}: {@value #DEFAULT_FILE_MANAGER}. */
+	public final static String DEFAULT_FILE_MANAGER = VALUE_LOCAL;
+	/** Name/Key of the property setting the local root directory where all UWS files must be stored.
+	 * <em>This property is used only if {@link #KEY_FILE_MANAGER} is set to {@link #VALUE_LOCAL}.</em> */
+	public final static String KEY_FILE_ROOT_PATH = "file_root_path";
+	/** Name/Key of the property indicating whether the jobs must be saved by user or not.
+	 * If yes, there will be one directory per user. Otherwise, all jobs are backuped in the same directory
+	 * (generally {@link #KEY_FILE_ROOT_PATH}). */
+	public final static String KEY_DIRECTORY_PER_USER = "directory_per_user";
+	/** Default value of the property {@link #KEY_DIRECTORY_PER_USER}: {@value #DEFAULT_DIRECTORY_PER_USER}. */
+	public final static boolean DEFAULT_DIRECTORY_PER_USER = false;
+	/** Name/Key of the property indicating whether the user directories (in which jobs of the user are backuped)
+	 * must be gathered in less directories. If yes, the groups are generally made using the alphabetic order.
+	 * The idea is to reduce the number of apparent directories and to easier the research of a user directory. */
+	public final static String KEY_GROUP_USER_DIRECTORIES = "group_user_directories";
+	/** Default value of the property {@link #KEY_GROUP_USER_DIRECTORIES}: {@value #DEFAULT_GROUP_USER_DIRECTORIES}. */
+	public final static boolean DEFAULT_GROUP_USER_DIRECTORIES = false;
+
+	/* LOG KEYS */
+
+	/** Name/Key of the property specifying the minimum type of messages (i.e. DEBUG, INFO, WARNING, ERROR, FATAL)
+	 * that must be logged. By default all messages are logged...which is equivalent to set this property to "DEBUG". */
+	public final static String KEY_MIN_LOG_LEVEL = "min_log_level";
+	/** Name/Key of the property specifying the frequency of the log file rotation.
+	 * By default the log rotation occurs every day at midnight. */
+	public final static String KEY_LOG_ROTATION = "log_rotation";
+
+	/* UWS BACKUP */
+
+	/** Name/Key of the property specifying the frequency (in milliseconds) of jobs backup.
+	 * This property accepts three types of value: "never" (default), "user_action" (the backup of a job is done when
+	 * it is modified), or a numeric positive value (expressed in milliseconds). */
+	public final static String KEY_BACKUP_FREQUENCY = "backup_frequency";
+	/** Value of the property {@link #KEY_BACKUP_FREQUENCY} indicating that jobs should never be backuped. */
+	public final static String VALUE_NEVER = "never";
+	/** Value of the property {@link #KEY_BACKUP_FREQUENCY} indicating that job backup should occur only when the user
+	 * creates or modifies one of his jobs. This value can be used ONLY IF {@link #KEY_BACKUP_BY_USER} is "true". */
+	public final static String VALUE_USER_ACTION = "user_action";
+	/** Default value of the property {@link #KEY_BACKUP_FREQUENCY}: {@link #DEFAULT_BACKUP_FREQUENCY}. */
+	public final static long DEFAULT_BACKUP_FREQUENCY = DefaultUWSBackupManager.MANUAL;	// = "never" => no UWS backup manager
+	/** Name/Key of the property indicating whether there should be one backup file per user or one file for all. */
+	public final static String KEY_BACKUP_BY_USER = "backup_by_user";
+	/** Default value of the property {@link #KEY_BACKUP_BY_USER}: {@value #DEFAULT_BACKUP_BY_USER}.
+	 * This property can be enabled only if a user identification method is provided. */
+	public final static boolean DEFAULT_BACKUP_BY_USER = false;
+
+	/* USER IDENTIFICATION */
+
+	/** Name/Key of the property specifying the user identification method to use.
+	 * None is implemented by the library, so a class must be provided as value of this property. */
+	public final static String KEY_USER_IDENTIFIER = "user_identifier";
+
+	/* REQUEST PARSER */
+
+	/** Name/Key of the property specifying the {@link RequestParser} class to use instead of the default {@link UWSRequestParser}. */
+	public final static String KEY_REQUEST_PARSER = "request_parser";
+
+	/* SERIALIZATION */
+
+	/** Name/Key of the property specifying a list of UWS serializers to add to the UWS service.
+	 * By default, this list if empty ; only the default UWS serializers exist. */
+	public final static String KEY_ADD_SERIALIZERS = "additional_serializers";
+	/** Name/Key of the property specifying the XSLT stylesheet to use when job are serialized in XML. */
+	public final static String KEY_XSLT_STYLESHEET = "xslt_stylesheet";
+	/** Name/Key of the property specifying the error writer the UWS service must use. */
+	public final static String KEY_ERROR_WRITER = "error_writer";
+
+	/* ADDITIONAL UWS ACTIONS */
+
+	/** Name/Key of the property specifying a list of actions to add to the UWS service.
+	 * By default, this list if empty ; only the default UWS actions exist. */
+	public final static String KEY_ADD_UWS_ACTIONS = "additional_actions";
+
+	/* CUSTOM FACTORY */
+
+	/** Name/Key of the property specifying the {@link UWSFactory} class to use instead of the default {@link ConfigurableUWSFactory}.
+	 * <em>Setting a value to this property could disable several properties of the UWS configuration file.</em> */
+	public final static String KEY_UWS_FACTORY = "uws_factory";
+
+	/** No instance of this class should be created. */
+	private UWSConfiguration(){}
+
+	/**
+	 * <p>Read the asked property from the given Properties object.</p>
+	 * <ul>
+	 * 	<li>The returned property value is trimmed (no space at the beginning and at the end of the string).</li>
+	 * 	<li>If the value is empty (length=0), NULL is returned.</li>
+	 * </ul>
+	 * 
+	 * @param prop	List of property
+	 * @param key	Property whose the value is requested.
+	 * 
+	 * @return		Return property value.
+	 */
+	public final static String getProperty(final Properties prop, final String key){
+		if (prop == null)
+			return null;
+
+		String value = prop.getProperty(key);
+		if (value != null){
+			value = value.trim();
+			return (value.length() == 0) ? null : value;
+		}
+
+		return value;
+	}
+
+	/**
+	 * Extract the job list name prefixing the given property name.
+	 * 
+	 * <p><b>Important:</b>
+	 * 	This function aims to be used for properties prefixed by a job list name such as
+	 * 	{@link #REGEXP_JOB_THREAD}, {@link #REGEXP_PARAMETERS}, ...
+	 * </p>
+	 * 
+	 * @param compoundPropertyName	Property name prefixed by a job list name.
+	 * 
+	 * @return	The prefix of the given property name,
+	 *        	or <code>null</code> if the given name is <code>null</code>
+	 *             or if it is not prefixed by a valid job list name.
+	 */
+	public final static String extractJobListName(final String compoundPropertyName){
+		if (compoundPropertyName == null || !compoundPropertyName.matches(REGEXP_JOB_LIST_NAME + "\\..+"))
+			return null;
+		return compoundPropertyName.substring(0, compoundPropertyName.indexOf('.'));
+	}
+
+	/**
+	 * Test whether a property value is a class name.
+	 * Expected syntax: a non-empty string surrounded by brackets ('{' and '}').
+	 * 
+	 * Note: The class name itself is not checked!
+	 * 
+	 * @param value	Property value.
+	 * 
+	 * @return <i>true</i> if the given value is formatted as a class name, <i>false</i> otherwise.
+	 */
+	public final static boolean isClassName(final String value){
+		return (value != null && value.length() > 2 && value.charAt(0) == '{' && value.charAt(value.length() - 1) == '}');
+	}
+
+	/**
+	 * Fetch the class object corresponding to the class name provided between brackets in the given value.
+	 * 
+	 * @param value			Value which is supposed to contain the class name between brackets (see {@link #isClassName(String)} for more details)
+	 * @param propertyName	Name of the property associated with the parameter "value".
+	 * @param expectedType	Type of the class expected to be returned ; it is also the type which parameterizes this function: C.
+	 * 
+	 * @return	The corresponding Class object.
+	 * 
+	 * @throws UWSException	If the class name is incorrect
+	 *                     	or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType").
+	 * 
+	 * @see #isClassName(String)
+	 */
+	@SuppressWarnings("unchecked")
+	public final static < C > Class<? extends C> fetchClass(final String value, final String propertyName, final Class<C> expectedType) throws UWSException{
+		if (!isClassName(value))
+			return null;
+
+		String classPath = value.substring(1, value.length() - 1).trim();
+		if (classPath.isEmpty())
+			return null;
+
+		try{
+			Class<? extends C> classObject = (Class<? extends C>)Class.forName(classPath);
+			if (!expectedType.isAssignableFrom(classObject))
+				throw new UWSException("The class specified by the property \"" + propertyName + "\" (" + value + ") is not implementing " + expectedType.getName() + ".");
+			else
+				return classObject;
+		}catch(ClassNotFoundException cnfe){
+			throw new UWSException("The class specified by the property \"" + propertyName + "\" (" + value + ") can not be found.");
+		}catch(ClassCastException cce){
+			throw new UWSException("The class specified by the property \"" + propertyName + "\" (" + value + ") is not implementing " + expectedType.getName() + ".");
+		}
+	}
+
+	/**
+	 * Test whether the specified class has a constructor with the specified parameters.
+	 * 
+	 * @param propValue		Value which is supposed to contain the class name between brackets (see {@link #isClassName(String)} for more details)
+	 * @param propName		Name of the property associated with the parameter "propValue".
+	 * @param expectedType	Type of the class expected to be returned ; it is also the type which parameterizes this function: C.
+	 * @param pTypes		List of each constructor parameter type. Each type MUST be exactly the type declared in the class constructor to select. <i>NULL or empty array if no parameter.</i>
+	 * 
+	 * @return	<code>true</code> if the specified class has a constructor with the specified parameters,
+	 *        	<code>false</code> otherwise.
+	 * 
+	 * @throws UWSException	If the class name is incorrect
+	 *                     	or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType").
+	 */
+	public final static < C > boolean hasConstructor(final String propValue, final String propName, final Class<C> expectedType, final Class<?>[] pTypes) throws UWSException{
+		// Ensure the given name is a class name specification:
+		if (!isClassName(propValue))
+			throw new UWSException("Class name expected for the property \"" + propName + "\" instead of: \"" + propValue + "\"! The specified class must extend/implement " + expectedType.getName() + ".");
+
+		// Fetch the class object:
+		Class<? extends C> classObj = fetchClass(propValue, propName, expectedType);
+		try{
+
+			// Get a constructor matching the given parameters list:
+			classObj.getConstructor((pTypes == null) ? new Class<?>[0] : pTypes);
+
+			return true;
+
+		}catch(Exception e){
+			return false;
+		}
+	}
+
+	/**
+	 * Fetch the specified constructor of the class corresponding to the class name provided between brackets in the given value.
+	 * 
+	 * <p><b>IMPORTANT:</b>
+	 * 	The number and types of given parameters MUST match exactly to the list of parameter types.
+	 * </p>
+	 * 
+	 * @param propValue		Value which is supposed to contain the class name between brackets (see {@link #isClassName(String)} for more details)
+	 * @param propName		Name of the property associated with the parameter "propValue".
+	 * @param expectedType	Type of the class expected to be returned ; it is also the type which parameterizes this function: C.
+	 * @param pTypes		List of each constructor parameter type. Each type MUST be exactly the type declared in the class constructor to select. <i>NULL or empty array if no parameter.</i>
+	 * 
+	 * @return	The corresponding constructor.
+	 * 
+	 * @throws UWSException	If the class name is incorrect
+	 *                     	or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType")
+	 *                     	or if the constructor with the specified parameters can not be found.
+	 */
+	public final static < C > Constructor<? extends C> fetchConstructor(final String propValue, final String propName, final Class<C> expectedType, final Class<?>[] pTypes) throws UWSException{
+		// Ensure the given name is a class name specification:
+		if (!isClassName(propValue))
+			throw new UWSException("Class name expected for the property \"" + propName + "\" instead of: \"" + propValue + "\"! The specified class must extend/implement " + expectedType.getName() + ".");
+
+		// Fetch the class object:
+		Class<? extends C> classObj = fetchClass(propValue, propName, expectedType);
+		try{
+
+			// Get a constructor matching the given parameters list:
+			return classObj.getConstructor((pTypes == null) ? new Class<?>[0] : pTypes);
+
+		}catch(NoSuchMethodException e){
+			// List parameters' type:
+			StringBuffer pTypesStr = new StringBuffer();
+			if (pTypes != null){
+				for(int i = 0; i < pTypes.length; i++){
+					if (pTypesStr.length() > 0)
+						pTypesStr.append(", ");
+					if (pTypes[i] == null)
+						pTypesStr.append("NULL");
+					pTypesStr.append(pTypes[i].getName());
+				}
+			}
+			// Throw the error:
+			throw new UWSException("Missing constructor " + classObj.getName() + "(" + pTypesStr.toString() + ")! See the value \"" + propValue + "\" of the property \"" + propName + "\".");
+		}catch(SecurityException se){
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, se, "Security error when trying to fetch the constructor with a single parameter of type " + expectedType.getName() + " of the class \"" + propValue + "\" specified by the property \"" + propName + "\"!");
+		}
+	}
+
+	/**
+	 * <p>Create an instance of the specified class. The class name is expected to be surrounded by {} in the given value.</p>
+	 * 
+	 * <p>The instance is created using the empty constructor of the specified class.</p>
+	 * 
+	 * @param propValue		Value which is supposed to contain the class name between brackets (see {@link #isClassName(String)} for more details)
+	 * @param propName		Name of the property associated with the parameter "propValue".
+	 * @param expectedType	Type of the class expected to be returned ; it is also the type which parameterizes this function: C.
+	 * 
+	 * @return	The corresponding instance.
+	 * 
+	 * @throws UWSException	If the class name is incorrect
+	 *                     	or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType")
+	 *                     	or if the specified class has no empty constructor
+	 *                     	or if an error occurred while calling this constructor.
+	 * 
+	 * @see #isClassName(String)
+	 * @see #fetchClass(String, String, Class)
+	 */
+	public final static < C > C newInstance(final String propValue, final String propName, final Class<C> expectedType) throws UWSException{
+		return newInstance(propValue, propName, expectedType, null, null);
+	}
+
+	/**
+	 * <p>Create an instance of the specified class. The class name is expected to be surrounded by {} in the given value.</p>
+	 * 
+	 * <p><b>IMPORTANT:</b>
+	 * 	The instance is created using the constructor whose the declaration matches exactly with the given list of parameter types.
+	 * 	The number and types of given parameters MUST match exactly to the list of parameter types.
+	 * </p>
+	 * 
+	 * @param propValue		Value which is supposed to contain the class name between brackets (see {@link #isClassName(String)} for more details)
+	 * @param propName		Name of the property associated with the parameter "propValue".
+	 * @param expectedType	Type of the class expected to be returned ; it is also the type which parameterizes this function: C.
+	 * @param pTypes		List of each constructor parameter type. Each type MUST be exactly the type declared in the class constructor to select. <i>NULL or empty array if no parameter.</i>
+	 * @param parameters	List of all constructor parameters. The number of object MUST match exactly the number of classes provided in the parameter pTypes. <i>NULL or empty array if no parameter.</i>
+	 * 
+	 * @return	The corresponding instance.
+	 * 
+	 * @throws UWSException	If the class name is incorrect
+	 *                     	or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType")
+	 *                     	or if the constructor with the specified parameters can not be found
+	 *                     	or if an error occurred while calling this constructor.
+	 * 
+	 * @see #isClassName(String)
+	 * @see #fetchClass(String, String, Class)
+	 */
+	public final static < C > C newInstance(final String propValue, final String propName, final Class<C> expectedType, final Class<?>[] pTypes, final Object[] parameters) throws UWSException{
+		// Ensure the given name is a class name specification:
+		if (!isClassName(propValue))
+			throw new UWSException("Class name expected for the property \"" + propName + "\" instead of: \"" + propValue + "\"! The specified class must extend/implement " + expectedType.getName() + ".");
+
+		Class<? extends C> classObj = null;
+		try{
+
+			// Fetch the class object:
+			classObj = fetchClass(propValue, propName, expectedType);
+
+			// Get a constructor matching the given parameters list:
+			Constructor<? extends C> constructor = classObj.getConstructor((pTypes == null) ? new Class<?>[0] : pTypes);
+
+			// Finally create a new instance:
+			return constructor.newInstance((parameters == null) ? new Object[0] : parameters);
+
+		}catch(NoSuchMethodException e){
+			// List parameters' type:
+			StringBuffer pTypesStr = new StringBuffer();
+			if (pTypes != null){
+				for(int i = 0; i < pTypes.length; i++){
+					if (pTypesStr.length() > 0)
+						pTypesStr.append(", ");
+					if (pTypes[i] == null)
+						pTypesStr.append("NULL");
+					pTypesStr.append(pTypes[i].getName());
+				}
+			}
+			// Throw the error:
+			throw new UWSException("Missing constructor " + classObj.getName() + "(" + pTypesStr.toString() + ")! See the value \"" + propValue + "\" of the property \"" + propName + "\".");
+		}catch(InstantiationException ie){
+			throw new UWSException("Impossible to create an instance of an abstract class: \"" + classObj.getName() + "\"! See the value \"" + propValue + "\" of the property \"" + propName + "\".");
+		}catch(InvocationTargetException ite){
+			if (ite.getCause() != null){
+				if (ite.getCause() instanceof UWSException)
+					throw (UWSException)ite.getCause();
+				else
+					throw new UWSException(ite.getCause());
+			}else
+				throw new UWSException(ite);
+		}catch(UWSException te){
+			throw te;
+		}catch(Exception ex){
+			throw new UWSException(UWSException.NOT_FOUND, ex, "Impossible to create an instance of " + expectedType.getName() + " as specified in the property \"" + propName + "\": \"" + propValue + "\"!");
+		}
+	}
+
+	/**
+	 * <p>Lets parsing a limit (for upload, ...) with its numeric value and its unit.</p>
+	 * <p>
+	 * 	Here is the expected syntax: num_val[unit].
+	 * 	Where unit is optional and should be one of the following values: B, kB, MB or GB.
+	 * 	If the unit is not specified, it is set by default to BYTES.
+	 * </p>
+	 * <p><i>Note: If the value is strictly less than 0 (whatever is the unit), the returned value will be -1.</i></p>
+	 * 
+	 * @param value				Property value (must follow the limit syntax: num_val[unit] ; ex: 20kB or 2000 (for 2000 bytes)).
+	 * @param propertyName		Name of the property which specify the limit.
+	 * 
+	 * @return	The expressed unit in bytes
+	 *        	or -1, if the given value was incorrect or negative.
+	 * 
+	 * @throws UWSException	If the syntax is incorrect or if a not allowed unit has been used.
+	 */
+	public final static long parseLimit(String value, final String propertyName) throws UWSException{
+		// Remove any whitespace inside or outside the numeric value and its unit:
+		if (value != null)
+			value = value.replaceAll("\\s", "");
+
+		// If empty value, return an infinite limit:
+		if (value == null || value.length() == 0)
+			return -1;
+
+		// A. Parse the string from the end in order to extract the unit part.
+		//    The final step of the loop is the extraction of the numeric value, when the first digit is encountered.
+		long numValue = -1;
+		StringBuffer buf = new StringBuffer();
+		for(int i = value.length() - 1; i >= 0; i--){
+			// if a digit, extract the numeric value:
+			if (value.charAt(i) >= '0' && value.charAt(i) <= '9'){
+				try{
+					numValue = Integer.parseInt(value.substring(0, i + 1));
+					break;
+				}catch(NumberFormatException nfe){
+					throw new UWSException("Integer expected for the property " + propertyName + " for the substring \"" + value.substring(0, i + 1) + "\" of the whole value: \"" + value + "\"!");
+				}
+			}
+			// if a character, store it for later processing:
+			else
+				buf.append(value.charAt(i));
+
+		}
+
+		// B. Parse the unit.
+		// if no unit, set BYTES by default:
+		if (buf.length() == 0)
+			;
+		// if the unit is too long, throw an exception:
+		else if (buf.length() > 2)
+			throw new UWSException("Unknown limit unit (" + buf.reverse().toString() + ") for the property " + propertyName + ": \"" + value + "\"!");
+		// try to identify the unit:
+		else{
+			// the base unit: bytes
+			if (buf.charAt(0) != 'B')
+				throw new UWSException("Unknown limit unit (" + buf.reverse().toString() + ") for the property " + propertyName + ": \"" + value + "\"!");
+			// the 10-power of the base unit, if any:
+			if (buf.length() > 1){
+				switch(buf.charAt(1)){
+					case 'G':
+						numValue *= 1000;
+					case 'M':
+						numValue *= 1000;
+					case 'k':
+						numValue *= 1000;
+						break;
+					default:
+						throw new UWSException("Unknown limit unit (" + buf.reverse().toString() + ") for the property " + propertyName + ": \"" + value + "\"!");
+				}
+			}
+		}
+
+		return (numValue < 0) ? -1 : numValue;
+	}
+
+}
diff --git a/src/uws/config/uws_configuration_file.html b/src/uws/config/uws_configuration_file.html
new file mode 100644
index 0000000000000000000000000000000000000000..3882d943455aaa65ec2daf71fcd4e79e4821eca8
--- /dev/null
+++ b/src/uws/config/uws_configuration_file.html
@@ -0,0 +1,658 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="UTF-8">
+		<title>UWS configuration file</title>
+		<style type="text/css">
+			p { text-align: justify; text-indent: 1em; }
+			table {
+				font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
+				width:100%;
+				border-collapse:collapse;
+			}
+			td, th {
+				font-size:1em;
+				border:1px solid #084B8A;
+				padding:3px 7px 2px 7px;
+				color: black;
+				background-color: #EFF5FB;
+			}
+			th, td[colspan="5"] {
+				font-size:1em;
+				text-align: left;
+				padding-top:5px;
+				padding-bottom:4px;
+				background-color:#045FB4;
+				color:#ffffff;
+			}
+			td[colspan="5"] {
+				font-style: italic;
+			}
+			tr.mandatory td {
+				color: black;
+				background-color: #CEE3F6;
+			}
+			th:nth-child(2), td:nth-child(2) {
+				text-align: center;
+				max-width: 2em;
+			}
+			tr.mandatory td:nth-child(2):after{
+				color: red;
+				font-weight: bold;
+				content: "M";
+			}
+			td:nth-child(5) {
+				font-family: monospace;
+			}
+			table ul  { list-style-position:inside; padding: 0; margin: 0; }
+			table p   { margin: 0; padding: 0; text-align: left; text-indent: 0; }
+			table p+p { padding-top: .5em; }
+			
+			/* JUST FOR DEVELOPMENT GUIDELINE */
+			.done, .mandatory .done { color: green; }
+			.later, .mandatory .later { color: orange; }
+			.todo, .mandatory .todo {color: red; }
+			
+		</style>
+		<script type="text/javascript">
+			function toggleOptional(){
+				var button = document.getElementById("toggleOptional");
+				var display = (button.value == "hide") ? 'none' : '';
+				var lines = document.querySelectorAll("tr.optional");
+				for(var i=0 ; i<lines.length ; i++)
+					lines[i].style.display = display;
+				button.value = (display == '') ? 'hide' : 'show';
+				button.textContent = (display == '') ? 'Hide optional' : 'Show optional';
+			}
+		</script>
+	</head>
+	<body>
+		<h1>UWS Configuration File</h1>
+		<p>
+			All properties listed in the below table are all the possible UWS configuration properties.
+			Some of them are mandatory. If one of these properties is missing, the UWS Service will not be able to start:
+			an error will be displayed immediately in the application server log and a HTTP 503 error will be sent when accessing the UWS URL.
+		</p>
+		<p>Besides, you should know that any property key not listed in this table will be ignored without error or warning message.</p>
+		<p>
+			However, any not allowed property value will generate a warning message in the application server log and the default value will be kept.
+			Thus, the UWS Service will be started and available but the desired configuration value will not be set. So, you should take a look
+			at the application server log every times you start the UWS Service!
+		</p>
+		
+		<p>Here is an empty minimum UWS configuration file: <a href="uws_min.properties">uws_min.properties</a> and a complete one: <a href="uws_full.properties">uws_full.properties</a>.</p>
+		
+		
+		<p><b>Important note:</b> Any limit value is an integer and so can be at most: 2<sup>31</sup>-1 bytes/rows = 2147483647B/R (or also for the byte unit: = 2147483kB = 2147MB = 2GB).
+		Otherwise, you should use the null value 0 to raise the limit constraint.</p>
+		
+		<p><i><u>Legend:</u> <b style="color:red">M</b> means that the property is mandatory. If nothing is written for the second column, the property is optional.</i></p>
+		
+		<button id="toggleOptional" value="hide" onClick="toggleOptional();">Hide optional</button> <i><span id="nbMandatory"></span>/<span id="nbTotal"></span> mandatory properties</i>
+		<br /><br/>
+		<table>
+			<tr>
+				<th>Property</th>
+				<th></th>
+				<th>Type</th>
+				<th>Description</th>
+				<th>Example</th>
+			</tr>
+			
+			<tr><td colspan="5">General</td></tr>
+			<tr class="optional">
+				<td class="todo">home_page</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>This property lets set a custom home page. 4 different kinds of value are accepted:</p>
+					<ul>
+						<li><u>nothing (default)</u>: the default home page provided by the library (a XML document listing all available job lists).</li>
+						<li><u>name or relative path of a file</u>: this method MUST be chosen if the new home page is a JSP file. This file MUST be inside the directory WebContent of your web application.</li>
+						<li><u>a URI starting with <code>file://</code></u>: in this method the local file pointed by the URI will be merely returned when the home page will be requested.</li>
+						<li><u>a URL</u>: here, a redirection toward this URL will be made at each request on the home page</li>
+						<li><u>a class name</u>: the class name of an extension of uws.service.actions.ShowHomePage which must replace the default home page action. This class MUST have at least one constructor with exactly one parameter not NULL of type uws.service.UWSService.</li>
+					</ul>
+					<p><em>By default, the default home page provided by the library is used.</em></p>
+				</td>
+				<td><ul><li>my_uws_homepage.jsp</li><li>jsp/my_uws_homepage.jsp</li><li>file:///home/foo/customHomePage.html</li><li>http://...</li><li>{aPackage.NewHomePage}</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">home_page_mime_type</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>MIME type of the service home page.</p>
+					<p>This property is used only if the specified "home_page" is a local file path (i.e. if "home_page=file://...").</p>
+					<p>If no value is provided "text/html" will be set by default.</p>
+					<p><em>Default: <code>text/html</code></em></p>
+				</td>
+				<td><ul><li>text/html <em>(default)</em></li><li>text/plain</li><li>application/xml</li></ul></td>
+			</tr>
+			
+			<tr><td colspan="5">Service</td></tr>
+			<tr class="optional">
+				<td class="todo">service_name</td>
+				<td></td>
+				<td>text</td>
+				<td>Name of the UWS Service.</td>
+				<td></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">service_description</td>
+				<td></td>
+				<td>text</td>
+				<td>Description of the UWS Service.</td>
+				<td></td>
+			</tr>
+			
+			<tr><td colspan="5">Job list</td></tr>
+			<tr class="mandatory">
+				<td class="todo">joblists</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Comma separated list of job list.</p>
+					<p>
+						At least one name MUST be provided. Otherwise, the UWS service will have no
+						job list and the job execution will not be possible.
+					</p>
+				</td>
+				<td><ul><li>jobs</li><li>myjobs,foo,bar</li></ul></td>
+			</tr>
+			
+			<tr><td colspan="5">Job</td></tr>
+			<tr class="mandatory">
+				<td class="todo"><i>&lt;jlName&gt;.</i>job_thread</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Class describing the execution of all jobs managed by the job list specified as prefixed of the property name.</p>
+					<p>
+						This property must be a class name (given between {...}). It must reference an extension of uws.job.JobThread.
+						This implementation must have at least a constructor with one parameter of type uws.job.UWSJob.
+					</p>
+				</td>
+				<td>{aPackage.MySuperJob}</td>
+			</tr>
+			<tr class="optional">
+				<td class="todo"><i>&lt;jlName&gt;.</i>job_parameters</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>List of all expected parameters.</p>
+					<p>
+						The value of this property is expected to be a comma separated list of parameters. At least a name without any space character is expected.
+						Limits about each parameter <em>MAY</em> be provided. Below are described all types of parameter definition that are supported in this property file:
+					</p>
+					<br/>
+					<ul>
+						<li><em>Just a name:</em>
+							<p style="text-align: center"><em>paramName</em></p>
+						<li><em>String parameter:</em>
+							<p style="text-align: center"><b>[</b> <em>paramName</em><b> , </b><em>modif?</em><b> , string , "</b><em>default</em><b>" , / </b><em>regular_expr</em><b> / </b><em>i</em><b> ]</b></p></li>
+						<li><em>Numeric parameter:</em>
+							<p style="text-align: center"><b>[</b> <em>paramName</em> <b>,</b> <em>modif?</em> <b>, numeric ,</b> <em>default</em> <b>,</b> <em>minimum</em> <b>,</b> <em>maximum</em> <b>]</b></p></li>
+						<li><em>Duration parameter:</em>
+							<p style="text-align: center"><b>[</b> <em>paramName</em> <b>,</b> <em>modif?</em> <b>, numeric ,</b> <em>default</em> <b>,</b> <em>minimum</em> <b>,</b> <em>maximum</em> <b>]</b></p></li>
+						<li><em>Custom:</em>
+							<p style="text-align: center"><b>[</b> <em>paramName</em> <b>, {</b> <em>aPackage.ClassName</em> <b>} ]</b></p></li>
+					</ul>
+					<p>
+						It is <em>not mandatory</em> to declare all expected parameters in this list. Even non listed parameters will appear
+						in the job description. However any parameter name provided in this list will be recognized <em>case insensitively</em> among the parameters of a job.
+						For instance, if the list contains the parameter <code>foo</code> and the user has provided the parameters <code>foo=bar&FOO=stuff</code>
+						when creating a job, the library will consider them as the same. It will take the last given value and will report this parameter in its
+						job description as <code>foo=stuff</code>.
+					</p>
+					<p>
+						All attributes except the name and the type (or the class name) are optional. They can be omitted which implies they will be set to their default value ;
+						generally <code>null</code>. Here are more details about each attribute:
+					</p>
+					<p>
+						<em>modif?</em> is a boolean flag aiming to indicate whether the value of the parameter can be modified after initialization of the job.
+						Any value (case insensitive) among the following list is interpreted as <code>true</code> ; any other value as <code>false</code>:
+						<code>true</code>, <code>t</code>, <code>yes</code> and <code>y</code>. If this flag is omitted it will be by default set to <code>true</code>
+						(i.e. the parameter can be modified after initialization).
+					</p>
+					<p>
+						<em>The regular expression</em> must be enclosed by <code>/</code>. It can contain any character except <code>/</code>. If a such character
+						has to be provided anyway, it can be done by doubling it. For instance: <code>/foo//bar/i</code>. The letter <code>i</code> after the regular
+						expression is optional. It indicates that the regular expression must be evaluated case insensitively.
+					</p>
+					<p>
+						<em>The default, minimum and maximum values</em> of numeric and duration parameters must be valid numeric values. For a numeric, any kind of
+						numerical value is accepted. For a duration, only positive or zero long/integer values are accepted. If one of these values is omitted it
+						will be considered as <code>null</code> (i.e. no limitation in the case of the minimum and maximum).
+					</p>
+					<p>
+						<em>A duration value</em> (default, minimum, maximum and even a user value provided when submitting/updating a job) must be a positive or zero long/integer value
+						eventually followed by a unit. Without a unit, by default the duration will be considered as being expressed in milliseconds. The supported
+						unit suffixes are the following (case sensitive): <code>milliseconds</code> (or <code>ms</code>), <code>seconds</code> (or <code>sec</code> or <code>s</code>),
+						<code>minutes</code> (or <code>min</code> or <code>m</code>), <code>hours</code> (or <code>h</code>), <code>days</code> (or <code>D</code>),
+						<code>weeks</code> (or <code>W</code> ; = 7 days), <code>months</code> (or <code>M</code> ; = 30 days) and <code>years</code> (or <code>Y</code> ; = 365 days).
+					</p>
+				</td>
+				<td><ul><li>myFreeParam</li><li>[myCustomParam, {aPackage.MyCustomController}]</li><li>[myNumericParam,false,numeric,0,-180,180]</li><li>[myDurationParam,,duration,,1min,3 hours]</li><li>myFreeParam, [myStringParam,yes,string,"foo",/(foo|bar|other)/i]</li></ul></td>
+			</tr>
+			
+			<tr><td colspan="5">Execution management</td></tr>
+			<tr class="optional">
+				<td class="todo"><em>&lt;jlName&gt;.</em>default_execution_duration</td>
+				<td></td>
+				<td>integer/text</td>
+				<td>
+					<p>Default execution duration of the jobs of the specified job list. The prefix "default" means here that the execution duration
+						will be this one if the client does not set one.</p>
+					<p>The default duration MUST be less or equals to the maximum execution duration. If this rule is not respected, the default execution duration is set immediately
+					 to the maximum execution duration.</p>
+					<p>A negative or null value means there is no restriction on the default execution duration: the execution could never end. Float values are not allowed.</p>
+					<p>This duration is expressed by default in milliseconds, but a different unit can be specified. This unit must suffix the value. Allowed units are the following
+						(<em>case sensitive!!</em>): <code>milliseconds</code> (or <code>ms</code>), <code>seconds</code> (or <code>sec</code> or <code>s</code>),
+						<code>minutes</code> (or <code>min</code> or <code>m</code>), <code>hours</code> (or <code>h</code>), <code>days</code> (or <code>D</code>),
+						<code>weeks</code> (or <code>W</code> ; = 7 days), <code>months</code> (or <code>M</code> ; = 30 days) and <code>years</code> (or <code>Y</code> ; = 365 days).</p>
+					<p><em>Default: <code>0</code> (no restriction)</em></p>
+				</td>
+				<td><ul><li>600000 <em>(10 minutes)</em></li><li>10m</li><li>10 minutes</li><li>10min</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo"><em>&lt;jlName&gt;.</em>max_execution_duration</td>
+				<td></td>
+				<td>integer/text</td>
+				<td>
+					<p>Maximum execution duration of the jobs of the specified job list. The prefix "max" means here that the client can not set a time greater than this one.</p>
+					<p>The maximum duration MUST be greater or equals to the default execution duration. If this rule is not respected, the default execution duration is set immediately
+					 to the maximum execution duration.</p>
+					<p>A negative or null value means there is no restriction on the maximum execution duration: the execution could never end. Float values are not allowed.</p>
+					<p>This duration is expressed by default in milliseconds, but a different unit can be specified. This unit must suffix the value. Allowed units are the following
+						(<em>case sensitive!!</em>): <code>milliseconds</code> (or <code>ms</code>), <code>seconds</code> (or <code>sec</code> or <code>s</code>),
+						<code>minutes</code> (or <code>min</code> or <code>m</code>), <code>hours</code> (or <code>h</code>), <code>days</code> (or <code>D</code>),
+						<code>weeks</code> (or <code>W</code> ; = 7 days), <code>months</code> (or <code>M</code> ; = 30 days) and <code>years</code> (or <code>Y</code> ; = 365 days).</p>
+					<p><em>Default: <code>0</code> (no restriction)</em></p>
+				</td>
+				<td><ul><li>3600000 <em>(1 hour)</em></li><li>1h</li><li>1 hours</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo"><i>&lt;jlName&gt;.</i>max_running_jobs</td>
+				<td></td>
+				<td>integer</td>
+				<td>
+					<p>Maximum number of jobs that can run in parallel inside the specified job list.</p>
+					<p>
+						If negative or <code>0</code>, no execution queue will be set. Thus
+						the number of running jobs will be unlimited <em>(default behaviour)</em>.
+					</p>
+					<p><em>Default: ø (no execution queue)</em></p>
+				</td>
+				<td><ul><li>-1 <em>(no execution queue)</em></li><li>100</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo"><i>&lt;jlName&gt;.</i>execution_manager</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Class responsible of the execution of all jobs inside the specified job list.</p>
+					<p>
+						Each time a UWS user asks to start a job, the library will ask to the ExecutionManager
+						of the parent job list in order to know if the execution must start now or later. 
+					</p>
+					<p>
+						This property must be a class name (given between {...}). It must reference an implementation of uws.job.manager.ExecutionManager.
+						This implementation must have at least a constructor with one parameter of type uws.service.log.UWSLog.
+					</p>
+					<p>
+						It is recommended to extend an existing implementation such as:
+						uws.job.manager.DefaultExecutionManager, uws.job.manager.AbstractQueuedExecutionManager,
+						uws.job.manager.QueuedExecutionManager.
+					</p>
+					<p><em>By default, uws.job.manager.DefaultExecutionManager is used IF no <code>max_running_jobs</code> is set,
+					otherwise uws.job.manager.QueuedExecutionManager is used.</em></p>
+				</td>
+				<td>{aPackage.MyExecutionManager}</td>
+			</tr>
+			
+			<tr><td colspan="5">Destruction management</td></tr>
+			<tr class="optional">
+				<td class="todo"><em>&lt;jlName&gt;.</em>default_destruction_interval</td>
+				<td></td>
+				<td>integer/text</td>
+				<td>
+					<p>Default interval between the creation and the automatic destruction of the jobs of the specified job list. The prefix "default" means here that the destruction
+						date will be set automatically by the UWS service if the client does not set one.</p>
+					<p>The default destruction interval MUST be less or equals to the maximum execution duration. If this rule is not respected, a given destruction interval will be
+						set immediately to the maximum execution interval.</p>
+					<p>A negative or null value means there is no restriction on the default destruction interval: the job could never be destroyed. Float values are not allowed.</p>
+					<p>This interval is expressed by default in milliseconds, but a different unit can be specified. This unit must suffix the value. Allowed units are the following
+						(<em>case sensitive!!</em>): <code>milliseconds</code> (or <code>ms</code>), <code>seconds</code> (or <code>sec</code> or <code>s</code>),
+						<code>minutes</code> (or <code>min</code> or <code>m</code>), <code>hours</code> (or <code>h</code>), <code>days</code> (or <code>D</code>),
+						<code>weeks</code> (or <code>W</code> ; = 7 days), <code>months</code> (or <code>M</code> ; = 30 days) and <code>years</code> (or <code>Y</code> ; = 365 days).</p>
+					<p><em>Default: <code>0</code> (no restriction)</em></p>
+				</td>
+				<td><ul><li>600000 <em>(10 minutes)</em></li><li>10m</li><li>10 minutes</li><li>10min</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo"><em>&lt;jlName&gt;.</em>max_destruction_interval</td>
+				<td></td>
+				<td>integer/text</td>
+				<td>
+					<p>Maximum interval between the creation and the automatic destruction of the jobs of the specified job list. The prefix "max" means here that the client can not
+						set an interval bigger than this one.</p>
+					<p>The maximum destruction interval MUST be bigger or equals to the default destruction interval. If this rule is not respected, a given destruction interval will be
+						set immediately to the maximum destruction interval.</p>
+					<p>A negative or null value means there is no restriction on the maximum destruction interval: the job could never be destroyed. Float values are not allowed.</p>
+					<p>This interval is expressed by default in milliseconds, but a different unit can be specified. This unit must suffix the value. Allowed units are the following
+						(<em>case sensitive!!</em>): <code>milliseconds</code> (or <code>ms</code>), <code>seconds</code> (or <code>sec</code> or <code>s</code>),
+						<code>minutes</code> (or <code>min</code> or <code>m</code>), <code>hours</code> (or <code>h</code>), <code>days</code> (or <code>D</code>),
+						<code>weeks</code> (or <code>W</code> ; = 7 days), <code>months</code> (or <code>M</code> ; = 30 days) and <code>years</code> (or <code>Y</code> ; = 365 days).</p>
+					<p><em>Default: <code>0</code> (no restriction)</em></p>
+				</td>
+				<td><ul><li>3600000 <em>(1 hour)</em></li><li>1h</li><li>1 hours</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo"><i>&lt;jlName&gt;.</i>destruction_manager</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Class responsible of the destruction of jobs owned by the specified job list.</p>
+					<p>
+						At each job creation, or each time the destruction time of a job is changed, the DestructionManager
+						of its job list is notified, in order to schedule its automatic destruction when the destruction time is reached.
+						<em>This manager does NOT perform the destruction ; it is just responsible to trigger the automatic
+						job destruction.</em> 
+					</p>
+					<p>
+						This property must be a class name (given between {...}). It must reference an implementation of uws.job.manager.DestructionManager.
+						This implementation must have at least an empty constructor.
+					</p>
+					<p>
+						It is recommended to extend an existing implementation such as:
+						uws.job.manager.DefaultDestructionManager.
+					</p>
+					<p><em>By default, uws.job.manager.DefaultDestructionManager is used.</em></p>
+				</td>
+				<td>{aPackage.MyDestructionManager}</td>
+			</tr>
+			
+			<tr><td colspan="5">Files</td></tr>
+			<tr class="mandatory">
+				<td class="todo">file_manager</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Type of the file manager.</p>
+					<p>Accepted values are: local (to manage files on the local system).
+					You can also add another way to manage files by providing the name (within brackets: {...}) of a class implementing UWSFileManager and having at least one constructor with only a java.util.Properties parameter.</p>
+				</td>
+				<td><ul><li>local</li><li>{apackage.MyUWSFileManager}</li></ul></td>
+			</tr>
+			<tr class="mandatory">
+				<td class="todo">file_root_path</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Local file path of the directory in which all UWS files (logs, errors, job results, backup, ...) must be.</p>
+					<p>The file path must be either an absolute local directory path or a directory path relative to WebContent
+					(i.e. the web application directory in which there are WEB-INF and META-INF).</p>
+				</td>	
+				<td><ul><li>/home/my_home_dir/uwsFiles</li><li>uwsFiles</li><li>WEB-INF/uwsFiles</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">directory_per_user</td>
+				<td></td>
+				<td>boolean</td>
+				<td>
+					<p>Tells whether a directory should be created for each user. If yes, the user directory will be named with the user ID. In this directory, there will be error files, job results
+					and it may be the backup file of the user.</p>
+					<p><em>Default: <code>true</code></em></p>
+				</td>
+				<td><ul><li>true <i>(default)</i></li><li>false</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">group_user_directories</td>
+				<td></td>
+				<td>boolean</td>
+				<td>
+					<p>Tells whether user directories must be grouped. If yes, directories are grouped by the first letter found in the user ID.</p>
+					<p><em>Default: <code>false</code></em></p>
+				</td>
+				<td><ul><li>true</li><li>false <i>(default)</i></li></ul></td>
+			</tr>
+			
+			<tr><td colspan="5">Log files</td></tr>
+			<tr class="optional">
+				<td class="todo">min_log_level</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Minimum level that a message must have in order to be logged.</p>
+					<p>5 possible values:</p>
+	 				<ul>
+	 					<li><b>DEBUG</b>: every messages are logged.</li>
+	 					<li><b>INFO</b>: every messages EXCEPT DEBUG are logged.</li>
+	 					<li><b>WARNING</b>: every messages EXCEPT DEBUG and INFO are logged.</li>
+	 					<li><b>ERROR</b>: only ERROR and FATAL messages are logged.</li>
+	 					<li><b>FATAL</b>: only FATAL messages are logged.</li>
+	 				</ul>
+	 				<p><em>Default: <code>DEBUG</code> (every messages are logged)</em></p>
+				</td>
+				<td><ul><li>DEBUG</li><li>INFO</li><li>WANRING</li><li>ERROR</li><li>FATAL</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">log_rotation</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Frequency of the log file rotation. That's to say, logs will be written in a new file after this period. This avoid having too big log files.
+					Old log files are renamed so that highlighting its logging period.</p>
+					<p>The frequency string must respect the following syntax:</p>
+	 				<ul>
+	 					<li><b>'D' hh mm</b>: daily schedule at hh:mm</li>
+	 					<li><b>'W' dd hh mm</b>: weekly schedule at the given day of the week (1:sunday, 2:monday, ..., 7:saturday) at hh:mm</li>
+	 					<li><b>'M' dd hh mm</b>: monthly schedule at the given day of the month at hh:mm</li>
+	 					<li><b>'h' mm</b>: hourly schedule at the given minute</li>
+	 					<li><b>'m'</b>: scheduled every minute (for completness :-))</li>
+	 				</ul>
+	 				<p><em>Where: hh = integer between 0 and 23, mm = integer between 0 and 59, dd (for 'W') = integer between 1 and 7 (1:sunday, 2:monday, ..., 7:saturday),
+	 				dd (for 'M') = integer between 1 and 31.</em></p>
+	 				<p><em><b>Warning:</b>
+	 					The frequency type is case sensitive! Then you should particularly pay attention at the case
+	 					when using the frequency types 'M' (monthly) and 'm' (every minute).
+	 				</em></p>
+	 				<p><em>Default: <code>D 0 0</code> (daily at midnight)</em></p>
+				</td>
+				<td><ul><li>D 6 30</li><li>W 2 6 30</li><li>M 2 6 30</li><li>h 10</li><li>m</li></ul></td>
+			</tr>
+			
+			<tr><td colspan="5">UWS Backup</td></tr>
+			<tr class="optional">
+				<td class="todo">backup_frequency</td>
+				<td></td>
+				<td>text or integer</td>
+				<td>
+					<p>Frequency at which the UWS service (that's to say, all its users and jobs) must be backuped.</p>
+					<p>Allowed values are: <code>never</code> (no backup will never be done), <code>user_action</code> (each time a user does a writing action, like creating or execution a job), a time (must be positive and not null) in milliseconds.</p>
+					<p><em>The value <code>user_action</code> can be used ONLY IF <code>backup_mode=true</code>.</em></p>
+					<p><em>Default: <code>backup_frequency=never</code> (no backup)</em></p>
+				</td>
+				<td><ul><li>never <em>(default)</em></li><li>user_action</li><li>3600000 <em>(1 hour)</em></li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">backup_by_user</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Tells whether the backup must be one file for every user (<code>false</code>), or one file for each user (<code>true</code>). This second option should be chosen if your UWS Service is organizing its files by user directories ; see the property <em>directory_per_user</em>.</p>
+					<p>This option can be enabled ONLY IF a user identification method is provided ; see property <code>user_identifier</code>.</p>
+					<p><em>Default: <code>false</code></em></p>
+				</td>
+				<td><ul><li>false <em>(default)</em></li><li>true</li></ul></td>
+			</tr>
+			
+			<tr><td colspan="5">User identification</td></tr>
+			<tr class="optional">
+				<td class="todo">user_identifier</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Class to use in order to identify a user of the UWS service. The same instance of this class will be used for every request sent to the service.</p>
+					<p>
+						The value of this property MUST be a class name (with brackets: {...}) of a class implementing the interface uws.service.UserIdentifier.
+						This class MUST have one of its constructors with no parameter.
+					</p>
+					<p><em>By default, no identification is performed ; all users are then anonymous and their jobs can be seen by everybody.</em></p>
+				</td>
+				<td>{apackage.FooUserIdentifier}</td>
+			</tr>
+			
+			<tr><td colspan="5">Job serialization</td></tr>
+			<tr class="optional">
+				<td class="todo">additional_serializers</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Comma-separated list of additional UWS serializers.</p>
+					<p>
+						A UWS serializer lets convert a job description into a specific format. A given serializer is used
+						when the HTTP content-type of a request is set to the MIME-Type of this serializer. The MIME-Type
+						associated with a serializer is provided by its function getMimeType(). 
+					</p>
+					<p>
+						By default, the UWS library supports the XML <em>(content-type: <code>text/xml</code>)</em>
+						and JSON <em>(content-type: <code>application/json</code>)</em> serializations.
+						If the given MIME-Type is unknown or is not provided, the default behavior is to describe the
+						jobs into the XML format.
+					</p>
+					<p><em>By default, this list is empty ; only the default serializers (XML and JSON) are available.</em></p>
+				</td>
+				<td><ul><li>{aPackage.MySuperSerializer}</li><li>{aPackage.MySerializer1}, {aPackage.MySerializer2}</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">xslt_stylesheet</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>URL of the XSLT stylesheet to link with all XML serializations of jobs (and its parameters).</p>
+					<p><em>By default, no XSLT stylesheet is defined.</em></p>
+				</td>
+				<td><ul><li>http://myuws.com/mySuperXSLT.xslt</li></ul></td>
+			</tr>
+			<tr class="optional">
+				<td class="todo">error_writer</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Class responsible of writing the error messages when an HTTP request fails.</p>
+					<p><em>By default, all errors are returned in HTTP format using the class uws.service.error.DefaultUWSErrorWriter.</em></p>
+				</td>
+				<td>{aPackage.MySuperErrorWriter}</td>
+			</tr>
+			
+			<tr><td colspan="5">Additional UWS Actions</td></tr>
+			<tr class="optional">
+				<td class="todo">additional_actions</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Comma-separated list of additional UWS actions.</p>
+					<p>
+						By default, the following standard UWS actions are already existing (with exactly this name):
+						<code>Show UWS Home Page</code> (0), <code>List Jobs</code> (1), <code>Add Job</code> (2),
+						<code>Set UWS Parameter</code> (3), <code>Destroy Job</code> (4), <code>Get Job</code> (5),
+						<code>Get Job Parameter</code> (6) and <code>Set Job Parameter</code> (7).
+						With this property, you can add or replace a custom actions to your UWS service.
+					</p>
+					<p>To merely <em>add a new action</em>, the syntax is:</p>
+					<p style="text-align:center"><code>index:</code><code>className</code></p>
+					<p>
+						The <code>index</code> with the following <code>:</code> are optional. If an index is specified, the action will be inserted at this position
+						in the list of all actions (standard and custom) of the UWS service ; above the index of all UWS standard actions is specified.
+						If no index is specified, the <code>:</code> separator MUST be omitted, and the new action will be added at the end of the UWS actions list.
+					</p>
+					<p>
+						To <em>replace an existing action</em>, no index should be specified (if there is, it will be ignored) and the implementation of
+						the function UWSAction.getName() MUST return the exact (case sensitivity is enabled) name of the action to replace.
+					</p>
+					<p>
+						 <b>Important note:</b>
+						 An action name is used only to identify internally a UWS action. To effectively trigger the execution of an action you must carefully
+						 implement the function UWSAction.match(...). The whole list of actions (standard and custom) is evaluated in the creation order.
+						 So if a standard action matches a request, the additional actions will never be evaluated ; the evaluation
+						 of the standard actions must all fail before getting a chance to evaluate any of the custom actions. That's why you have the possibility
+						 to insert an action wherever you want in the list, and to replace existing actions.
+					</p>
+					<p>
+						Each item of the list MUST be the name of a class implementing uws.service.actions.UWSAction. This class MUST have at least one constructor
+						with exactly one parameter of type uws.service.UWSService.
+					</p>
+					<p><em>By default, this list is empty ; only the standard UWS actions exist.</em></p>
+				</td>
+				<td><ul><li>{aPackage.MySuperAction}</li><li>2:{aPackage.MyOtherSuperAction}</li><li>0:{aPackage.FirstAction}, {aPackage.LastAction}</li></ul></td>
+			</tr>
+			
+			<tr><td colspan="5">HTTP request parser</td></tr>
+			<tr class="optional">
+				<td class="todo">request_parser</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>
+						Class to use in replacement of the default UWSRequestParser. A request parser aims to interpret
+						all incoming HTTP requests. You may need to customize it if, for instance, you want to handle a
+						special way to provide parameters <i>(note that 'multipart/form-data' is already supported by the
+						default implementation of the UWS library)</i>.
+					</p>
+					<p>
+						This property must be a class name (given between {...}). It must reference an implementation of
+						uws.service.request.RequestParser. This implementation must have at least a constructor with a single
+						parameter of type uws.service.file.UWSFileManager.
+					</p>
+					<p>
+						It is recommended to extend an existing implementation such as:
+						uws.service.request.UWSRequestParser.
+					</p>
+					<p><em>By default, uws.service.request.UWSRequest is used</em></p>
+				</td>
+				<td>{aPackage.MyRequestParser}</td>
+			</tr>
+			
+			<tr><td colspan="5">Custom UWS Factory</td></tr>
+			<tr class="optional">
+				<td class="todo">uws_factory</td>
+				<td></td>
+				<td>text</td>
+				<td>
+					<p>Class to use in replacement of the default UWSFactory.</p>
+					<p>
+						This property must be a class name (given between {...}). It must reference an implementation of UWSFactory.
+						This implementation must have at least an empty constructor.
+					</p>
+					<p>
+						It is recommended to extend an existing implementation such as:
+						uws.service.AbstractUWSFactory.
+					</p>
+					<p><b>Warning!</b> By default, the default UWSFactory (uws.config.ConfigurableUWSFactory) is used and support all
+					the following properties:</p>
+					<ul>
+						<li>request_parser,</li>
+						<li><em>&lt;joblist&gt;.</em>job_thread,</li>
+						<li><em>&lt;joblist&gt;.</em>job_parameters,</li>
+						<li><em>&lt;joblist&gt;.</em>default_execution_duration,</li>
+						<li><em>&lt;joblist&gt;.</em>max_execution_duration,</li>
+						<li><em>&lt;joblist&gt;.</em>default_destruction_interval,</li>
+						<li><em>&lt;joblist&gt;.</em>max_destruction_interval.</li>
+					</ul>
+					<p>Replacing the default UWSFactory by your own implementation implies that you will have
+					to deal with all these properties by yourself.</p>
+				</td>
+				<td>{aPackage.MyUWSFactory}</td>
+			</tr>
+			
+		</table>
+		<script type="text/javascript">
+			var nb = document.getElementsByClassName("mandatory").length;
+			document.getElementById("nbMandatory").textContent = nb;
+			nb += document.getElementsByClassName("optional").length;
+			document.getElementById("nbTotal").textContent = nb;
+		</script>
+	</body>
+</html>
\ No newline at end of file
diff --git a/src/uws/config/uws_full.properties b/src/uws/config/uws_full.properties
new file mode 100644
index 0000000000000000000000000000000000000000..c4f2c76cdf98fca62c2d260bf5011893366493d0
--- /dev/null
+++ b/src/uws/config/uws_full.properties
@@ -0,0 +1,552 @@
+##########################################################
+#             FULL UWS CONFIGURATION FILE                #
+#                                                        #
+# UWS Version: 4.2                                       #
+# Date: 13 June 2016                                     #
+# Author: Gregory Mantelet (ARI)                         #
+#                                                        #
+########################################################## 
+
+###########
+# GENERAL #
+###########
+
+# [OPTIONAL]
+# This property lets set a custom home page.
+# 
+# 4 different kinds of value are accepted:
+#     * nothing (default): the default home page provided by the library (a XML
+#                          document listing all available job lists).
+#     * name or relative path of a file: this method MUST be chosen if the new
+#                                        home page is a JSP file. This file
+#                                        MUST be inside the directory
+#                                        WebContent of your web application.
+#     * a URI starting with file://: in this method the local file pointed by
+#                                    the URI will be merely returned when the
+#                                    home page will be requested.
+#     * a URL: here, a redirection toward this URL will be made at each request
+#              on the home page
+#     * a class name: the class name of an extension of
+#                     uws.service.actions.ShowHomePage which must replace the
+#                     default home page action. This class MUST have at least
+#                     one constructor with exactly one parameter not NULL of
+#                     type uws.service.UWSService.
+home_page = 
+
+# [OPTIONAL]
+# MIME type of the service home page.
+# 
+# This property is used only if the specified "home_page" is a local file path
+# (i.e. if "home_page=file://...").
+# 
+# If no value is provided "text/html" will be set by default.
+# 
+# Default: text/html
+home_page_mime_type = 
+
+###########
+# SERVICE #
+###########
+
+# [OPTIONAL]
+# Name of the UWS Service.
+service_name = uws
+
+# [OPTIONAL]
+# Description of the UWS Service.
+service_description = My UWS Service is so amazing! You should use it with your favorite UWS client.
+
+############
+# JOB LIST #
+############
+
+# [MANDATORY]
+# Comma separated list of job list.
+# 
+# At least one name MUST be provided. Otherwise, the UWS service will have no
+# job list and the job execution will not be possible.
+joblists = jobs
+
+#######
+# JOB #
+#######
+
+# [MANDATORY]
+# Class describing the execution of all jobs managed by the job list specified
+# as prefixed of the property name.
+# 
+# This property must be a class name (given between {...}). It must reference
+# an extension of uws.job.JobThread. This implementation must have at least a
+# constructor with one parameter of type uws.job.UWSJob.
+jobs.job_thread = 
+
+# [OPTIONAL]
+# List of all expected parameters.
+# 
+# The value of this property is expected to be a comma separated list of
+# parameters. At least a name without any space character is expected. Limits
+# about each parameter MAY be provided. Below are described all types of
+# parameter definition that are supported in this property file:
+# 
+#     * Just a name:
+#                                paramName
+#     * String parameter:
+#         [ paramName , modif? , string , "default" , / regular_expr / i ]
+#     * Numeric parameter:
+#          [ paramName , modif? , numeric , default , minimum , maximum ]
+#     * Duration parameter:
+#          [ paramName , modif? , numeric , default , minimum , maximum ]
+#     * Custom:
+#                   [ paramName , { aPackage.ClassName } ]
+# 
+# It is not mandatory to declare all expected parameters in this list. Even non
+# listed parameters will appear in the job description. However any parameter
+# name provided in this list will be recognized case insensitively among the
+# parameters of a job. For instance, if the list contains the parameter foo and
+# the user has provided the parameters foo=bar&FOO=stuff when creating a job,
+# the library will consider them as the same. It will take the last given value
+# and will report this parameter in its job description as foo=stuff.
+# 
+# All attributes except the name and the type (or the class name) are optional.
+# They can be omitted which implies they will be set to their default value ;
+# generally null. Here are more details about each attribute:
+# 
+# modif? is a boolean flag aiming to indicate whether the value of the parameter
+# can be modified after initialization of the job. Any value (case insensitive)
+# among the following list is interpreted as true ; any other value as false:
+# true, t, yes and y. If this flag is omitted it will be by default set to true
+# (i.e. the parameter can be modified after initialization).
+# 
+# The regular expression must be enclosed by /. It can contain any character
+# except /. If a such character has to be provided anyway, it can be done by
+# doubling it. For instance: /foo//bar/i. The letter i after the regular
+# expression is optional. It indicates that the regular expression must be
+# evaluated case insensitively.
+# 
+# The default, minimum and maximum values of numeric and duration parameters
+# must be valid numeric values. For a numeric, any kind of numerical value is
+# accepted. For a duration, only positive or zero long/integer values are
+# accepted. If one of these values is omitted it will be considered as null
+# (i.e. no limitation in the case of the minimum and maximum).
+# 
+# A duration value (default, minimum, maximum and even a user value provided
+# when submitting/updating a job) must be a positive or zero long/integer value
+# eventually followed by a unit. Without a unit, by default the duration will be
+# considered as being expressed in milliseconds. The supported unit suffixes are
+# the following (case sensitive): milliseconds (or ms), seconds (or sec or s),
+# minutes (or min or m), hours (or h), days (or D), weeks (or W ; = 7 days),
+# months (or M ; = 30 days) and years (or Y ; = 365 days).
+jobs.job_parameters = 
+
+########################
+# EXECUTION MANAGEMENT #
+########################
+
+# [OPTIONAL]
+# Default execution duration of the jobs of the specified job list. The prefix
+# "default" means here that the execution duration will be this one if the
+# client does not set one.
+# 
+# The default duration MUST be less or equals to the maximum execution duration.
+# If this rule is not respected, the default execution duration is set
+# immediately to the maximum execution duration.
+# 
+# A negative or null value means there is no restriction on the default
+# execution duration: the execution could never end. Float values are not
+# allowed.
+# 
+# This duration is expressed by default in milliseconds, but a different unit
+# can be specified. This unit must suffix the value. Allowed units are the
+# following (case sensitive!!): milliseconds (or ms), seconds (or sec or s),
+# minutes (or min or m), hours (or h), days (or D), weeks (or W ; = 7 days),
+# months (or M ; = 30 days) and years (or Y ; = 365 days).
+# 
+# Default: 0 (no restriction)
+jobs.default_execution_duration = 0
+
+# [OPTIONAL]
+# Maximum execution duration of the jobs of the specified job list. The prefix
+# "max" means here that the client can not set a time greater than this one.
+# 
+# The maximum duration MUST be greater or equals to the default execution
+# duration. If this rule is not respected, the default execution duration is set
+# immediately to the maximum execution duration.
+# 
+# A negative or null value means there is no restriction on the maximum
+# execution duration: the execution could never end. Float values are not
+# allowed.
+# 
+# This duration is expressed by default in milliseconds, but a different unit
+# can be specified. This unit must suffix the value. Allowed units are the
+# following (case sensitive!!): milliseconds (or ms), seconds (or sec or s),
+# minutes (or min or m), hours (or h), days (or D), weeks (or W ; = 7 days),
+# months (or M ; = 30 days) and years (or Y ; = 365 days).
+# 
+# Default: 0 (no restriction)
+jobs.max_execution_duration = 0
+
+# [OPTIONAL]
+# Maximum number of jobs that can run in parallel inside the specified job list.
+# 
+# If negative or 0, no execution queue will be set. Thus the number of running
+# jobs will be unlimited (default behaviour).
+# 
+# Default: ø (no execution queue)
+jobs.max_running_jobs = 0
+
+# [OPTIONAL]
+# Class responsible of the execution of all jobs inside the specified job list.
+# 
+# Each time a UWS user asks to start a job, the library will ask to the
+# ExecutionManager of the parent job list in order to know if the execution must
+# start now or later.
+# 
+# This property must be a class name (given between {...}). It must reference an
+# implementation of uws.job.manager.ExecutionManager. This implementation must
+# have at least a constructor with one parameter of type uws.service.log.UWSLog.
+# 
+# It is recommended to extend an existing implementation such as: 
+#     uws.job.manager.DefaultExecutionManager,
+#     uws.job.manager.AbstractQueuedExecutionManager,
+#     uws.job.manager.QueuedExecutionManager.
+# 
+# By default, uws.job.manager.DefaultExecutionManager is used IF no
+# max_running_jobs is set, otherwise uws.job.manager.QueuedExecutionManager is
+# used.
+jobs.execution_manager = 
+
+##########################
+# DESTRUCTION MANAGEMENT #
+##########################
+
+# [OPTIONAL]
+# Default interval between the creation and the automatic destruction of the
+# jobs of the specified job list. The prefix "default" means here that the
+# destruction date will be set automatically by the UWS service if the client
+# does not set one.
+# 
+# The default destruction interval MUST be less or equals to the maximum
+# execution duration. If this rule is not respected, a given destruction
+# interval will be set immediately to the maximum execution interval.
+# 
+# A negative or null value means there is no restriction on the default
+# destruction interval: the job could never be destroyed. Float values are not
+# allowed.
+# 
+# This interval is expressed by default in milliseconds, but a different unit
+# can be specified. This unit must suffix the value. Allowed units are the
+# following (case sensitive!!): milliseconds (or ms), seconds (or sec or s),
+# minutes (or min or m), hours (or h), days (or D), weeks (or W ; = 7 days),
+# months (or M ; = 30 days) and years (or Y ; = 365 days).
+# 
+# Default: 0 (no restriction)
+jobs.default_destruction_interval = 0
+
+# [OPTIONAL]
+# Maximum interval between the creation and the automatic destruction of the
+# jobs of the specified job list. The prefix "max" means here that the client
+# can not set an interval bigger than this one.
+# 
+# The maximum destruction interval MUST be bigger or equals to the default
+# destruction interval. If this rule is not respected, a given destruction
+# interval will be set immediately to the maximum destruction interval.
+# 
+# A negative or null value means there is no restriction on the maximum
+# destruction interval: the job could never be destroyed. Float values are not
+# allowed.
+# 
+# This interval is expressed by default in milliseconds, but a different unit
+# can be specified. This unit must suffix the value. Allowed units are the
+# following (case sensitive!!): milliseconds (or ms), seconds (or sec or s),
+# minutes (or min or m), hours (or h), days (or D), weeks (or W ; = 7 days),
+# months (or M ; = 30 days) and years (or Y ; = 365 days).
+# 
+# Default: 0 (no restriction)
+jobs.max_destruction_interval = 0
+
+# [OPTIONAL]
+# Class responsible of the destruction of jobs owned by the specified job list.
+# 
+# At each job creation, or each time the destruction time of a job is changed,
+# the DestructionManager of its job list is notified, in order to schedule its
+# automatic destruction when the destruction time is reached. This manager does
+# NOT perform the destruction ; it is just responsible to trigger the automatic
+# job destruction.
+# 
+# This property must be a class name (given between {...}). It must reference an
+# implementation of uws.job.manager.DestructionManager. This implementation must
+# have at least an empty constructor.
+# 
+# It is recommended to extend an existing implementation such as: 
+#     uws.job.manager.DefaultDestructionManager.
+# 
+# By default, uws.job.manager.DefaultDestructionManager is used.
+jobs.destruction_manager = 
+
+#########
+# FILES #
+#########
+
+# [MANDATORY]
+# Type of the file manager.
+# 
+# Accepted values are: local (to manage files on the local system). You can
+# also add another way to manage files by providing the name (within brackets:
+# {...}) of a class implementing UWSFileManager and having at least one
+# constructor with only a java.util.Properties parameter.
+# 
+# Allowed values: local, a class name.
+file_manager = local
+
+# [MANDATORY]
+# Local file path of the directory in which all UWS files (logs, errors, job
+# results, backup, ...) must be. The file path must be either an absolute local
+# directory path or a directory path relative to WebContent (i.e. the web
+# application directory in which there are WEB-INF and META-INF).
+file_root_path = 
+
+# [OPTIONAL]
+# Tells whether a directory should be created for each user.
+# 
+# If yes, the user directory will be named with the user ID. In this directory,
+# there will be error files, job results and it may be the backup file of the
+# user.
+# 
+# Allowed values: true (default), false.
+directory_per_user = true
+
+# [OPTIONAL]
+# Tells whether user directories must be grouped.
+# 
+# If yes, directories are grouped by the first letter found in the user ID.
+# 
+# Allowed values: true, false (default).
+group_user_dir = false
+
+#############
+# LOG FILES #
+#############
+
+# [OPTIONAL]
+# Minimum level that a message must have in order to be logged.
+#
+# 5 possible values:
+#     * DEBUG: every messages are logged.
+#     * INFO: every messages EXCEPT DEBUG are logged.
+#     * WARNING: every messages EXCEPT DEBUG and INFO are logged.
+#     * ERROR: only ERROR and FATAL messages are logged.
+#     * FATAL: only FATAL messages are logged.
+#
+# Default: DEBUG (every messages are logged)
+min_log_level = 
+
+# [OPTIONAL]
+# Frequency of the log file rotation. That's to say, logs will be written in a
+# new file after this period. This avoid having too big log files.
+# Old log files are renamed so that highlighting its logging period.
+# 
+# The frequency string must respect the following syntax:
+#     'D' hh mm: daily schedule at hh:mm
+#     'W' dd hh mm: weekly schedule at the given day of the week (1:sunday,
+#                   2:monday, ..., 7:saturday) at hh:mm
+#     'M' dd hh mm: monthly schedule at the given day of the month at hh:mm
+#     'h' mm: hourly schedule at the given minute
+#     'm': scheduled every minute (for completness :-))
+# Where: hh = integer between 0 and 23, mm = integer between 0 and 59, dd (for
+#        'W') = integer between 1 and 7 (1:sunday, 2:monday, ..., 7:saturday),
+#        dd (for 'M') = integer between 1 and 31.
+# 
+# Warning: The frequency type is case sensitive! Then you should particularly
+#          pay attention at the case when using the frequency types 'M'
+#          (monthly) and 'm' (every minute).
+# 
+# Note: this property is ignored if the file manager is not any more an
+#       extension of uws.service.file.LocalUWSFileManager.
+# 
+# Default: D 0 0 (daily at midnight)
+log_rotation = 
+
+##############
+# UWS_BACKUP #
+##############
+
+# [OPTIONAL]
+# Frequency at which the UWS service (that's to say, all its users and jobs)
+# must be backuped.
+# 
+# Allowed values: never (no backup will never be done ; default), user_action
+#                 (each time a user does a writing action, like creating or
+#                 execution a job), a time (must be positive and not null) in
+#                 milliseconds.
+# 
+# The value user_action can be used ONLY IF backup_mode=true.
+# 
+# Default: never
+backup_frequency = never
+
+# [OPTIONAL]
+# Tells whether the backup must be one file for every user (false), or one file
+# for each user (true). This second option should be chosen if your UWS Service
+# is organizing its files by user directories ; see the property
+# directory_per_user.
+# 
+# This option can be enabled ONLY IF a user identification method is provided ;
+# see property user_identifier.
+# 
+# Default: false
+backup_by_user = false
+
+#######################
+# USER IDENTIFICATION #
+#######################
+
+# [OPTIONAL]
+# Class to use in order to identify a user of the UWS service.
+# 
+# The same instance of this class will be used for every request sent to the
+# service.
+# 
+# The value of this property MUST be a class name (with brackets: {...}) of a
+# class implementing the interface uws.service.UserIdentifier. This class MUST
+# have one of its constructors with no parameter.
+# 
+# Default: no identification is performed => all users are then anonymous and
+#          their jobs can be seen by everybody.
+user_identifier = 
+
+##############################
+# ADDITIONAL UWS SERIALIZERS #
+##############################
+
+# [OPTIONAL]
+# Comma-separated list of additional UWS serializers.
+# 
+# A UWS serializer lets convert a job description into a specific format. A
+# given serializer is used when the HTTP content-type of a request is set to the
+# MIME-Type of this serializer. The MIME-Type associated with a serializer is
+# provided by its function getMimeType().
+# 
+# By default, the UWS library supports the XML (content-type: text/xml) and JSON
+# (content-type: application/json) serializations. If the given MIME-Type is
+# unknown or is not provided, the default behavior is to describe the jobs into
+# the XML format.
+# 
+# By default, this list is empty ; only the default serializers (XML and JSON)
+# are available.
+additional_serializers = 
+
+# [OPTIONAL]
+# URL of the XSLT stylesheet to link with all XML serializations of jobs (and
+# its parameters).
+# 
+# By default, no XSLT stylesheet is defined.
+xslt_stylesheet = 
+
+# [OPTIONAL]
+# Class responsible of writing the error messages when an HTTP request fails.
+# 
+# By default, all errors are returned in HTTP format using the class uws.service.error.DefaultUWSErrorWriter.
+error_writer = 
+
+##########################
+# ADDITIONAL UWS ACTIONS #
+##########################
+
+# [OPTIONAL]
+# Comma-separated list of additional UWS actions.
+# 
+# By default, the following standard UWS actions are already existing (with
+# exactly this name):
+#   Show UWS Home Page (0),
+#   List Jobs (1),
+#   Add Job (2),
+#   Set UWS Parameter (3),
+#   Destroy Job (4),
+#   Get Job (5),
+#   Get Job Parameter (6)
+#   and Set Job Parameter (7).
+# With this property, you can add or replace a custom actions to your UWS
+# service.
+# 
+# To merely add a new action, the syntax is:
+# 
+#                             index:className
+# 
+# The index with the following : are optional. If an index is specified, the
+# action will be inserted at this position in the list of all actions (standard
+# and custom) of the UWS service ; above the index of all UWS standard actions
+# is specified. If no index is specified, the : separator MUST be omitted, and
+# the new action will be added at the end of the UWS actions list.
+# 
+# To replace an existing action, no index should be specified (if there is, it
+# will be ignored) and the implementation of the function UWSAction.getName()
+# MUST return the exact (case sensitivity is enabled) name of the action to
+# replace.
+# 
+# Important note:
+# An action name is used only to identify internally a UWS action. To
+# effectively trigger the execution of an action you must carefully implement
+# the function UWSAction.match(...). The whole list of actions (standard and
+# custom) is evaluated in the creation order. So if a standard action matches a
+# request, the additional actions will never be evaluated ; the evaluation of
+# the standard actions must all fail before getting a chance to evaluate any of
+# the custom actions. That's why you have the possibility to insert an action
+# wherever you want in the list, and to replace existing actions.
+# 
+# Each item of the list MUST be the name of a class implementing
+# uws.service.actions.UWSAction. This class MUST have at least one constructor
+# with exactly one parameter of type uws.service.UWSService.
+# 
+# By default, this list is empty ; only the standard UWS actions exist.
+additional_actions = 
+
+##################
+# REQUEST_PARSER #
+##################
+
+# [OPTIONAL]
+# Class to use in replacement of the default UWSRequestParser.
+# 
+# A request parser aims to interpret all incoming HTTP requests.
+# You may need to customize it if, for instance, you want to handle a special
+# way to provide parameters (note that 'multipart/form-data' is already
+# supported by the default implementation of the UWS library).
+# 
+# This property must be a class name (given between {...}). It must reference
+# an implementation of uws.service.request.RequestParser. This implementation
+# must have at least a constructor with a single parameter of type
+# uws.service.file.UWSFileManager.
+# 
+# It is recommended to extend an existing implementation such as:
+# uws.service.request.UWSRequestParser.
+# 
+# By default, uws.service.request.UWSRequest is used
+request_parser = 
+
+######################
+# CUSTOM UWS_FACTORY #
+######################
+
+# [OPTIONAL]
+# Class to use in replacement of the default UWSFactory.
+# 
+# This property must be a class name (given between {...}). It must reference
+# an implementation of UWSFactory. This implementation must have at least an
+# empty constructor.
+# 
+# It is recommended to extend an existing implementation such as:
+# uws.service.AbstractUWSFactory.
+# 
+# Warning! By default, the default UWSFactory
+#          (uws.config.ConfigurableUWSFactory) is used and support all the
+#          following properties:
+#     * request_parser,
+#     * <joblist>.job_thread,
+#     * <joblist>.job_parameters,
+#     * <joblist>.default_execution_duration,
+#     * <joblist>.max_execution_duration,
+#     * <joblist>.default_destruction_interval,
+#     * <joblist>.max_destruction_interval
+# Replacing the default UWSFactory by your own implementation implies that you
+# will have to deal with all these properties by yourself.
+uws_factory = 
diff --git a/src/uws/config/uws_min.properties b/src/uws/config/uws_min.properties
new file mode 100644
index 0000000000000000000000000000000000000000..81658b50c17a24cd88bb3a5d7e543b8c0ae2717b
--- /dev/null
+++ b/src/uws/config/uws_min.properties
@@ -0,0 +1,52 @@
+##########################################################
+#            MINIMUM UWS CONFIGURATION FILE              #
+#                                                        #
+# UWS Version: 4.2                                       #
+# Date: 13 June 2016                                     #
+# Author: Gregory Mantelet (ARI)                         #
+#                                                        #
+##########################################################
+
+############
+# JOB LIST #
+############
+
+# [MANDATORY]
+# Comma separated list of job list.
+# 
+# At least one name MUST be provided. Otherwise, the UWS service will have no
+# job list and the job execution will not be possible.
+joblists = jobs
+
+#######
+# JOB #
+#######
+
+# [MANDATORY]
+# Class describing the execution of all jobs managed by the job list specified
+# as prefixed of the property name.
+# 
+# This property must be a class name (given between {...}). It must reference
+# an extension of uws.job.JobThread. This implementation must have at least a
+# constructor with one parameter of type uws.job.UWSJob.
+jobs.job_thread = 
+
+#########
+# FILES #
+#########
+
+# Type of the file manager.
+#
+# Accepted values are: local (to manage files on the local system). You can
+# also add another way to manage files by providing the name (within brackets:
+# {...}) of a class implementing UWSFileManager and having at least one
+# constructor with only a java.util.Properties parameter.
+#
+# Allowed values: local, a class name.
+file_manager = local
+
+# Local file path of the directory in which all UWS files (logs, errors, job
+# results, backup, ...) must be. The file path must be either an absolute local
+# directory path or a directory path relative to WebContent (i.e. the web
+# application directory in which there are WEB-INF and META-INF).
+file_root_path = 
diff --git a/src/uws/job/parameters/DurationParamController.java b/src/uws/job/parameters/DurationParamController.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae33a2d5fd981b59f9be3d8cb449488f0848c6e1
--- /dev/null
+++ b/src/uws/job/parameters/DurationParamController.java
@@ -0,0 +1,354 @@
+package uws.job.parameters;
+
+/*
+ * 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 2016 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.text.ParseException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import uws.UWSException;
+
+/**
+ * <p>
+ * 	Let controlling a duration parameter. Thus it is possible to set a default but also a minimum and a maximum value.
+ * 	Moreover you can indicate whether the value of the parameter can be modified by the user or not after initialization.
+ * </p>
+ * 
+ * <p>This controller behaves like a {@link NumericParamController} EXCEPT on two points:</p>
+ * <ul>
+ * 	<li>Every given value is casted into a long value.
+ * 		This implies that any {@link Double} or {@link Float} values will be truncated/rounded.</li>
+ * 	<li>It is possible to check a {@link String} expressing the duration in a different unit.
+ * 		This string must be prefixed by a unit. See {@link #parseDuration(String)} (and its reverse operation {@link #toString(Long)})
+ * 		for more details.</li>
+ * </ul>
+ * 
+ * @author Gr&eacute;gory Mantelet (ARI)
+ * @version 4.2 (06/2016)
+ * @since 4.2
+ */
+public class DurationParamController extends NumericParamController {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Create a parameter controller for duration value with no restriction.
+	 * 
+	 * <p>
+	 * 	A default, minimum and/or maximum value can be set after creation using {@link #setDefault(Number)},
+	 * 	{@link #setMinimum(Number)} and {@link #setMaximum(Number)}. By default this parameter can always be modified,
+	 * 	but it can be forbidden using {@link #allowModification(boolean)}.
+	 * </p>
+	 */
+	public DurationParamController(){
+		super();
+	}
+
+	/**
+	 * <p>Create a controller for a parameter expressing a duration.
+	 * The default and the maximum value are initialized with the given parameters (expressed in milliseconds).
+	 * The third parameter allows also to forbid the modification of the parameter value by the user,
+	 * if set to <i>false</i>.</p>
+	 * 
+	 * <p>
+	 * 	A default and/or maximum value can be modified after creation using {@link #setDefault(Number)}
+	 * 	and {@link #setMaximum(Number)}. The flag telling whether this parameter can be modified by the user
+	 * 	can be changed using {@link #allowModification(boolean)}.
+	 * </p>
+	 * 
+	 * <p><b>Important note:</b>
+	 * 	Values given in this constructor MUST be expressed in milliseconds.
+	 * </p>
+	 * 
+	 * @param defaultValue		Value (in ms) set by default to the parameter, when none is specified.
+	 * @param minValue			Minimum value (in ms) that can be set. If a smaller value is provided by the user, an exception will be thrown by {@link #check(Object)}.
+	 * @param maxValue			Maximum value (in ms) that can be set. If a bigger value is provided by the user, an exception will be thrown by {@link #check(Object)}.
+	 * @param allowModification	<i>true</i> to allow the user to modify this value when creating a job, <i>false</i> otherwise.
+	 */
+	public DurationParamController(final Long defaultValue, final Long minValue, final Long maxValue, final boolean allowModification){
+		super(defaultValue, minValue, maxValue, allowModification);
+	}
+
+	/**
+	 * Cast the given value as a long value and call {@link NumericParamController#setDefault(Number)}.
+	 * 
+	 * @see uws.job.parameters.NumericParamController#setMinimum(java.lang.Number)
+	 */
+	@Override
+	public void setDefault(final Number newDefaultValue){
+		super.setDefault((newDefaultValue == null) ? null : newDefaultValue.longValue());
+	}
+
+	/**
+	 * Cast the given value as a long value and call {@link NumericParamController#setMinimum(Number)}.
+	 * 
+	 * @see uws.job.parameters.NumericParamController#setMinimum(java.lang.Number)
+	 */
+	@Override
+	public void setMinimum(final Number newMinValue){
+		super.setMinimum((newMinValue == null) ? null : newMinValue.longValue());
+	}
+
+	/**
+	 * Cast the given value as a long value and call {@link NumericParamController#setMaximum(Number)}.
+	 * 
+	 * @see uws.job.parameters.NumericParamController#setMinimum(java.lang.Number)
+	 */
+	@Override
+	public void setMaximum(final Number newMaxValue){
+		super.setMaximum((newMaxValue == null) ? null : newMaxValue.longValue());
+	}
+
+	@Override
+	public Object check(final Object value) throws UWSException{
+		// If no value, return the default one:
+		if (value == null)
+			return getDefault();
+
+		// Otherwise, parse the given numeric value:
+		long numVal;
+		if (value instanceof Number)
+			numVal = ((Number)value).longValue();
+		else if (value instanceof String){
+			try{
+				numVal = parseDuration((String)value);
+			}catch(ParseException pe){
+				throw new UWSException(UWSException.BAD_REQUEST, "Wrong format for a duration parameter: \"" + value + "\"! It should be a positive long value between " + (minValue == null ? 0 : toString((Long)minValue)) + " and " + (maxValue == null ? toString(Long.MAX_VALUE) : toString((Long)maxValue)) + " (Default value: " + (defaultValue == null ? "none" : toString((Long)defaultValue)) + "). This value may be followed by a unit among: milliseconds (ms ; the default), seconds (s), minutes (min,m), hours (h), days (D), weeks (W), months (M) or years (Y).");
+			}
+		}else
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Wrong type for a duration parameter: class \"" + value.getClass().getName() + "\"! It should be a positive long value or a string containing only a positive long value eventually followed by a unit.");
+
+		// If the value is SMALLER than the minimum, the minimum value will be returned:
+		if (minValue != null && numVal < minValue.doubleValue())
+			return minValue;
+		// If the value is BIGGER than the maximum, the maximum value will be returned:
+		else if (maxValue != null && numVal > maxValue.doubleValue())
+			return maxValue;
+		// Otherwise, return the parsed number:
+		else
+			return numVal;
+	}
+
+	/* **************** */
+	/* DURATION PARSING */
+	/* **************** */
+
+	/** Multiplication factor between milliseconds and seconds.
+	 * <p>A second is here defined as 1000 milliseconds. So the value is computed as follows: 1000.</p> */
+	protected final static long MULT_SEC = 1000;
+
+	/** Multiplication factor between milliseconds and minutes.
+	 * <p>A minute is here defined as 60 seconds. So the value is computed as follows: {@link #MULT_SEC}*60.</p> */
+	protected final static long MULT_MIN = 60000;
+
+	/** Multiplication factor between milliseconds and hours.
+	 * <p>An hour is here defined as 60 minutes. So the value is computed as follows: {@link #MULT_MINUTES}*60.</p> */
+	protected final static long MULT_HOURS = 3600000;
+
+	/** Multiplication factor between milliseconds and days.
+	 * <p>A day is here defined as 24 hours. So the value is computed as follows: {@link #MULT_HOURS}*24.</p> */
+	protected final static long MULT_DAYS = 86400000;
+
+	/** Multiplication factor between milliseconds and weeks.
+	 * <p>A week is here defined as 7 days. So the value is computed as follows: {@link #MULT_DAYS}*7.</p> */
+	protected final static long MULT_WEEKS = 604800000;
+
+	/** Multiplication factor between milliseconds and months.
+	 * <p>A month is here defined as 30 days. So the value is computed as follows: {@link #MULT_DAYS}*30.</p> */
+	protected final static long MULT_MONTHS = 2592000000l;
+
+	/** Multiplication factor between milliseconds and years.
+	 * <p>A year is here defined as 365 days. So the value is computed as follows: {@link #MULT_DAYS}*365.</p> */
+	protected final static long MULT_YEARS = 31536000000l;
+
+	/** Regular Expression of all string allowed to mean MILLISECONDS.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_MS = "milliseconds|ms";
+
+	/** Regular Expression of all string allowed to mean SECONDS.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_SEC = "seconds|sec|s";
+
+	/** Regular Expression of all string allowed to mean MINUTES.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_MIN = "min|minutes|m";
+
+	/** Regular Expression of all string allowed to mean HOURS.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_HOURS = "hours|h";
+
+	/** Regular Expression of all string allowed to mean DAYS.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_DAYS = "days|D";
+
+	/** Regular Expression of all string allowed to mean WEEKS.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_WEEKS = "weeks|W";
+
+	/** Regular Expression of all string allowed to mean MONTHS.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_MONTHS = "months|M";
+
+	/** Regular Expression of all string allowed to mean YEARS.
+	 * <p><b>Important:</b> opening and closing brackets are omitted here for a better integration
+	 * in the duration string's regular expression (see {@link #PATTERN_DURATION} ).</p> */
+	protected static String REGEXP_YEARS = "years|Y";
+
+	/** Pattern created with the Regular Expression of a valid duration string.
+	 * <p>
+	 * 	Such string MUST be a positive integer/long value eventually suffixed by a unit.
+	 * 	Allowed unit strings are the following:
+	 * </p>
+	 * <ul>
+	 * 	<li>milliseconds, ms</li>
+	 * 	<li>seconds, sec, s</li>
+	 * 	<li>minutes, min, m</li>
+	 * 	<li>hours, h</li>
+	 * 	<li>days, D</li>
+	 * 	<li>weeks, W</li>
+	 * 	<li>months, M</li>
+	 * 	<li>years, Y</li>
+	 * </ul>
+	 * 
+	 * <p><b>Important:</b>
+	 * 	Units are case <b>sensitive</b>!
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	Space characters are ignored only if leading or trailing the whole string,
+	 * 	or if between the duration and its unit.
+	 * </i></p> */
+	protected static Pattern PATTERN_DURATION = Pattern.compile("\\s*([0-9]+)\\s*(" + REGEXP_MS + "|" + REGEXP_SEC + "|" + REGEXP_MIN + "|" + REGEXP_HOURS + "|" + REGEXP_DAYS + "|" + REGEXP_WEEKS + "|" + REGEXP_MONTHS + "|" + REGEXP_YEARS + ")?\\s*");
+
+	/**
+	 * Parse the given duration string.
+	 * 
+	 * <p>
+	 * 	Such string MUST be a positive integer/long value eventually suffixed by a unit.
+	 * 	Allowed unit strings are the following:
+	 * </p>
+	 * <ul>
+	 * 	<li>milliseconds, ms</li>
+	 * 	<li>seconds, sec, s</li>
+	 * 	<li>minutes, min, m</li>
+	 * 	<li>hours, h</li>
+	 * 	<li>days, D</li>
+	 * 	<li>weeks, W</li>
+	 * 	<li>months, M</li>
+	 * 	<li>years, Y</li>
+	 * </ul>
+	 * 
+	 * <p><b>Important:</b>
+	 * 	Units are case <b>sensitive</b>!
+	 * </p>
+	 * 
+	 * <p><i>Note:
+	 * 	Space characters are ignored only if leading or trailing the whole string,
+	 * 	or if between the duration and its unit.
+	 * </i></p>
+	 * 
+	 * @param duration	The duration string.
+	 * 
+	 * @return	The parsed duration converted into milliseconds,
+	 *        	or <code>-1</code> if the given string is <code>null</code> or negative.
+	 * 
+	 * @throws ParseException	If the given string is using an unknown unit string,
+	 *                       	or if the string does not start digits.
+	 * 
+	 * @see #toString(Long)
+	 */
+	public long parseDuration(final String duration) throws ParseException{
+		if (duration == null || duration.matches("\\s*-.*"))
+			return -1;
+
+		Matcher matcher = PATTERN_DURATION.matcher(duration);
+		if (!matcher.matches())
+			throw new ParseException("Unexpected format for a duration: \"" + duration + "\"! Cause: it does not match the following Regular Expression: " + PATTERN_DURATION.pattern(), 0);
+
+		try{
+			// Extract the numerical value:
+			long numDuration = Long.parseLong(matcher.group(1));
+
+			// Apply any multiplication to this duration:
+			String unit = matcher.group(2);
+			if (unit == null || unit.length() == 0 || unit.matches("(" + REGEXP_MS + ")"))
+				return numDuration;
+			else if (unit.matches("(" + REGEXP_SEC + ")"))
+				return numDuration * MULT_SEC;
+			else if (unit.matches("(" + REGEXP_MIN + ")"))
+				return numDuration * MULT_MIN;
+			else if (unit.matches("(" + REGEXP_HOURS + ")"))
+				return numDuration * MULT_HOURS;
+			else if (unit.matches("(" + REGEXP_DAYS + ")"))
+				return numDuration * MULT_DAYS;
+			else if (unit.matches("(" + REGEXP_WEEKS + ")"))
+				return numDuration * MULT_WEEKS;
+			else if (unit.matches("(" + REGEXP_MONTHS + ")"))
+				return numDuration * MULT_MONTHS;
+			else if (unit.matches("(" + REGEXP_YEARS + ")"))
+				return numDuration * MULT_YEARS;
+		}catch(Exception ex){
+			throw new ParseException("Unexpected format for a duration: \"" + duration + "\"! Cause: " + ex.getMessage(), matcher.regionStart());
+		}
+
+		return -1;
+	}
+
+	/**
+	 * Convert a duration value (expressed in milliseconds) into the best human readable unit value.
+	 * 
+	 * @param duration	A duration in milliseconds.
+	 * 
+	 * @return	An empty string if the given duration is <code>null</code>,
+	 *        	or a string expressing the given duration in the best integer value with a unit suffix.
+	 * 
+	 * @see #parseDuration(String)
+	 */
+	public String toString(Long duration){
+		if (duration == null)
+			return "";
+
+		if (duration == 0)
+			return "0ms";
+		else if (duration % MULT_YEARS == 0)
+			return duration / MULT_YEARS + "Y";
+		else if (duration % MULT_MONTHS == 0)
+			return duration / MULT_MONTHS + "M";
+		else if (duration % MULT_WEEKS == 0)
+			return duration / MULT_WEEKS + "W";
+		else if (duration % MULT_DAYS == 0)
+			return duration / MULT_DAYS + "D";
+		else if (duration % MULT_HOURS == 0)
+			return duration / MULT_HOURS + "h";
+		else if (duration % MULT_MIN == 0)
+			return duration / MULT_MIN + "m";
+		else if (duration % MULT_SEC == 0)
+			return duration / MULT_SEC + "s";
+		else
+			return duration + "ms";
+	}
+
+}
diff --git a/src/uws/job/parameters/NumericParamController.java b/src/uws/job/parameters/NumericParamController.java
new file mode 100644
index 0000000000000000000000000000000000000000..da206832f8b1ff888088f968fc48dd214eda5621
--- /dev/null
+++ b/src/uws/job/parameters/NumericParamController.java
@@ -0,0 +1,300 @@
+package uws.job.parameters;
+
+/*
+ * 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 2016 - Astronomisches Rechen Institut (ARI)
+ */
+
+import java.io.Serializable;
+
+import uws.UWSException;
+
+/**
+ * <p>
+ * 	Let controlling a numeric parameter. Thus it is possible to set a default but also a minimum and a maximum value.
+ * 	Moreover you can indicate whether the value of the parameter can be modified by the user or not after initialization.
+ * </p>
+ * 
+ * <p>Here is the logic applied by this controller for a numeric parameter:</p>
+ * <ul>
+ * 	<li>If no value is specified by the UWS client, the default value is returned.
+ * 		But if no default value is returned, <code>null</code> will then be returned.</li>
+ * 	<li>If the given value is smaller than the minimum (if any is set), the minimum value is returned.</li>
+ * 	<li>If the given value is bigger than the maximum (if any is set), the maximum value is returned.</li>
+ * </ul>
+ * <p>
+ * 	This implementation aims to be generic enough to support most of the numeric parameters, but if the logic
+ * 	presented above is not enough or does not fit your needs, you are free to extend it and thus benefit of the
+ * 	implementation of most of the functions and attributes of this controller.
+ * </p>
+ * 
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 4.2 (06/2016)
+ * @since 4.2
+ */
+public class NumericParamController implements InputParamController, Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/** The maximum value. */
+	protected Number minValue = null;
+
+	/** The default value. <i>MUST be between {@link #minValue} and  {@link #maxValue}</i> */
+	protected Number defaultValue = null;
+
+	/** The maximum value. */
+	protected Number maxValue = null;
+
+	/** Indicates whether the parameter can be modified. */
+	protected boolean allowModification = true;
+
+	/**
+	 * Create a numeric controller with no restriction.
+	 * 
+	 * <p>
+	 * 	A default, minimum and/or maximum value can be set after creation using {@link #setDefault(Number)},
+	 * 	{@link #setMinimum(Number)} and {@link #setMaximum(Number)}. By default this parameter can always be modified,
+	 * 	but it can be forbidden using {@link #allowModification(boolean)}.
+	 * </p>
+	 */
+	public NumericParamController(){}
+
+	/**
+	 * <p>Create a controller for a numeric parameter.
+	 * The default and the maximum value are initialized with the given parameters.
+	 * The third parameter allows also to forbid the modification of the parameter value by the user,
+	 * if set to <i>false</i>.</p>
+	 * 
+	 * <p>
+	 * 	A default and/or maximum value can be modified after creation using {@link #setDefault(Number)}
+	 * 	and {@link #setMaximum(Number)}. The flag telling whether this parameter can be modified by the user
+	 * 	can be changed using {@link #allowModification(boolean)}.
+	 * </p>
+	 * 
+	 * @param defaultValue		Value set by default to the parameter, when none is specified.
+	 * @param minValue			Minimum value that can be set. If a smaller value is provided by the user, an exception will be thrown by {@link #check(Object)}.
+	 * @param maxValue			Maximum value that can be set. If a bigger value is provided by the user, an exception will be thrown by {@link #check(Object)}.
+	 * @param allowModification	<i>true</i> to allow the user to modify this value when creating a job, <i>false</i> otherwise.
+	 */
+	public NumericParamController(final Number defaultValue, final Number minValue, final Number maxValue, final boolean allowModification){
+		reset(defaultValue, minValue, maxValue);
+		allowModification(allowModification);
+	}
+
+	/* ***************** */
+	/* GETTERS & SETTERS */
+	/* ***************** */
+
+	/**
+	 * Gets the minimum value of this parameter.
+	 * 
+	 * @return	The minimum value
+	 *        	or <code>null</code> if none has been specified.
+	 */
+	public final Number getMinimum(){
+		return minValue;
+	}
+
+	/**
+	 * Sets the minimum value of this parameter.
+	 * 
+	 * <p><b>Warning !:</b>
+	 * 	If the <em>given</em> value is <em>bigger</em> than the {@link #getMaximum() maximum} value,
+	 * 	the minimum will be set automatically to the {@link #getMaximum() maximum} value ;
+	 * 	the given one will then be ignored.
+	 * </p>
+	 * 
+	 * <p><b>Warning 2:</b>
+	 * 	If the <em>default</em> value is <em>smaller</em> than the new {@link #getMinimum() minimum} value,
+	 * 	the default will be set automatically to the new {@link #getMinimum() minimum} value.
+	 * </p>
+	 * 
+	 * @param newMinValue The new minimum value. <i><code>null</code> is allowed ; it will remove the constraint on the minimum</i>
+	 */
+	public void setMinimum(final Number newMinValue){
+		// If NULL, set it and return:
+		if (newMinValue == null)
+			minValue = null;
+		// Otherwise:
+		else{
+			// Set the minimum to the maximum value if the given value is BIGGER than the current maximum:
+			if (maxValue != null && newMinValue.doubleValue() > maxValue.doubleValue())
+				minValue = maxValue;
+			// Otherwise, set it exactly as provided:
+			else
+				minValue = newMinValue;
+
+			/* Ensure the default value is still bigger than the minimum.
+			 * If not, set the default to the minimum: */
+			if (defaultValue != null && defaultValue.doubleValue() < minValue.doubleValue())
+				defaultValue = minValue;
+		}
+	}
+
+	/**
+	 * Gets the maximum value of this parameter.
+	 * 
+	 * @return	The maximum value
+	 *        	or <code>null</code> if none has been specified.
+	 */
+	public final Number getMaximum(){
+		return maxValue;
+	}
+
+	/**
+	 * Sets the maximum value of this parameter.
+	 * 
+	 * <p><b>Warning 1:</b>
+	 * 	If the <em>given</em> value is <em>smaller</em> than the {@link #getMinimum() minimum} value,
+	 * 	the maximum will be set automatically to the {@link #getMinimum() minimum} value ;
+	 * 	the given one will then be ignored.
+	 * </p>
+	 * 
+	 * <p><b>Warning 2:</b>
+	 * 	If the <em>default</em> value is <em>bigger</em> than the new {@link #getMaximum() maximum} value,
+	 * 	the default will be set automatically to the new {@link #getMaximum() maximum} value.
+	 * </p>
+	 * 
+	 * @param newMaxValue The new maximum value. <i><code>null</code> is allowed ; it will remove the constraint on the maximum</i>
+	 */
+	public void setMaximum(final Number newMaxValue){
+		// If NULL, set it and return:
+		if (newMaxValue == null)
+			maxValue = null;
+		// Otherwise:
+		else{
+			// Set the maximum to the minimum value if the given value is SMALLER than the current minimum:
+			if (minValue != null && newMaxValue.doubleValue() < minValue.doubleValue())
+				maxValue = minValue;
+			// Otherwise, set it exactly as provided:
+			else
+				maxValue = newMaxValue;
+
+			/* Ensure the default value is still smaller than the maximum.
+			 * If not, set the default to the maximum: */
+			if (defaultValue != null && defaultValue.doubleValue() > maxValue.doubleValue())
+				defaultValue = maxValue;
+		}
+	}
+
+	@Override
+	public Object getDefault(){
+		return defaultValue;
+	}
+
+	/**
+	 * Set the default value that the parameter must have if none is specified by the user.
+	 * 
+	 * <p><b>Warning:</b>
+	 * 	If the given value is not between the {@link #getMinimum() minimum} and the {@link #getMaximum() maximum}
+	 * 	of this controller, the default value will be automatically set to the {@link #getMinimum() minimum} value.
+	 * </p>
+	 * 
+	 * @param newDefaultValue	The new default value for this controller.
+	 *                       	<i><code>null</code> allowed ; <code>null</code> will then be returned if a given parameter is not set by the user</i>
+	 */
+	public void setDefault(final Number newDefaultValue){
+		// If NULL, set it and return:
+		if (newDefaultValue == null)
+			defaultValue = null;
+		// Otherwise:
+		else{
+			// If SMALLER than the minimum of this controller, set the default to the minimum:
+			if (minValue != null && newDefaultValue.doubleValue() < minValue.doubleValue())
+				defaultValue = minValue;
+			// If BIGGER than the maximum of this controller, set the default to the maximum:
+			else if (maxValue != null && newDefaultValue.doubleValue() > maxValue.doubleValue())
+				defaultValue = maxValue;
+			// Otherwise, set it exactly as provided:
+			else
+				defaultValue = newDefaultValue;
+		}
+	}
+
+	/**
+	 * Reset all fields in the same time.
+	 * 
+	 * @param defaultVal	Value set by default to the parameter, when none is specified.
+	 * @param minVal		Minimum value that can be set. If a smaller value is provided by the user, an exception will be thrown by {@link #check(Object)}.
+	 * @param maxVal		Maximum value that can be set. If a bigger value is provided by the user, an exception will be thrown by {@link #check(Object)}.
+	 */
+	public void reset(final Number defaultVal, final Number minVal, final Number maxVal){
+		// Reset manually all concerned fields to NULL in order to avoid any conflict between the new and the old values:
+		minValue = null;
+		defaultValue = null;
+		maxValue = null;
+
+		// Set the given values with the appropriate set functions:
+		setMinimum(minVal);
+		setMaximum(maxVal);
+		setDefault(defaultVal);
+	}
+
+	@Override
+	public boolean allowModification(){
+		return allowModification;
+	}
+
+	/**
+	 * Lets indicating whether the value of the parameter can be modified after initialization.
+	 * 
+	 * @param allowModification <i>true</i> if the parameter value can be modified,
+	 *                         	<i>false</i> otherwise.
+	 */
+	public void allowModification(final boolean allowModification){
+		this.allowModification = allowModification;
+	}
+
+	/* ***************** */
+	/* CHECKING FUNCTION */
+	/* ***************** */
+
+	@Override
+	public Object check(final Object value) throws UWSException{
+		// If no value, return the default one:
+		if (value == null)
+			return getDefault();
+
+		// Otherwise, parse the given numeric value:
+		Number numVal = null;
+		if (value instanceof Number)
+			numVal = (Number)value;
+		else if (value instanceof String){
+			String strValue = (String)value;
+			try{
+				numVal = Double.parseDouble(strValue);
+			}catch(NumberFormatException nfe1){
+				try{
+					numVal = Long.parseLong(strValue);
+				}catch(NumberFormatException nfe2){
+					throw new UWSException(UWSException.BAD_REQUEST, "Wrong format for a numeric parameter: \"" + strValue + "\"! It should be a double or a long value between " + (minValue == null ? Double.MIN_VALUE : minValue) + " and " + (maxValue == null ? Double.MAX_VALUE : maxValue) + " (Default value: " + (defaultValue == null ? "none" : defaultValue) + ").");
+				}
+			}
+		}else
+			throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Wrong type for a numeric parameter: class \"" + value.getClass().getName() + "\"! It should be a double, a long value or a string containing only a double or a long value.");
+
+		// If the value is SMALLER than the minimum, the minimum value will be returned:
+		if (minValue != null && numVal.doubleValue() < minValue.doubleValue())
+			return minValue;
+		// If the value is BIGGER than the maximum, the maximum value will be returned:
+		else if (maxValue != null && numVal.doubleValue() > maxValue.doubleValue())
+			return maxValue;
+		// Otherwise, return the parsed number:
+		else
+			return numVal;
+	}
+
+}
diff --git a/src/uws/job/parameters/StringParamController.java b/src/uws/job/parameters/StringParamController.java
index 2eecd59327768ed955441981ad5a911a5b29c3e3..482e1a8f6e5c0680784813a124ff01de31b842e7 100644
--- a/src/uws/job/parameters/StringParamController.java
+++ b/src/uws/job/parameters/StringParamController.java
@@ -16,7 +16,7 @@ package uws.job.parameters;
  * 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-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -26,19 +26,26 @@ import uws.UWSException;
  * Let controlling a String parameter.
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (09/2014)
+ * @version 4.2 (06/2016)
  */
 public class StringParamController implements InputParamController {
 
 	/** Name of the controlled parameter. */
 	private final String paramName;
 
-	/** Default value of this parameter. <i>By default: NULL</i> */
+	/** Default value of this parameter.
+	 * <i>By default: <code>null</code></i> */
 	private String defaultValue = null;
 
-	/** List of all allowed values. If NULL, any value is allowed. <i>By default: NULL</i> */
+	/** List of all allowed values. If NULL, any value is allowed.
+	 * <i>By default: <code>null</code></i> */
 	private String[] possibleValues = null;
 
+	/** Regular Expression that any value of this input parameter must match in order to be valid.
+	 * <i>By default: <code>null</code>.</i>
+	 * @since 4.2 */
+	private String regexp = null;
+
 	/** Tells whether the parameter can be modified after its initialization. */
 	private boolean allowModification = true;
 
@@ -50,7 +57,7 @@ public class StringParamController implements InputParamController {
 	 * @see #StringParamController(String, String, String[], boolean)
 	 */
 	public StringParamController(final String paramName){
-		this(paramName, null, null, true);
+		this(paramName, null, (String[])null, true);
 	}
 
 	/**
@@ -68,6 +75,21 @@ public class StringParamController implements InputParamController {
 		allowModification(allowModif);
 	}
 
+	/**
+	 * Builds a controller of the specified String parameter and configures it.
+	 * 
+	 * @param paramName			Name of the controlled parameter.
+	 * @param defaultValue		Default value of this parameter. <i><u>note:</u> It may be NULL. If the next parameter is not NULL or empty, the default value must be one of its values.</i>
+	 * @param regExp			Regular Expression that any value of this parameter must match. <i><u>note:</u> It may be NULL or empty. In this case, any value is allowed.</i>
+	 * @param allowModif		<i>true</i> to allow the modification of the specified parameter after its initialization, <i>false</i> otherwise.
+	 */
+	public StringParamController(final String paramName, final String defaultValue, final String regExp, final boolean allowModif){
+		this.paramName = paramName;
+		setDefaultValue(defaultValue);
+		setRegExp(regExp);
+		allowModification(allowModif);
+	}
+
 	/**
 	 * Gets the default value of the parameter.
 	 * 
@@ -86,6 +108,36 @@ public class StringParamController implements InputParamController {
 		this.defaultValue = defaultValue;
 	}
 
+	/**
+	 * Gets the Regular Expression that all values of this parameter must match.
+	 * 
+	 * @return The current Regular Expression. (MAY BE NULL)
+	 * 
+	 * @since 4.2
+	 */
+	public final String getRegExp(){
+		return regexp;
+	}
+
+	/**
+	 * Sets the Regular Expression that all values of this parameter must match.
+	 * 
+	 * <p><b>Warning:</b> setting a non empty regular expression will set to <code>null</code>
+	 * 	any list of allowed values eventually set in this controller.
+	 * </p>
+	 * 
+	 * @param regExp The new Regular Expression. <i><u>note:</u> If NULL or empty, any value is allowed.</i>
+	 * 
+	 * @since 4.2
+	 */
+	public final void setRegExp(String regExp){
+		if (regExp != null && regExp.length() > 0){
+			this.regexp = regExp;
+			this.possibleValues = null;
+		}else
+			this.regexp = null;
+	}
+
 	/**
 	 * Gets the list of all allowed values.
 	 * 
@@ -98,13 +150,19 @@ public class StringParamController implements InputParamController {
 	/**
 	 * Sets the list of all allowed values.
 	 * 
+	 * <p><b>Warning:</b> setting a non empty list of values will set to <code>null</code>
+	 * 	any regular expression eventually set in this controller.
+	 * </p>
+	 * 
 	 * @param possibleValues The new allowed values. <i><u>note:</u> If NULL or empty, any value is allowed.</i>
 	 */
 	public final void setPossibleValues(String[] possibleValues){
 		if (possibleValues == null || possibleValues.length == 0)
 			this.possibleValues = null;
-		else
+		else{
 			this.possibleValues = possibleValues;
+			this.regexp = null;
+		}
 	}
 
 	@Override
@@ -134,6 +192,11 @@ public class StringParamController implements InputParamController {
 						return v;
 				}
 				throw new UWSException(UWSException.BAD_REQUEST, "Unknown value for the parameter \"" + paramName + "\": \"" + strValue + "\". It should be " + getExpectedFormat());
+			}else if (regexp != null){
+				if (strValue.matches(regexp))
+					return strValue;
+				else
+					throw new UWSException(UWSException.BAD_REQUEST, "Incorrect value for the property \"" + paramName + "\": \"" + strValue + "\". It should be " + getExpectedFormat());
 			}else
 				return strValue;
 		}else
@@ -151,7 +214,9 @@ public class StringParamController implements InputParamController {
 			for(int i = 0; i < possibleValues.length; i++)
 				buffer.append((i == 0) ? "" : ", ").append(possibleValues[i]);
 			return buffer.toString();
-		}else
+		}else if (regexp != null)
+			return "a String matching this Regular Expression: " + regexp;
+		else
 			return "a String value.";
 	}
 
diff --git a/src/uws/service/UWSService.java b/src/uws/service/UWSService.java
index 8622bdebef08ea431c64e842bba96ce3fb2f409c..248f25beae0cea7fb9b12d97715c79f8b891d0af 100644
--- a/src/uws/service/UWSService.java
+++ b/src/uws/service/UWSService.java
@@ -137,7 +137,7 @@ import uws.service.request.RequestParser;
  * </p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.2 (01/2016)
+ * @version 4.2 (06/2016)
  */
 public class UWSService implements UWS {
 
@@ -168,6 +168,10 @@ public class UWSService implements UWS {
 	/** Indicates whether the home page must be a copy or a redirection to the given URL. */
 	protected boolean homeRedirection = false;
 
+	/** MIME type of the custom home page. By default, it is "text/html".
+	 * @since 4.2 */
+	protected String homePageMimeType = "text/html";
+
 	/** List of UWS actions (i.e. to list jobs, to get a job, to set a job parameter, etc...). */
 	protected final Vector<UWSAction> uwsActions;
 
@@ -601,6 +605,42 @@ public class UWSService implements UWS {
 		homeRedirection = false;
 	}
 
+	/**
+	 * <p>Get the MIME type of the custom home page.</p>
+	 * 
+	 * <p>By default, it is the same as the default home page: "text/html".</p>
+	 * 
+	 * <p><i>Note:
+	 * 	This function has a sense only if the HOME PAGE resource of this UWS service
+	 * 	is still the default home page (i.e. {@link ShowHomePage}).
+	 * </i></p>
+	 * 
+	 * @return	MIME type of the custom home page.
+	 * 
+	 * @since 4.2
+	 */
+	public final String getHomePageMimeType(){
+		return homePageMimeType;
+	}
+
+	/**
+	 * <p>Set the MIME type of the custom home page.</p>
+	 * 
+	 * <p>A NULL value will be considered as "text/html".</p>
+	 * 
+	 * <p><i>Note:
+	 * 	This function has a sense only if the HOME PAGE resource of this UWS service
+	 * 	is still the default home page (i.e. {@link ShowHomePage}).
+	 * </i></p>
+	 * 
+	 * @param mime	MIME type of the custom home page.
+	 * 
+	 * @since 4.2
+	 */
+	public final void setHomePageMimeType(final String mime){
+		homePageMimeType = (mime == null || mime.trim().length() == 0) ? "text/html" : mime.trim();
+	}
+
 	/* ********************** */
 	/* SERIALIZERS MANAGEMENT */
 	/* ********************** */
@@ -1117,7 +1157,7 @@ public class UWSService implements UWS {
 			/*
 			 *   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.
 			 */
@@ -1133,7 +1173,7 @@ public class UWSService implements UWS {
 			 *   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, response, request, reqID, user, ((action != null) ? action.getName() : null));
diff --git a/src/uws/service/actions/ShowHomePage.java b/src/uws/service/actions/ShowHomePage.java
index d40c892c556a2e577c183e8f2c3d51a64960a4d9..1fa4e246f0b3577202bd3ba8277430d81aa0850c 100644
--- a/src/uws/service/actions/ShowHomePage.java
+++ b/src/uws/service/actions/ShowHomePage.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-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -45,7 +45,7 @@ import uws.service.log.UWSLog.LogLevel;
  * <p>This action displays the UWS home page.</p>
  * 
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 4.1 (04/2015)
+ * @version 4.2 (06/2016)
  */
 public class ShowHomePage extends UWSAction {
 	private static final long serialVersionUID = 1L;
@@ -128,7 +128,7 @@ public class ShowHomePage extends UWSAction {
 				URL homePageUrl = new URL(uws.getHomePage());
 				BufferedReader reader = new BufferedReader(new InputStreamReader(homePageUrl.openStream()));
 
-				response.setContentType("text/html");
+				response.setContentType(uws.getHomePageMimeType());
 				response.setCharacterEncoding(UWSToolBox.DEFAULT_CHAR_ENCODING);
 				PrintWriter writer = response.getWriter();
 				try{
diff --git a/test/uws/config/TestConfigurableUWSFactory.java b/test/uws/config/TestConfigurableUWSFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1e21d8f2b92ac8a489f93e6da434872a8c8ecb0
--- /dev/null
+++ b/test/uws/config/TestConfigurableUWSFactory.java
@@ -0,0 +1,327 @@
+package uws.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static uws.config.ConfigurableUWSFactory.PATTERN_PARAMETER;
+
+import java.util.regex.Matcher;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uws.UWSException;
+import uws.job.parameters.DurationParamController;
+import uws.job.parameters.NumericParamController;
+import uws.job.parameters.StringParamController;
+
+public class TestConfigurableUWSFactory {
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@Test
+	public void testPatternParameter(){
+		/* JUST A NAME => OK */
+		Matcher matcher = PATTERN_PARAMETER.matcher("param1");
+		assertTrue(matcher.matches());
+		assertEquals("param1", matcher.group(16));
+		assertNull(matcher.group(6));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(11));
+		assertNull(matcher.group(18));
+
+		/* EMPTY STRING => OK */
+		matcher = PATTERN_PARAMETER.matcher("");
+		assertTrue(matcher.matches());
+		assertEquals(0, matcher.group(1).length());
+		assertNull(matcher.group(6));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(11));
+		assertNull(matcher.group(18));
+
+		/* EMPTY STRING + , (SOMETHING OR NOT) => OK */
+		matcher = PATTERN_PARAMETER.matcher(",");
+		assertTrue(matcher.matches());
+		assertEquals(0, matcher.group(1).length());
+		assertEquals(0, matcher.group(18).length());
+		matcher = PATTERN_PARAMETER.matcher(",something");
+		assertTrue(matcher.matches());
+		assertEquals(0, matcher.group(1).length());
+		assertEquals("something", matcher.group(18));
+		assertNull(matcher.group(6));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(11));
+
+		/* A FULL DEFINITION WITH JUST NAME => ERROR */
+		matcher = PATTERN_PARAMETER.matcher("[param1]");
+		assertFalse(matcher.matches());
+
+		/* NUMERIC */
+		matcher = PATTERN_PARAMETER.matcher("[param1,yes, numeric, 123,45,65]");
+		assertTrue(matcher.matches());
+		assertEquals("param1", matcher.group(2));
+		assertEquals("yes", matcher.group(5));
+		assertEquals("numeric", matcher.group(7));
+		assertEquals(" 123", matcher.group(8));
+		assertEquals("45", matcher.group(9));
+		assertEquals("65", matcher.group(10));
+		assertNull(matcher.group(11));
+		assertNull(matcher.group(16));
+		assertNull(matcher.group(18));
+
+		matcher = PATTERN_PARAMETER.matcher("[param1,yes, numeric, 123]");
+		assertFalse(matcher.matches());
+
+		/* DURATION */
+		matcher = PATTERN_PARAMETER.matcher("[param1,false,duration,10D,1h,1M]");
+		assertTrue(matcher.matches());
+		assertEquals("param1", matcher.group(2));
+		assertEquals("false", matcher.group(5));
+		assertEquals("duration", matcher.group(7));
+		assertEquals("10D", matcher.group(8));
+		assertEquals("1h", matcher.group(9));
+		assertEquals("1M", matcher.group(10));
+		assertNull(matcher.group(11));
+		assertNull(matcher.group(16));
+		assertNull(matcher.group(18));
+
+		/* STRING */
+		matcher = PATTERN_PARAMETER.matcher(" [ param1, true, string , \"foo\" , /toto///i ] ");
+		assertTrue(matcher.matches());
+		assertEquals(" param1", matcher.group(2));
+		assertEquals(" true", matcher.group(5));
+		assertEquals("string", matcher.group(11));
+		assertEquals("foo", matcher.group(12));
+		assertEquals("toto//", matcher.group(13));
+		assertEquals("i", matcher.group(15));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(16));
+		assertNull(matcher.group(18));
+
+		matcher = PATTERN_PARAMETER.matcher(" [ param1, true, STRING, \"foo\" , /toto///I ] ");
+		assertTrue(matcher.matches());
+		assertEquals(" param1", matcher.group(2));
+		assertEquals(" true", matcher.group(5));
+		assertEquals("STRING", matcher.group(11));
+		assertEquals("foo", matcher.group(12));
+		assertEquals("toto//", matcher.group(13));
+		assertEquals("I", matcher.group(15));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(16));
+		assertNull(matcher.group(18));
+
+		matcher = PATTERN_PARAMETER.matcher(" [ param1, true, STRING, \"foo\" , /f.*/i ] ");
+		assertTrue(matcher.matches());
+		assertEquals(" param1", matcher.group(2));
+		assertEquals(" true", matcher.group(5));
+		assertEquals("STRING", matcher.group(11));
+		assertEquals("foo", matcher.group(12));
+		assertEquals("f.*", matcher.group(13));
+		assertEquals("i", matcher.group(15));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(16));
+		assertNull(matcher.group(18));
+
+		// MORE PARAMETER
+		matcher = PATTERN_PARAMETER.matcher("param1, [ param2, true, STRING, \"foo\" , /toto///I ] ");
+		assertTrue(matcher.matches());
+		assertEquals("param1", matcher.group(16));
+		assertNull(matcher.group(6));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(11));
+		assertEquals(" [ param2, true, STRING, \"foo\" , /toto///I ] ", matcher.group(18));
+
+		matcher = PATTERN_PARAMETER.matcher("param1,  ");
+		assertTrue(matcher.matches());
+		assertEquals("param1", matcher.group(16));
+		assertNull(matcher.group(6));
+		assertNull(matcher.group(7));
+		assertNull(matcher.group(11));
+		assertEquals("  ", matcher.group(18));
+		
+		// CUSTOM CONTROLLER
+		matcher = PATTERN_PARAMETER.matcher("[param1, {aPackage.MyCustomController} ]");
+		assertTrue(matcher.matches());
+		assertEquals("param1", matcher.group(2));
+		assertEquals("{aPackage.MyCustomController}", matcher.group(4));
+		assertNull(matcher.group(5));
+		assertNull(matcher.group(6));
+		assertNull(matcher.group(16));
+		assertNull(matcher.group(18));
+	}
+	
+	@Test
+	public void testInitParameters(){
+		// NO PARAMETER
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", "");
+			assertFalse(factory.jobParams.containsKey("jobs"));
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", "  	");
+			assertFalse(factory.jobParams.containsKey("jobs"));
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", null);
+			assertFalse(factory.jobParams.containsKey("jobs"));
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+		
+		// PARAMETER WITH NO CONTROLLER
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", "param1");
+			assertNotNull(factory.jobParams.get("jobs"));
+			assertEquals(1, factory.jobParams.get("jobs").size());
+			assertTrue(factory.jobParams.get("jobs").containsKey("param1"));
+			assertNull(factory.jobParams.get("jobs").get("param1"));
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+		
+		// PARAMETER WITH A CUSTOM CONTROLLER
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", "[ param1 , {uws.job.parameters.NumericParamController} ]");
+			assertNotNull(factory.jobParams.get("jobs"));
+			assertEquals(1, factory.jobParams.get("jobs").size());
+			assertTrue(factory.jobParams.get("jobs").containsKey("param1"));
+			assertNotNull(factory.jobParams.get("jobs").get("param1"));
+			assertEquals(NumericParamController.class, factory.jobParams.get("jobs").get("param1").getClass());
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+		
+		// PARAMETER WITH A STRING CONTROLLER
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", "[ param1 , yes, string, \"blabla\" , /b.*/i ], [ param2 , , string, \"blabla\" , /b.*/ ], [ param3 , false , string, \" a\" , /\\s+[a-zA-Z]/ ]");
+			assertNotNull(factory.jobParams.get("jobs"));
+			assertEquals(3, factory.jobParams.get("jobs").size());
+			// test param1
+			assertTrue(factory.jobParams.get("jobs").containsKey("param1"));
+			assertNotNull(factory.jobParams.get("jobs").get("param1"));
+			assertEquals(StringParamController.class, factory.jobParams.get("jobs").get("param1").getClass());
+			StringParamController controller = (StringParamController)factory.jobParams.get("jobs").get("param1");
+			assertEquals("blabla", controller.getDefault());
+			assertEquals("(?i)b.*", controller.getRegExp());
+			assertTrue(controller.allowModification());
+			// test param2
+			assertTrue(factory.jobParams.get("jobs").containsKey("param2"));
+			assertNotNull(factory.jobParams.get("jobs").get("param2"));
+			assertEquals(StringParamController.class, factory.jobParams.get("jobs").get("param2").getClass());
+			controller = (StringParamController)factory.jobParams.get("jobs").get("param2");
+			assertEquals("blabla", controller.getDefault());
+			assertEquals("b.*", controller.getRegExp());
+			assertTrue(controller.allowModification());
+			// test param3
+			assertTrue(factory.jobParams.get("jobs").containsKey("param3"));
+			assertNotNull(factory.jobParams.get("jobs").get("param3"));
+			assertEquals(StringParamController.class, factory.jobParams.get("jobs").get("param3").getClass());
+			controller = (StringParamController)factory.jobParams.get("jobs").get("param3");
+			assertEquals(" a", controller.getDefault());
+			assertEquals("\\s+[a-zA-Z]", controller.getRegExp());
+			assertFalse(controller.allowModification());
+			
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+		
+		// PARAMETER WITH A NUMERIC CONTROLLER
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", "[ param1 , yes, numeric, 20, 10,30], [ param2 , , numeric, 0, -10, 10 ], [ param3 , false , numeric, , , 100]");
+			assertNotNull(factory.jobParams.get("jobs"));
+			assertEquals(3, factory.jobParams.get("jobs").size());
+			// test param1
+			assertTrue(factory.jobParams.get("jobs").containsKey("param1"));
+			assertNotNull(factory.jobParams.get("jobs").get("param1"));
+			assertEquals(NumericParamController.class, factory.jobParams.get("jobs").get("param1").getClass());
+			NumericParamController controller = (NumericParamController)factory.jobParams.get("jobs").get("param1");
+			assertEquals(20.0, controller.getDefault());
+			assertEquals(10.0, controller.getMinimum());
+			assertEquals(30.0, controller.getMaximum());
+			assertTrue(controller.allowModification());
+			// test param2
+			assertTrue(factory.jobParams.get("jobs").containsKey("param2"));
+			assertNotNull(factory.jobParams.get("jobs").get("param2"));
+			assertEquals(NumericParamController.class, factory.jobParams.get("jobs").get("param2").getClass());
+			controller = (NumericParamController)factory.jobParams.get("jobs").get("param2");
+			assertEquals(0.0, controller.getDefault());
+			assertEquals(-10.0, controller.getMinimum());
+			assertEquals(10.0, controller.getMaximum());
+			assertTrue(controller.allowModification());
+			// test param3
+			assertTrue(factory.jobParams.get("jobs").containsKey("param3"));
+			assertNotNull(factory.jobParams.get("jobs").get("param3"));
+			assertEquals(NumericParamController.class, factory.jobParams.get("jobs").get("param3").getClass());
+			controller = (NumericParamController)factory.jobParams.get("jobs").get("param3");
+			assertNull(controller.getDefault());
+			assertNull(controller.getMinimum());
+			assertEquals(100.0, controller.getMaximum());
+			assertFalse(controller.allowModification());
+			
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+		
+		// PARAMETER WITH A DURATION CONTROLLER
+		try{
+			ConfigurableUWSFactory factory = new ConfigurableUWSFactory();
+			factory.initParameters("jobs", "[ param1 , yes, duration, 20sec, 1s, 30 seconds], [ param2 , , duration, 10min, 1 m, 1 hours ], [ param3 , false , duration, 1D , , 1 years]");
+			assertNotNull(factory.jobParams.get("jobs"));
+			assertEquals(3, factory.jobParams.get("jobs").size());
+			// test param1
+			assertTrue(factory.jobParams.get("jobs").containsKey("param1"));
+			assertNotNull(factory.jobParams.get("jobs").get("param1"));
+			assertEquals(DurationParamController.class, factory.jobParams.get("jobs").get("param1").getClass());
+			DurationParamController controller = (DurationParamController)factory.jobParams.get("jobs").get("param1");
+			assertEquals(20000l, controller.getDefault());
+			assertEquals(1000l, controller.getMinimum());
+			assertEquals(30000l, controller.getMaximum());
+			assertTrue(controller.allowModification());
+			// test param2
+			assertTrue(factory.jobParams.get("jobs").containsKey("param2"));
+			assertNotNull(factory.jobParams.get("jobs").get("param2"));
+			assertEquals(DurationParamController.class, factory.jobParams.get("jobs").get("param2").getClass());
+			controller = (DurationParamController)factory.jobParams.get("jobs").get("param2");
+			assertEquals(600000l, controller.getDefault());
+			assertEquals(60000l, controller.getMinimum());
+			assertEquals(3600000l, controller.getMaximum());
+			assertTrue(controller.allowModification());
+			// test param3
+			assertTrue(factory.jobParams.get("jobs").containsKey("param3"));
+			assertNotNull(factory.jobParams.get("jobs").get("param3"));
+			assertEquals(DurationParamController.class, factory.jobParams.get("jobs").get("param3").getClass());
+			controller = (DurationParamController)factory.jobParams.get("jobs").get("param3");
+			assertEquals(86400000l, controller.getDefault());
+			assertNull(controller.getMinimum());
+			assertEquals(31536000000l, controller.getMaximum());
+			assertFalse(controller.allowModification());
+			
+		}catch(UWSException ue){
+			ue.printStackTrace(System.err);
+			fail("Unexpected error! See the console for a full stack trace.");
+		}
+	}
+
+}
diff --git a/test/uws/config/TestUWSConfiguration.java b/test/uws/config/TestUWSConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..92b6dc5cba2cddd13ec02bc987925e697cee9070
--- /dev/null
+++ b/test/uws/config/TestUWSConfiguration.java
@@ -0,0 +1,450 @@
+package uws.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static uws.config.UWSConfiguration.KEY_FILE_MANAGER;
+import static uws.config.UWSConfiguration.KEY_UWS_FACTORY;
+import static uws.config.UWSConfiguration.fetchClass;
+import static uws.config.UWSConfiguration.hasConstructor;
+import static uws.config.UWSConfiguration.isClassName;
+import static uws.config.UWSConfiguration.newInstance;
+import static uws.config.UWSConfiguration.parseLimit;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uws.ISO8601Format;
+import uws.UWSException;
+import uws.job.ErrorType;
+import uws.service.UWSFactory;
+
+public class TestUWSConfiguration {
+
+	@Before
+	public void setUp() throws Exception{}
+
+	/**
+	 * TEST isClassName(String):
+	 * 	- null, "", "{}", "an incorrect syntax" 				=> FALSE must be returned
+	 * 	- "{ }", "{ 	}", "{class.path}", "{ class.path	}" 	=> TRUE must be returned
+	 * 
+	 * @see ConfigurableUWSServlet#isClassName(String)
+	 */
+	@Test
+	public void testIsClassPath(){
+		// NULL and EMPTY:
+		assertFalse(isClassName(null));
+		assertFalse(isClassName(""));
+
+		// EMPTY CLASSPATH:
+		assertFalse(isClassName("{}"));
+
+		// INCORRECT CLASSPATH:
+		assertFalse(isClassName("incorrect class name ; missing {}"));
+
+		// VALID CLASSPATH:
+		assertTrue(isClassName("{class.path}"));
+
+		// CLASSPATH VALID ONLY IN THE SYNTAX:
+		assertTrue(isClassName("{ }"));
+		assertTrue(isClassName("{		}"));
+
+		// NOT TRIM CLASSPATH:
+		assertTrue(isClassName("{ class.name	}"));
+	}
+
+	/**
+	 * TEST getClass(String,String,Class):
+	 * 	- null, "", "{}", "an incorrect syntax", "{ }", "{ 	}" 						=> NULL must be returned
+	 * 	- "{java.lang.String}", "{ java.lang.String	}"								=> a valid DefaultServiceConnection must be returned
+	 * 	- "{mypackage.foo}", "{java.util.ArrayList}" (while a String is expected)	=> a UWSException must be thrown
+	 */
+	@Test
+	public void testGetClassStringStringClass(){
+		// NULL and EMPTY:
+		try{
+			assertNull(fetchClass(null, KEY_FILE_MANAGER, String.class));
+		}catch(UWSException e){
+			fail("If a NULL value is provided as class name: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e));
+		}
+		try{
+			assertNull(fetchClass("", KEY_FILE_MANAGER, String.class));
+		}catch(UWSException e){
+			fail("If an EMPTY value is provided as class name: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// EMPTY CLASS NAME:
+		try{
+			assertNull(fetchClass("{}", KEY_FILE_MANAGER, String.class));
+		}catch(UWSException e){
+			fail("If an EMPTY class name is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// INCORRECT SYNTAX:
+		try{
+			assertNull(fetchClass("incorrect class name ; missing {}", KEY_FILE_MANAGER, String.class));
+		}catch(UWSException e){
+			fail("If an incorrect class name is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// VALID CLASS NAME:
+		try{
+			Class<? extends String> classObject = fetchClass("{java.lang.String}", KEY_FILE_MANAGER, String.class);
+			assertNotNull(classObject);
+			assertEquals(classObject.getName(), "java.lang.String");
+		}catch(UWSException e){
+			fail("If a VALID class name is provided: getClass(...) MUST return a Class object of the wanted type!\nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// INCORRECT CLASS NAME:
+		try{
+			fetchClass("{mypackage.foo}", KEY_FILE_MANAGER, String.class);
+			fail("This MUST have failed because an incorrect class name is provided!");
+		}catch(UWSException e){
+			assertEquals(e.getClass(), UWSException.class);
+			assertEquals(e.getMessage(), "The class specified by the property \"" + KEY_FILE_MANAGER + "\" ({mypackage.foo}) can not be found.");
+		}
+
+		// INCOMPATIBLE TYPES:
+		try{
+			@SuppressWarnings("unused")
+			Class<? extends String> classObject = fetchClass("{java.util.ArrayList}", KEY_FILE_MANAGER, String.class);
+			fail("This MUST have failed because a class of a different type has been asked!");
+		}catch(UWSException e){
+			assertEquals(e.getClass(), UWSException.class);
+			assertEquals(e.getMessage(), "The class specified by the property \"" + KEY_FILE_MANAGER + "\" ({java.util.ArrayList}) is not implementing " + String.class.getName() + ".");
+		}
+
+		// CLASS NAME VALID ONLY IN THE SYNTAX:
+		try{
+			assertNull(fetchClass("{ }", KEY_FILE_MANAGER, String.class));
+		}catch(UWSException e){
+			fail("If an EMPTY class name is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e));
+		}
+		try{
+			assertNull(fetchClass("{		}", KEY_FILE_MANAGER, String.class));
+		}catch(UWSException e){
+			fail("If an EMPTY class name is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e));
+		}
+
+		// NOT TRIM CLASS NAME:
+		try{
+			Class<?> classObject = fetchClass("{ java.lang.String	}", KEY_FILE_MANAGER, String.class);
+			assertNotNull(classObject);
+			assertEquals(classObject.getName(), "java.lang.String");
+		}catch(UWSException e){
+			fail("If a VALID class name is provided: getClass(...) MUST return a Class object of the wanted type!\nCaught exception: " + getPertinentMessage(e));
+		}
+	}
+
+	/**
+	 * TEST hasConstructor(String,String,Class,Class[]):
+	 * 	(tests already performed by {@link #testGetClassStringStringClass()})
+	 * 	- null, "", "{}", "an incorrect syntax", "{ }", "{ 	}" 						=> must fail with a UWSException
+	 * 	- "{java.lang.String}", "{ java.lang.String	}"								=> a valid DefaultServiceConnection must be returned
+	 * 	- "{mypackage.foo}", "{java.util.ArrayList}" (while a String is expected)	=> a UWSException must be thrown
+	 * 	(new tests)
+	 * 	- if the specified constructor exists return <code>true</code>, else <code>false</code> must be returned.
+	 */
+	@Test
+	public void testHasConstructor(){
+		/* hasConstructor(...) must throw an exception if the specification of the class (1st and 3rd parameters)
+		 * is wrong. But that is performed by fetchClass(...) which is called at the beginning of the function
+		 * and is not surrounded by a try-catch. So all these tests are already done by testGetClassStringStringClass(). */
+
+		// With a missing list of parameters:
+		try{
+			assertTrue(hasConstructor("{java.lang.String}", "STRING", String.class, null));
+		}catch(UWSException te){
+			te.printStackTrace();
+			fail("\"No list of parameters\" MUST be interpreted as the specification of a constructor with no parameter! This test has failed.");
+		}
+
+		// With an empty list of parameters
+		try{
+			assertTrue(hasConstructor("{java.lang.String}", "STRING", String.class, new Class[0]));
+		}catch(UWSException te){
+			te.printStackTrace();
+			fail("\"An empty list of parameters\" MUST be interpreted as the specification of a constructor with no parameter! This test has failed.");
+		}
+
+		// With a wrong list of parameters - 1
+		try{
+			assertFalse(hasConstructor("{uws.config.ConfigurableUWSFactory}", KEY_UWS_FACTORY, UWSFactory.class, new Class[]{}));
+		}catch(UWSException te){
+			te.printStackTrace();
+			fail("ConfigurableUWSFactory does not have an empty constructor ; this test should have failed!");
+		}
+
+		// With a wrong list of parameters - 2
+		try{
+			assertFalse(hasConstructor("{uws.config.ConfigurableUWSFactory}", KEY_UWS_FACTORY, UWSFactory.class, new Class[]{String.class,String.class}));
+		}catch(UWSException te){
+			te.printStackTrace();
+			fail("ConfigurableUWSFactory does not have a constructor with 2 Strings as parameter ; this test should have failed!");
+		}
+
+		// With a good list of parameters - 1
+		try{
+			assertTrue(hasConstructor("{uws.config.ConfigurableUWSFactory}", KEY_UWS_FACTORY, UWSFactory.class, new Class[]{Properties.class}));
+		}catch(UWSException te){
+			te.printStackTrace();
+			fail("ConfigurableUWSFactory has a constructor with a Properties in parameter ; this test should have failed!");
+		}
+
+		// With a good list of parameters - 2
+		try{
+			assertTrue(hasConstructor("{java.lang.String}", "STRING", String.class, new Class[]{String.class}));
+		}catch(UWSException te){
+			te.printStackTrace();
+			fail("String has a constructor with a String as parameter ; this test should have failed!");
+		}
+	}
+
+	@Test
+	public void testNewInstance(){
+		// VALID CONSTRUCTOR with no parameters:
+		try{
+			ISO8601Format dateFormat = newInstance("{uws.ISO8601Format}", "dateFormat", ISO8601Format.class);
+			assertNotNull(dateFormat);
+			assertEquals("uws.ISO8601Format", dateFormat.getClass().getName());
+		}catch(Exception ex){
+			ex.printStackTrace();
+			fail("This test should have succeeded: the parameters of newInstance(...) are all valid.");
+		}
+
+		// VALID CONSTRUCTOR with some parameters:
+		try{
+			final int errorCode = 503;
+			final String message = "My super test exception.";
+			final ErrorType type = ErrorType.TRANSIENT;
+			UWSException exception = newInstance("{uws.UWSException}", "exception", UWSException.class, new Class<?>[]{int.class,String.class,ErrorType.class}, new Object[]{errorCode,message,type});
+			assertNotNull(exception);
+			assertEquals("uws.UWSException", exception.getClass().getName());
+			assertEquals(errorCode, exception.getHttpErrorCode());
+			assertEquals(message, exception.getMessage());
+			assertEquals(type, exception.getUWSErrorType());
+		}catch(Exception ex){
+			ex.printStackTrace();
+			fail("This test should have succeeded: the constructor UWSException(int,String,ErrorType) exists.");
+		}
+
+		// VALID CONSTRUCTOR with some parameters whose the type is an extension (not the exact type):
+		OutputStream output = null;
+		File tmp = new File("tmp.empty");
+		try{
+			output = newInstance("{java.io.BufferedOutputStream}", "stream", OutputStream.class, new Class<?>[]{OutputStream.class}, new OutputStream[]{new FileOutputStream(tmp)});
+			assertNotNull(output);
+			assertEquals(BufferedOutputStream.class, output.getClass());
+		}catch(Exception ex){
+			ex.printStackTrace();
+			fail("This test should have succeeded: the constructor TAPSchema(String,String,String) exists.");
+		}finally{
+			try{
+				tmp.delete();
+				if (output != null)
+					output.close();
+			}catch(IOException ioe){}
+		}
+
+		// NOT A CLASS NAME:
+		try{
+			newInstance("uws.ISO8601Format", "dateFormat", ISO8601Format.class);
+			fail("This MUST have failed because the property value is not a class name!");
+		}catch(Exception ex){
+			assertEquals(UWSException.class, ex.getClass());
+			assertEquals("Class name expected for the property \"dateFormat\" instead of: \"uws.ISO8601Format\"! The specified class must extend/implement uws.ISO8601Format.", ex.getMessage());
+		}
+
+		// NO MATCHING CONSTRUCTOR:
+		try{
+			newInstance("{uws.UWSException}", "exception", UWSException.class, new Class<?>[]{String.class,String.class}, new Object[]{"foo","bar"});
+			fail("This MUST have failed because the specified class does not have any expected constructor!");
+		}catch(Exception ex){
+			assertEquals(UWSException.class, ex.getClass());
+			assertEquals("Missing constructor uws.UWSException(java.lang.String, java.lang.String)! See the value \"{uws.UWSException}\" of the property \"exception\".", ex.getMessage());
+		}
+
+		// VALID CONSTRUCTOR with primitive type:
+		try{
+			ClassWithAPrimitiveConstructor aClass = newInstance("{uws.config.TestUWSConfiguration$ClassWithAPrimitiveConstructor}", "aClass", ClassWithAPrimitiveConstructor.class, new Class<?>[]{int.class}, new Object[]{123});
+			assertNotNull(aClass);
+			assertEquals(ClassWithAPrimitiveConstructor.class, aClass.getClass());
+			assertEquals(123, aClass.myParam);
+			aClass = newInstance("{uws.config.TestUWSConfiguration$ClassWithAPrimitiveConstructor}", "aClass", ClassWithAPrimitiveConstructor.class, new Class<?>[]{int.class}, new Object[]{new Integer(123)});
+			assertNotNull(aClass);
+			assertEquals(ClassWithAPrimitiveConstructor.class, aClass.getClass());
+			assertEquals(123, aClass.myParam);
+		}catch(Exception ex){
+			ex.printStackTrace();
+			fail("This test should have succeeded: the constructor ClassWithAPrimitiveConstructor(int) exists.");
+		}
+
+		// WRONG CONSTRUCTOR with primitive type:
+		try{
+			newInstance("{uws.config.TestUWSConfiguration$ClassWithAPrimitiveConstructor}", "aClass", ClassWithAPrimitiveConstructor.class, new Class<?>[]{Integer.class}, new Object[]{new Integer(123)});
+			fail("This MUST have failed because the constructor of the specified class expects an int, not an java.lang.Integer!");
+		}catch(Exception ex){
+			assertEquals(UWSException.class, ex.getClass());
+			assertEquals("Missing constructor uws.config.TestUWSConfiguration$ClassWithAPrimitiveConstructor(java.lang.Integer)! See the value \"{uws.config.TestUWSConfiguration$ClassWithAPrimitiveConstructor}\" of the property \"aClass\".", ex.getMessage());
+		}
+
+		// THE CONSTRUCTOR THROWS A UWSException:
+		try{
+			newInstance("{uws.config.TestUWSConfiguration$ClassAlwaysThrowUWSError}", "uwsError", ClassAlwaysThrowUWSError.class);
+			fail("This MUST have failed because the constructor of the specified class throws a UWSException!");
+		}catch(Exception ex){
+			assertEquals(UWSException.class, ex.getClass());
+			assertEquals("This error is always thrown by ClassAlwaysThrowUWSError ^^", ex.getMessage());
+		}
+	}
+
+	/**
+	 * TEST parseLimit(String,String):
+	 * 	- nothing, -123, 0				=> -1
+	 * 	- 20, 20B, 20 B					=> 20
+	 * 	- 100kB, 100 k B				=> 100000
+	 * 	- 100MB, 1 0 0MB				=> 100000000
+	 * 	- 100GB, 1 0 0 G B				=> 100000000000
+	 * 	- B								=> -1
+	 * 	- kB							=> -1
+	 * 	- foo, 100b, 100TB, 1foo, 20r	=> an exception must occur
+	 */
+	@Test
+	public void testParseLimitStringString(){
+		final String propertyName = "LIMIT_PROPERTY"; // TODO Change the string of the propertyName variable
+		// Test empty or negative or null values => OK!
+		try{
+			String[] testValues = new String[]{null,"","  	 ","-123"};
+			long limit;
+			for(String v : testValues){
+				limit = parseLimit(v, propertyName);
+				assertEquals(limit, -1);
+			}
+			// 0 test:
+			limit = parseLimit("0", propertyName);
+			assertEquals(limit, 0);
+		}catch(UWSException te){
+			fail("All these empty limit values are valid, so these tests should have succeeded!\nCaught exception: " + getPertinentMessage(te));
+		}
+
+		// Test all accepted bytes values:
+		try{
+			String[] testValues = new String[]{"20","20B","20 B"};
+			long limit;
+			for(String v : testValues){
+				limit = parseLimit(v, propertyName);
+				assertEquals(limit, 20);
+			}
+		}catch(UWSException te){
+			te.printStackTrace();
+			fail("All these bytes limit values are valid, so these tests should have succeeded!\nCaught exception: " + getPertinentMessage(te));
+		}
+
+		// Test all accepted kilo-bytes values:
+		try{
+			String[] testValues = new String[]{"100kB","100 k B"};
+			long limit;
+			for(String v : testValues){
+				limit = parseLimit(v, propertyName);
+				assertEquals(limit, 100000);
+			}
+		}catch(UWSException te){
+			fail("All these kilo-bytes limit values are valid, so these tests should have succeeded!\nCaught exception: " + getPertinentMessage(te));
+		}
+
+		// Test all accepted mega-bytes values:
+		try{
+			String[] testValues = new String[]{"100MB","1 0 0MB"};
+			long limit;
+			for(String v : testValues){
+				limit = parseLimit(v, propertyName);
+				assertEquals(limit, 100000000);
+			}
+		}catch(UWSException te){
+			fail("All these mega-bytes limit values are valid, so these tests should have succeeded!\nCaught exception: " + getPertinentMessage(te));
+		}
+
+		// Test all accepted giga-bytes values:
+		try{
+			String[] testValues = new String[]{"100GB","1 0 0 G B"};
+			long limit;
+			for(String v : testValues){
+				limit = parseLimit(v, propertyName);
+				assertEquals(limit, 100000000000l);
+			}
+		}catch(UWSException te){
+			fail("All these giga-bytes limit values are valid, so these tests should have succeeded!\nCaught exception: " + getPertinentMessage(te));
+		}
+
+		// Test with only the BYTES unit provided:
+		try{
+			long limit = parseLimit("B", propertyName);
+			assertEquals(limit, -1);
+		}catch(UWSException te){
+			fail("Providing only the ROWS unit is valid, so this test should have succeeded!\nCaught exception: " + getPertinentMessage(te));
+		}
+
+		// Test with only the KILO BYTES unit provided:
+		try{
+			long limit = parseLimit("kB", propertyName);
+			assertEquals(limit, -1);
+		}catch(UWSException te){
+			fail("Providing only the BYTES unit is valid, so this test should have succeeded!\nCaught exception: " + getPertinentMessage(te));
+		}
+
+		// Test with incorrect limit formats:
+		String[] values = new String[]{"","100","100","1"};
+		String[] unitPart = new String[]{"foo","b","TB","foo"};
+		for(int i = 0; i < values.length; i++){
+			try{
+				parseLimit(values[i] + unitPart[i], propertyName);
+				fail("This test should have failed because an incorrect limit is provided: \"" + values[i] + unitPart[i] + "\"!");
+			}catch(UWSException te){
+				assertEquals(te.getClass(), UWSException.class);
+				assertEquals(te.getMessage(), "Unknown limit unit (" + unitPart[i] + ") for the property " + propertyName + ": \"" + values[i] + unitPart[i] + "\"!");
+
+			}
+		}
+		// Test with an incorrect numeric limit value:
+		try{
+			parseLimit("abc100b", propertyName);
+			fail("This test should have failed because an incorrect limit is provided: \"abc100b\"!");
+		}catch(UWSException te){
+			assertEquals(te.getClass(), UWSException.class);
+			assertEquals(te.getMessage(), "Integer expected for the property " + propertyName + " for the substring \"abc100\" of the whole value: \"abc100b\"!");
+		}
+	}
+
+	public static final String getPertinentMessage(final Exception ex){
+		return (ex.getCause() == null || ex.getMessage().equals(ex.getCause().getMessage())) ? ex.getMessage() : ex.getCause().getMessage();
+	}
+
+	private static class ClassAlwaysThrowUWSError {
+		@SuppressWarnings("unused")
+		public ClassAlwaysThrowUWSError() throws UWSException{
+			throw new UWSException("This error is always thrown by ClassAlwaysThrowUWSError ^^");
+		}
+	}
+
+	private static class ClassWithAPrimitiveConstructor {
+		private final int myParam;
+
+		@SuppressWarnings("unused")
+		public ClassWithAPrimitiveConstructor(int aParam) throws UWSException{
+			myParam = aParam;
+		}
+	}
+
+}
diff --git a/test/uws/job/parameters/TestDurationParamController.java b/test/uws/job/parameters/TestDurationParamController.java
new file mode 100644
index 0000000000000000000000000000000000000000..289c0a07c9bfe84da109607b19588949b26982c4
--- /dev/null
+++ b/test/uws/job/parameters/TestDurationParamController.java
@@ -0,0 +1,182 @@
+package uws.job.parameters;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.text.ParseException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestDurationParamController {
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@Test
+	public void testCheck(){
+		// min < value < max => value (in ms):
+		DurationParamController controller = new DurationParamController();
+		try{
+			controller.reset(10, 5, controller.parseDuration("1M"));
+			assertEquals(23l, controller.check(23));
+			assertEquals(23l, controller.check("23"));
+			assertEquals(1987200000l, controller.check("23D"));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// value < min < max => min:
+		controller = new DurationParamController();
+		try{
+			controller.reset(10, 5, controller.parseDuration("1M"));
+			assertEquals(5l, controller.check(2));
+			assertEquals(5l, controller.check("5"));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// value > max => max:
+		controller = new DurationParamController();
+		try{
+			controller.reset(10, 5, controller.parseDuration("1M"));
+			assertEquals(2592000000l, controller.check(31536000000l));
+			assertEquals(2592000000l, controller.check("31536000000"));
+			assertEquals(2592000000l, controller.check("2 months"));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// NULL value => default
+		controller = new DurationParamController();
+		try{
+			controller.reset(10, 5, controller.parseDuration("1M"));
+			assertEquals(10l, controller.check(null));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+	}
+
+	@Test
+	public void testToStringLong(){
+		DurationParamController controller = new DurationParamController();
+		try{
+			assertEquals(0, controller.toString(null).length());
+			assertEquals("0ms", controller.toString(0l));
+			assertEquals("23ms", controller.toString(23l));
+			assertEquals("1s", controller.toString(1000l));
+			assertEquals("60001ms", controller.toString(60001l));
+			assertEquals("1m", controller.toString(60000l));
+			assertEquals("1D", controller.toString(controller.parseDuration("24 hours")));
+			assertEquals("23h", controller.toString(controller.parseDuration("23 hours")));
+			assertEquals("23D", controller.toString(controller.parseDuration("23 days")));
+			assertEquals("2W", controller.toString(controller.parseDuration("14 days")));
+			assertEquals("23W", controller.toString(controller.parseDuration("23 weeks")));
+			assertEquals("1M", controller.toString(controller.parseDuration("30 days")));
+			assertEquals("23M", controller.toString(controller.parseDuration("23 months")));
+			assertEquals("1Y", controller.toString(controller.parseDuration("365 days")));
+			assertEquals("23Y", controller.toString(controller.parseDuration("23 years")));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+	}
+
+	@Test
+	public void testParseDuration(){
+		// NULL => -1
+		DurationParamController controller = new DurationParamController();
+		try{
+			assertEquals(-1, controller.parseDuration(null));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// Negative duration:
+		controller = new DurationParamController();
+		try{
+			assertEquals(-1, controller.parseDuration("-23"));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// Duration without unit:
+		controller = new DurationParamController();
+		try{
+			assertEquals(23, controller.parseDuration("23"));
+			assertEquals(23, controller.parseDuration("  23"));
+			assertEquals(23, controller.parseDuration("23 	"));
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// Duration with unit:
+		controller = new DurationParamController();
+		try{
+			// MILLISECONDS
+			String[] units = new String[]{"ms","milliseconds"," ms"," milliseconds  "};
+			for(String u : units)
+				assertEquals(23, controller.parseDuration("23" + u));
+
+			// SECONDS
+			units = new String[]{"s","sec","seconds"," s","sec  "," seconds  "};
+			for(String u : units)
+				assertEquals(23000, controller.parseDuration("23" + u));
+
+			// MINUTES
+			units = new String[]{"m","min","minutes"," m","min  "," minutes  "};
+			for(String u : units)
+				assertEquals(1380000, controller.parseDuration("23" + u));
+
+			// HOURS
+			units = new String[]{"h","hours"," h"," hours  "};
+			for(String u : units)
+				assertEquals(82800000, controller.parseDuration("23" + u));
+
+			// DAYS
+			units = new String[]{"D","days"," D"," days  "};
+			for(String u : units)
+				assertEquals(1987200000, controller.parseDuration("23" + u));
+
+			// WEEKS
+			units = new String[]{"W","weeks"," W"," weeks  "};
+			for(String u : units)
+				assertEquals(13910400000l, controller.parseDuration("23" + u));
+
+			// MONTHS
+			units = new String[]{"M","months"," M"," months  "};
+			for(String u : units)
+				assertEquals(59616000000l, controller.parseDuration("23" + u));
+
+			// YEARS
+			units = new String[]{"Y","years"," Y"," years  "};
+			for(String u : units)
+				assertEquals(725328000000l, controller.parseDuration("23" + u));
+
+		}catch(Exception e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// Duration with an unknown unit:
+		controller = new DurationParamController();
+		String[] units = new String[]{"MS","Seconds","H","foo"};
+		for(String u : units){
+			try{
+				controller.parseDuration("23" + u);
+				fail("This unit (" + u + ") is not supported! An error should have been thrown.");
+			}catch(Exception e){
+				assertEquals(ParseException.class, e.getClass());
+				assertEquals("Unexpected format for a duration: \"23" + u + "\"! Cause: it does not match the following Regular Expression: " + DurationParamController.PATTERN_DURATION.pattern(), e.getMessage());
+			}
+		}
+	}
+
+}
diff --git a/test/uws/job/parameters/TestNumericParamController.java b/test/uws/job/parameters/TestNumericParamController.java
new file mode 100644
index 0000000000000000000000000000000000000000..03c7410eef9b250250c060baa2ef745a23a781e7
--- /dev/null
+++ b/test/uws/job/parameters/TestNumericParamController.java
@@ -0,0 +1,238 @@
+package uws.job.parameters;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uws.UWSException;
+
+public class TestNumericParamController {
+
+	@Before
+	public void setUp() throws Exception{}
+
+	@Test
+	public void testSetMinimum(){
+		// Just a minimum value => no problem to change it:
+		NumericParamController controller = new NumericParamController();
+		controller.setMinimum(-23);
+		assertEquals(-23, controller.getMinimum().intValue());
+		controller.setMinimum(0);
+		assertEquals(0, controller.getMinimum().intValue());
+
+		// But if there is a maximum...
+		// either the new minimum is less than the maximum => ok
+		controller = new NumericParamController();
+		controller.setMaximum(100);
+		controller.setMinimum(20);
+		assertEquals(20, controller.getMinimum());
+		// or it is bigger => it must be set to the maximum:
+		controller = new NumericParamController();
+		controller.setMaximum(10);
+		controller.setMinimum(20);
+		assertEquals(10, controller.getMinimum());
+
+		// If there is a default value and this value is smaller than the new minimum => default = min
+		controller = new NumericParamController();
+		controller.setDefault(10);
+		controller.setMaximum(50);
+		assertNull(controller.getMinimum());
+		assertEquals(50, controller.getMaximum());
+		assertEquals(10, controller.getDefault());
+		controller.setMinimum(20);
+		assertEquals(20, controller.getMinimum());
+		assertEquals(50, controller.getMaximum());
+		assertEquals(20, controller.getDefault());
+
+		// NULL given => NULL set:
+		controller = new NumericParamController();
+		controller.setMinimum(null);
+		assertNull(controller.getMinimum());
+		controller.setMinimum(23);
+		assertEquals(23, controller.getMinimum());
+		controller.setMinimum(null);
+		assertNull(controller.getMinimum());
+	}
+
+	@Test
+	public void testSetMaximum(){
+		// Just a minimum value => no problem to change it:
+		NumericParamController controller = new NumericParamController();
+		controller.setMaximum(23);
+		assertEquals(23, controller.getMaximum().intValue());
+		controller.setMaximum(0);
+		assertEquals(0, controller.getMaximum().intValue());
+
+		// But if there is a minimum...
+		// either the new maximum is bigger than the minimum => ok
+		controller = new NumericParamController();
+		controller.setMinimum(10);
+		controller.setMaximum(20);
+		assertEquals(20, controller.getMaximum());
+		// or it is smaller => it must be set to the minimum:
+		controller = new NumericParamController();
+		controller.setMinimum(20);
+		controller.setMaximum(0);
+		assertEquals(20, controller.getMaximum());
+
+		// If there is a default value and this value is bigger than the new maximum => default = max
+		controller = new NumericParamController();
+		controller.setDefault(50);
+		controller.setMinimum(20);
+		assertEquals(20, controller.getMinimum());
+		assertNull(controller.getMaximum());
+		assertEquals(50, controller.getDefault());
+		controller.setMaximum(30);
+		assertEquals(20, controller.getMinimum());
+		assertEquals(30, controller.getMaximum());
+		assertEquals(30, controller.getDefault());
+
+		// NULL given => NULL set:
+		controller = new NumericParamController();
+		controller.setMaximum(null);
+		assertNull(controller.getMaximum());
+		controller.setMaximum(23);
+		assertEquals(23, controller.getMaximum());
+		controller.setMaximum(null);
+		assertNull(controller.getMaximum());
+	}
+
+	@Test
+	public void testSetDefault(){
+		// No limit => everything is allowed for the default value:
+		NumericParamController controller = new NumericParamController();
+		controller.setDefault(23);
+		assertEquals(23, ((Number)controller.getDefault()).intValue());
+		controller.setDefault(-123);
+		assertEquals(-123, ((Number)controller.getDefault()).intValue());
+
+		// If there is a maximum...
+		// either default is smaller (or equals) => ok
+		controller = new NumericParamController();
+		controller.setMaximum(50);
+		controller.setDefault(20);
+		assertEquals(20, ((Number)controller.getDefault()).intValue());
+		controller.setDefault(50);
+		assertEquals(50, ((Number)controller.getDefault()).intValue());
+		controller.setDefault(-123);
+		assertEquals(-123, ((Number)controller.getDefault()).intValue());
+		// or default is bigger => default = max
+		controller = new NumericParamController();
+		controller.setMaximum(50);
+		controller.setDefault(70);
+		assertEquals(50, ((Number)controller.getDefault()).intValue());
+
+		// If there is a minimum...
+		// either default is bigger (or equals) => ok
+		controller = new NumericParamController();
+		controller.setMinimum(10);
+		controller.setDefault(20);
+		assertEquals(20, ((Number)controller.getDefault()).intValue());
+		controller.setDefault(10);
+		assertEquals(10, ((Number)controller.getDefault()).intValue());
+		controller.setDefault(123);
+		assertEquals(123, ((Number)controller.getDefault()).intValue());
+		// or default is smaller => default = min
+		controller = new NumericParamController();
+		controller.setMinimum(10);
+		controller.setDefault(0);
+		assertEquals(10, ((Number)controller.getDefault()).intValue());
+
+		// NULL given => NULL set:
+		controller = new NumericParamController();
+		controller.setDefault(null);
+		assertNull(controller.getDefault());
+		controller.setDefault(23);
+		assertEquals(23, controller.getDefault());
+		controller.setDefault(null);
+		assertNull(controller.getDefault());
+	}
+
+	@Test
+	public void testReset(){
+		// Just a minimum:
+		NumericParamController controller = new NumericParamController();
+		controller.reset(null, 23, null);
+		assertNull(controller.getDefault());
+		assertNull(controller.getMaximum());
+		assertEquals(23, controller.getMinimum());
+
+		// Just a maximum:
+		controller.reset(null, null, 23);
+		assertNull(controller.getDefault());
+		assertNull(controller.getMinimum());
+		assertEquals(23, controller.getMaximum());
+
+		// Just a default:
+		controller.reset(23, null, null);
+		assertNull(controller.getMinimum());
+		assertNull(controller.getMaximum());
+		assertEquals(23, controller.getDefault());
+
+		// min < default < max
+		controller.reset(20, 10, 30);
+		assertEquals(10, controller.getMinimum());
+		assertEquals(30, controller.getMaximum());
+		assertEquals(20, controller.getDefault());
+
+		// default < min < max
+		controller.reset(0, 10, 30);
+		assertEquals(10, controller.getMinimum());
+		assertEquals(30, controller.getMaximum());
+		assertEquals(10, controller.getDefault());
+
+		// default > max > min
+		controller.reset(50, 10, 30);
+		assertEquals(10, controller.getMinimum());
+		assertEquals(30, controller.getMaximum());
+		assertEquals(30, controller.getDefault());
+	}
+
+	@Test
+	public void testCheck(){
+		// min < value < max  => value
+		NumericParamController controller = new NumericParamController();
+		controller.reset(20, 10, 30);
+		try{
+			assertEquals(15, controller.check(15));
+		}catch(UWSException e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// No value => default
+		controller = new NumericParamController();
+		controller.reset(20, 10, 30);
+		try{
+			assertEquals(20, controller.check(null));
+		}catch(UWSException e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// value < min => min
+		controller = new NumericParamController();
+		controller.reset(20, 10, 30);
+		try{
+			assertEquals(10, controller.check(0));
+		}catch(UWSException e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+		// value > max => max
+		controller = new NumericParamController();
+		controller.reset(20, 10, 30);
+		try{
+			assertEquals(30, controller.check(50));
+		}catch(UWSException e){
+			e.printStackTrace(System.err);
+			fail("Unexpected error! See the console for more details about the error.");
+		}
+
+	}
+
+}