Skip to content
Snippets Groups Projects
Commit ded9cead authored by Grégory Mantelet's avatar Grégory Mantelet
Browse files

[UWS] Fix recurrent ConcurrentModificationException during service overload.

This error occurred generally during the backup process while trying to
backup the job list of a specific user. If several of his jobs were running
and changing state during the backup process, this
ConcurrentModificationException was thrown. This generally happens when the same
user submits a lot of shorts jobs in the same time.

This exception was due to a non thread-safe usage of
UWSParameters.additionalParams. To fix this issue, instead of creating it as a
normal HashMap, it is now created as a ConcurrentHashMap.

The same modification has also been applied to UWSParameters.params. In addition
of the replacement of HashMap into ConcurrentHashMap, all `synchronized` blocks
have been removed....there should not be needed any more.
parent 1f4bc6b1
No related branches found
No related tags found
No related merge requests found
...@@ -2,21 +2,21 @@ package uws.job.parameters; ...@@ -2,21 +2,21 @@ package uws.job.parameters;
/* /*
* This file is part of UWSLibrary. * This file is part of UWSLibrary.
* *
* UWSLibrary is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UWSLibrary is distributed in the hope that it will be useful, * UWSLibrary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>.
* *
* Copyright 2012-2017 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Copyright 2012-2018 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
* Astronomisches Rechen Institut (ARI) * Astronomisches Rechen Institut (ARI)
*/ */
...@@ -32,6 +32,7 @@ import java.util.List; ...@@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -43,7 +44,7 @@ import uws.service.request.UploadFile; ...@@ -43,7 +44,7 @@ import uws.service.request.UploadFile;
/** /**
* <p>Let extracting all UWS standard and non-standard parameters from a map.</p> * <p>Let extracting all UWS standard and non-standard parameters from a map.</p>
* *
* <h3>Input parameter check</h3> * <h3>Input parameter check</h3>
* <p> * <p>
* It is possible to check the value of some or all parameters by calling the function {@link InputParamController#check(Object)} * It is possible to check the value of some or all parameters by calling the function {@link InputParamController#check(Object)}
...@@ -51,7 +52,7 @@ import uws.service.request.UploadFile; ...@@ -51,7 +52,7 @@ import uws.service.request.UploadFile;
* provided at the creation of a {@link UWSParameters}. If none are given, default ones are used (see {@link #getDefaultControllers()}) * provided at the creation of a {@link UWSParameters}. If none are given, default ones are used (see {@link #getDefaultControllers()})
* for the standard UWS parameters (e.g. destruction time, duration, etc...). * for the standard UWS parameters (e.g. destruction time, duration, etc...).
* </p> * </p>
* *
* <h3>Default value</h3> * <h3>Default value</h3>
* <p> * <p>
* By calling the function {@link #init()}, you set a default value to any parameter which has an {@link InputParamController} * By calling the function {@link #init()}, you set a default value to any parameter which has an {@link InputParamController}
...@@ -61,7 +62,7 @@ import uws.service.request.UploadFile; ...@@ -61,7 +62,7 @@ import uws.service.request.UploadFile;
* The function {@link InputParamController#getDefault()} returns a default value for its associated parameter. * The function {@link InputParamController#getDefault()} returns a default value for its associated parameter.
* This value must be obviously different from <i>NULL</i>. * This value must be obviously different from <i>NULL</i>.
* </p> * </p>
* *
* <h3>Updating a {@link UWSParameters}</h3> * <h3>Updating a {@link UWSParameters}</h3>
* <p> * <p>
* It is possible to update a {@link UWSParameters} with another {@link UWSParameters} thanks to the function * It is possible to update a {@link UWSParameters} with another {@link UWSParameters} thanks to the function
...@@ -73,7 +74,7 @@ import uws.service.request.UploadFile; ...@@ -73,7 +74,7 @@ import uws.service.request.UploadFile;
* {@link InputParamController} with them and override the function {@link InputParamController#allowModification()} * {@link InputParamController} with them and override the function {@link InputParamController#allowModification()}
* so that it returns <i>false</i>. * so that it returns <i>false</i>.
* </p> * </p>
* *
* <h3>Case sensitivity</h3> * <h3>Case sensitivity</h3>
* <p> * <p>
* All UWS STANDARD parameters can be provided in any case: they will always be identified and updated. * All UWS STANDARD parameters can be provided in any case: they will always be identified and updated.
...@@ -85,7 +86,7 @@ import uws.service.request.UploadFile; ...@@ -85,7 +86,7 @@ import uws.service.request.UploadFile;
* of all the additional parameters you are expected at the creation: see {@link #UWSParameters(HttpServletRequest, Collection, Map)} * of all the additional parameters you are expected at the creation: see {@link #UWSParameters(HttpServletRequest, Collection, Map)}
* and {@link #UWSParameters(Map, Collection, Map)}. * and {@link #UWSParameters(Map, Collection, Map)}.
* </p> * </p>
* *
* <h3>Additional parameters case normalization</h3> * <h3>Additional parameters case normalization</h3>
* <p> * <p>
* Indeed, the second parameter of these constructors (if != NULL) is used to normalize the name of the additional parameters so * Indeed, the second parameter of these constructors (if != NULL) is used to normalize the name of the additional parameters so
...@@ -102,7 +103,7 @@ import uws.service.request.UploadFile; ...@@ -102,7 +103,7 @@ import uws.service.request.UploadFile;
* <li><u>With "FOO" in the second parameter of the constructor:</u> {@link #get(String) get("FOO")} will return something if in the request there was a parameter named: "foo", "FOO", "Foo", ...</li> * <li><u>With "FOO" in the second parameter of the constructor:</u> {@link #get(String) get("FOO")} will return something if in the request there was a parameter named: "foo", "FOO", "Foo", ...</li>
* <li><u>If the second parameter is empty, NULL or does not contain "FOO":</u> {@link #get(String) get("FOO")} will return something if in the request there was a parameter named exactly "FOO".</li> * <li><u>If the second parameter is empty, NULL or does not contain "FOO":</u> {@link #get(String) get("FOO")} will return something if in the request there was a parameter named exactly "FOO".</li>
* </ul> * </ul>
* *
* <h3>UWS standard parameters</h3> * <h3>UWS standard parameters</h3>
* <p>All UWS standard parameters are identified in this class. However, only READ/WRITE parameters are kept. * <p>All UWS standard parameters are identified in this class. However, only READ/WRITE parameters are kept.
* All the others are ignored. The read/write UWS standard parameters are:</p> * All the others are ignored. The read/write UWS standard parameters are:</p>
...@@ -114,17 +115,17 @@ import uws.service.request.UploadFile; ...@@ -114,17 +115,17 @@ import uws.service.request.UploadFile;
* <p><i><u>note 1:</u> All parameters stored under the parameter {@link UWSJob#PARAM_PARAMETERS} (that's to say, additional parameters) * <p><i><u>note 1:</u> All parameters stored under the parameter {@link UWSJob#PARAM_PARAMETERS} (that's to say, additional parameters)
* are also considered as READ/WRITE parameters !</i></p> * are also considered as READ/WRITE parameters !</i></p>
* <p><i><u>note 2:</u> If several values have been submitted for the same UWS standard parameter, just the last occurrence is taken into account.</i></p> * <p><i><u>note 2:</u> If several values have been submitted for the same UWS standard parameter, just the last occurrence is taken into account.</i></p>
* *
* @author Gr&eacute;gory Mantelet (CDS;ARI) * @author Gr&eacute;gory Mantelet (CDS;ARI)
* @version 4.2 (09/2017) * @version 4.4 (07/2018)
*/ */
public class UWSParameters implements Iterable<Entry<String,Object>> { public class UWSParameters implements Iterable<Entry<String, Object>> {
/** /**
* <p>Read-Write parameters.</p> * <p>Read-Write parameters.</p>
* <p>Names of the UWS parameters whose the value can be modified by the user.</p> * <p>Names of the UWS parameters whose the value can be modified by the user.</p>
*/ */
protected final static String[] UWS_RW_PARAMETERS = new String[]{UWSJob.PARAM_PHASE,UWSJob.PARAM_RUN_ID,UWSJob.PARAM_EXECUTION_DURATION,UWSJob.PARAM_DESTRUCTION_TIME,UWSJob.PARAM_PARAMETERS}; protected final static String[] UWS_RW_PARAMETERS = new String[]{ UWSJob.PARAM_PHASE, UWSJob.PARAM_RUN_ID, UWSJob.PARAM_EXECUTION_DURATION, UWSJob.PARAM_DESTRUCTION_TIME, UWSJob.PARAM_PARAMETERS };
/** Regular expression allowing to test which UWS parameters can be set. Actually, only: phase, runID, executionduration and destruction. */ /** Regular expression allowing to test which UWS parameters can be set. Actually, only: phase, runID, executionduration and destruction. */
public final static String UWS_RW_PARAMETERS_REGEXP = ("(" + UWSJob.PARAM_PHASE + "|" + UWSJob.PARAM_RUN_ID + "|" + UWSJob.PARAM_EXECUTION_DURATION + "|" + UWSJob.PARAM_DESTRUCTION_TIME + ")").toLowerCase(); public final static String UWS_RW_PARAMETERS_REGEXP = ("(" + UWSJob.PARAM_PHASE + "|" + UWSJob.PARAM_RUN_ID + "|" + UWSJob.PARAM_EXECUTION_DURATION + "|" + UWSJob.PARAM_DESTRUCTION_TIME + ")").toLowerCase();
...@@ -133,12 +134,12 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -133,12 +134,12 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
* <p>Read-Only parameters.</p> * <p>Read-Only parameters.</p>
* <p>Names of the UWS parameters whose the value can NOT be modified by the user. These value are not kept. They are only ignored.</p> * <p>Names of the UWS parameters whose the value can NOT be modified by the user. These value are not kept. They are only ignored.</p>
*/ */
protected final static String[] UWS_RO_PARAMETERS = new String[]{UWSJob.PARAM_JOB_ID,UWSJob.PARAM_OWNER,UWSJob.PARAM_QUOTE,UWSJob.PARAM_START_TIME,UWSJob.PARAM_END_TIME,UWSJob.PARAM_RESULTS,UWSJob.PARAM_ERROR_SUMMARY}; protected final static String[] UWS_RO_PARAMETERS = new String[]{ UWSJob.PARAM_JOB_ID, UWSJob.PARAM_OWNER, UWSJob.PARAM_QUOTE, UWSJob.PARAM_START_TIME, UWSJob.PARAM_END_TIME, UWSJob.PARAM_RESULTS, UWSJob.PARAM_ERROR_SUMMARY };
/** /**
* List of all extracted parameters. * List of all extracted parameters.
*/ */
protected final Map<String,Object> params = new HashMap<String,Object>(10); protected final Map<String, Object> params = new ConcurrentHashMap<String, Object>(10);
/** /**
* <p>List of all UWS additional parameters.</p> * <p>List of all UWS additional parameters.</p>
...@@ -146,7 +147,7 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -146,7 +147,7 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
* It is deleted (set to NULL) when there is a modification in the list of all parameters * It is deleted (set to NULL) when there is a modification in the list of all parameters
* (so in the function {@link #set(String, Object)}, {@link #update(UWSParameters)} and {@link #init()}).</i></p> * (so in the function {@link #set(String, Object)}, {@link #update(UWSParameters)} and {@link #init()}).</i></p>
*/ */
private Map<String,Object> additionalParams = null; private Map<String, Object> additionalParams = null;
/** /**
* List of all uploaded files among the whole set of parameters. * List of all uploaded files among the whole set of parameters.
...@@ -162,11 +163,11 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -162,11 +163,11 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* List of the controllers of all the input parameters. * List of the controllers of all the input parameters.
*/ */
protected final Map<String,InputParamController> mapParamControllers; protected final Map<String, InputParamController> mapParamControllers;
/** /**
* Builds an empty list of UWS parameters. * Builds an empty list of UWS parameters.
* *
* @see #UWSParameters(Collection, Map) * @see #UWSParameters(Collection, Map)
*/ */
public UWSParameters(){ public UWSParameters(){
...@@ -175,18 +176,18 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -175,18 +176,18 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Builds an empty list of UWS parameters.</p> * <p>Builds an empty list of UWS parameters.</p>
* *
* <p><i><u>note:</u> Even if no controllers is provided, this constructor sets the default * <p><i><u>note:</u> Even if no controllers is provided, this constructor sets the default
* input parameter controllers (see {@link #getDefaultControllers()}).</i></p> * input parameter controllers (see {@link #getDefaultControllers()}).</i></p>
* *
* @param expectedAdditionalParams The names of all expected additional parameters (MAY BE NULL). * @param expectedAdditionalParams The names of all expected additional parameters (MAY BE NULL).
* <i><u>note:</u> they will be identified with no case sensitivity * <i><u>note:</u> they will be identified with no case sensitivity
* and stored with the same case as in this collection.</i> * and stored with the same case as in this collection.</i>
* @param inputParamControllers Controllers of the input parameters (MAY BE NULL). * @param inputParamControllers Controllers of the input parameters (MAY BE NULL).
* *
* @see #getDefaultControllers() * @see #getDefaultControllers()
*/ */
public UWSParameters(final Collection<String> expectedAdditionalParams, final Map<String,InputParamController> inputParamControllers){ public UWSParameters(final Collection<String> expectedAdditionalParams, final Map<String, InputParamController> inputParamControllers){
// Set the input parameter controllers: // Set the input parameter controllers:
mapParamControllers = getDefaultControllers(); mapParamControllers = getDefaultControllers();
if (inputParamControllers != null) if (inputParamControllers != null)
...@@ -198,13 +199,13 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -198,13 +199,13 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Extracts and identifies all UWS standard and non-standard parameters from the given {@link HttpServletRequest}.</p> * <p>Extracts and identifies all UWS standard and non-standard parameters from the given {@link HttpServletRequest}.</p>
* *
* <p><i><u>note:</u> The default input parameter controllers are set by default (see {@link #getDefaultControllers()}).</i></p> * <p><i><u>note:</u> The default input parameter controllers are set by default (see {@link #getDefaultControllers()}).</i></p>
* *
* @param request The request to parse to extract the parameters. * @param request The request to parse to extract the parameters.
* *
* @throws UWSException If one of the given parameter is incorrect or badly formatted. * @throws UWSException If one of the given parameter is incorrect or badly formatted.
* *
* @see #UWSParameters(HttpServletRequest, Collection, Map) * @see #UWSParameters(HttpServletRequest, Collection, Map)
*/ */
public UWSParameters(final HttpServletRequest request) throws UWSException{ public UWSParameters(final HttpServletRequest request) throws UWSException{
...@@ -213,42 +214,42 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -213,42 +214,42 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Extracts and identifies all UWS standard and non-standard parameters from the given {@link HttpServletRequest}.</p> * <p>Extracts and identifies all UWS standard and non-standard parameters from the given {@link HttpServletRequest}.</p>
* *
* <p><i><u>note:</u> Even if no controllers is provided, this constructor sets the default * <p><i><u>note:</u> Even if no controllers is provided, this constructor sets the default
* input parameter controllers (see {@link #getDefaultControllers()}).</i></p> * input parameter controllers (see {@link #getDefaultControllers()}).</i></p>
* *
* @param request The request to parse to extract the parameters. * @param request The request to parse to extract the parameters.
* @param expectedAdditionalParams The names of all expected additional parameters. * @param expectedAdditionalParams The names of all expected additional parameters.
* <i><u>note:</u> they will be identified with no case sensitivity * <i><u>note:</u> they will be identified with no case sensitivity
* and stored with the same case as in this collection.</i> * and stored with the same case as in this collection.</i>
* @param inputParamControllers Controllers of the input parameters. * @param inputParamControllers Controllers of the input parameters.
* *
* @throws UWSException If one of the given parameter is incorrect or badly formatted. * @throws UWSException If one of the given parameter is incorrect or badly formatted.
* *
* @see #UWSParameters(Map, Collection, Map) * @see #UWSParameters(Map, Collection, Map)
*/ */
public UWSParameters(final HttpServletRequest request, final Collection<String> expectedAdditionalParams, final Map<String,InputParamController> inputParamControllers) throws UWSException{ public UWSParameters(final HttpServletRequest request, final Collection<String> expectedAdditionalParams, final Map<String, InputParamController> inputParamControllers) throws UWSException{
this(getParameters(request), expectedAdditionalParams, inputParamControllers); this(getParameters(request), expectedAdditionalParams, inputParamControllers);
} }
/** /**
* <p>Get the parameters stored in the given HTTP request.</p> * <p>Get the parameters stored in the given HTTP request.</p>
* *
* <p> * <p>
* Since the version 4.1, parameters are extracted immediately when the request is received. They are then stored in an attribute * Since the version 4.1, parameters are extracted immediately when the request is received. They are then stored in an attribute
* under the name of {@link UWS#REQ_ATTRIBUTE_PARAMETERS}. Thus, the map of parameters can be got in that way. However, if this attribute * under the name of {@link UWS#REQ_ATTRIBUTE_PARAMETERS}. Thus, the map of parameters can be got in that way. However, if this attribute
* does not exist, this function will ask for the parameters extracted by {@link HttpServletRequest} ({@link HttpServletRequest#getParameterNames()} * does not exist, this function will ask for the parameters extracted by {@link HttpServletRequest} ({@link HttpServletRequest#getParameterNames()}
* and {@link HttpServletRequest#getParameter(String)}). In this last case only the last non-null occurrence of any parameter will be kept. * and {@link HttpServletRequest#getParameter(String)}). In this last case only the last non-null occurrence of any parameter will be kept.
* </p> * </p>
* *
* @param request HTTP request from which the parameters must be got. * @param request HTTP request from which the parameters must be got.
* *
* @return The extracted parameters. * @return The extracted parameters.
* *
* @since 4.1 * @since 4.1
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected static Map<String,Object> getParameters(final HttpServletRequest request){ protected static Map<String, Object> getParameters(final HttpServletRequest request){
// No request => no parameters: // No request => no parameters:
if (request == null) if (request == null)
return null; return null;
...@@ -257,12 +258,13 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -257,12 +258,13 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
* If so, these parameters can be found as a Map<String,Object> in the request attribute "UWS_PARAMETERS": */ * If so, these parameters can be found as a Map<String,Object> in the request attribute "UWS_PARAMETERS": */
try{ try{
if (request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS) != null) if (request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS) != null)
return (Map<String,Object>)request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS); return (Map<String, Object>)request.getAttribute(UWS.REQ_ATTRIBUTE_PARAMETERS);
}catch(Exception e){} // 2 possible exceptions: ClassCastException and NullPointerException }catch(Exception e){
} // 2 possible exceptions: ClassCastException and NullPointerException
/* If there is no such attribute or if it is not of the good type, /* If there is no such attribute or if it is not of the good type,
* extract only application/x-www-form-urlencoded parameters: */ * extract only application/x-www-form-urlencoded parameters: */
Map<String,Object> map = new HashMap<String,Object>(request.getParameterMap().size()); Map<String, Object> map = new HashMap<String, Object>(request.getParameterMap().size());
Enumeration<String> names = request.getParameterNames(); Enumeration<String> names = request.getParameterNames();
int i; int i;
String n; String n;
...@@ -283,42 +285,42 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -283,42 +285,42 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Extracts and identifies all UWS standard and non-standard parameters from the map.</p> * <p>Extracts and identifies all UWS standard and non-standard parameters from the map.</p>
* *
* <p><i><u>note:</u> The default input parameter controllers are set by default (see {@link #getDefaultControllers()}).</i></p> * <p><i><u>note:</u> The default input parameter controllers are set by default (see {@link #getDefaultControllers()}).</i></p>
* *
* @param params A map of parameters. * @param params A map of parameters.
* *
* @throws UWSException If one of the given parameter is incorrect or badly formatted. * @throws UWSException If one of the given parameter is incorrect or badly formatted.
* *
* @see #UWSParameters(Map, Collection, Map) * @see #UWSParameters(Map, Collection, Map)
*/ */
public UWSParameters(final Map<String,Object> params) throws UWSException{ public UWSParameters(final Map<String, Object> params) throws UWSException{
this(params, null, null); this(params, null, null);
} }
/** /**
* <p>Extracts and identifies all UWS standard and non-standard parameters from the map.</p> * <p>Extracts and identifies all UWS standard and non-standard parameters from the map.</p>
* *
* <p><i><u>note:</u> Even if no controllers is provided, this constructor sets the default * <p><i><u>note:</u> Even if no controllers is provided, this constructor sets the default
* input parameter controllers (see {@link #getDefaultControllers()}).</i></p> * input parameter controllers (see {@link #getDefaultControllers()}).</i></p>
* *
* @param params A map of parameters. * @param params A map of parameters.
* @param expectedAdditionalParams The names of all expected additional parameters. * @param expectedAdditionalParams The names of all expected additional parameters.
* <i><u>note:</u> they will be identified with no case sensitivity * <i><u>note:</u> they will be identified with no case sensitivity
* and stored with the same case as in this collection.</i> * and stored with the same case as in this collection.</i>
* @param inputParamControllers Controllers of the input parameters. * @param inputParamControllers Controllers of the input parameters.
* *
* @throws UWSException If one of the given parameter is incorrect or badly formatted. * @throws UWSException If one of the given parameter is incorrect or badly formatted.
* *
* @see #UWSParameters(Collection, Map) * @see #UWSParameters(Collection, Map)
*/ */
public UWSParameters(final Map<String,Object> params, final Collection<String> expectedAdditionalParams, final Map<String,InputParamController> inputParamControllers) throws UWSException{ public UWSParameters(final Map<String, Object> params, final Collection<String> expectedAdditionalParams, final Map<String, InputParamController> inputParamControllers) throws UWSException{
this(expectedAdditionalParams, inputParamControllers); this(expectedAdditionalParams, inputParamControllers);
// Load all parameters: // Load all parameters:
if (params != null && !params.isEmpty()){ if (params != null && !params.isEmpty()){
Iterator<Entry<String,Object>> it = params.entrySet().iterator(); Iterator<Entry<String, Object>> it = params.entrySet().iterator();
Entry<String,Object> entry; Entry<String, Object> entry;
while(it.hasNext()){ while(it.hasNext()){
entry = it.next(); entry = it.next();
set(entry.getKey(), entry.getValue()); set(entry.getKey(), entry.getValue());
...@@ -328,11 +330,11 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -328,11 +330,11 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* Builds a default list of controllers for the standard UWS input parameters. * Builds a default list of controllers for the standard UWS input parameters.
* *
* @return Default list of controllers. <i><u>note:</u> This map can be modified !</i> * @return Default list of controllers. <i><u>note:</u> This map can be modified !</i>
*/ */
protected Map<String,InputParamController> getDefaultControllers(){ protected Map<String, InputParamController> getDefaultControllers(){
HashMap<String,InputParamController> controllers = new HashMap<String,InputParamController>(2); HashMap<String, InputParamController> controllers = new HashMap<String, InputParamController>(2);
controllers.put(UWSJob.PARAM_EXECUTION_DURATION, new ExecutionDurationController()); controllers.put(UWSJob.PARAM_EXECUTION_DURATION, new ExecutionDurationController());
controllers.put(UWSJob.PARAM_DESTRUCTION_TIME, new DestructionTimeController()); controllers.put(UWSJob.PARAM_DESTRUCTION_TIME, new DestructionTimeController());
return controllers; return controllers;
...@@ -340,11 +342,11 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -340,11 +342,11 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Must return the input parameter controller of the specified parameter.</p> * <p>Must return the input parameter controller of the specified parameter.</p>
* *
* <p><i><u>note:</u> This function is supposed to be case sensitive !</i></p> * <p><i><u>note:</u> This function is supposed to be case sensitive !</i></p>
* *
* @param inputParamName The name of the parameter whose the controller is asked. * @param inputParamName The name of the parameter whose the controller is asked.
* *
* @return The corresponding controller or <i>null</i> if there is no controller for the specified parameter. * @return The corresponding controller or <i>null</i> if there is no controller for the specified parameter.
*/ */
protected InputParamController getController(final String inputParamName){ protected InputParamController getController(final String inputParamName){
...@@ -353,15 +355,15 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -353,15 +355,15 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* Must return the list of all available input parameter controllers. * Must return the list of all available input parameter controllers.
* *
* @return An iterator over all available controllers. * @return An iterator over all available controllers.
*/ */
protected Iterator<Entry<String,InputParamController>> getControllers(){ protected Iterator<Entry<String, InputParamController>> getControllers(){
return mapParamControllers.entrySet().iterator(); return mapParamControllers.entrySet().iterator();
} }
@Override @Override
public final Iterator<Entry<String,Object>> iterator(){ public final Iterator<Entry<String, Object>> iterator(){
return params.entrySet().iterator(); return params.entrySet().iterator();
} }
...@@ -391,19 +393,17 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -391,19 +393,17 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Set the default value to all missing parameters (ONLY for those who have a controller -&gt; see {@link InputParamController}).</p> * <p>Set the default value to all missing parameters (ONLY for those who have a controller -&gt; see {@link InputParamController}).</p>
* *
* <p><i><u>note:</u> This method is thread safe (synchronized on the list of parameters) !</i></p> * <p><i><u>note:</u> This method is thread safe!</i></p>
*/ */
public final void init(){ public final void init(){
Iterator<Entry<String,InputParamController>> itControllers = getControllers(); Iterator<Entry<String, InputParamController>> itControllers = getControllers();
if (itControllers != null){ if (itControllers != null){
synchronized(params){ Entry<String, InputParamController> entry;
Entry<String,InputParamController> entry; while(itControllers.hasNext()){
while(itControllers.hasNext()){ entry = itControllers.next();
entry = itControllers.next(); if (!params.containsKey(entry.getKey()) && entry.getValue().getDefault() != null)
if (!params.containsKey(entry.getKey()) && entry.getValue().getDefault() != null) params.put(entry.getKey(), entry.getValue().getDefault());
params.put(entry.getKey(), entry.getValue().getDefault());
}
} }
} }
} }
...@@ -411,69 +411,68 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -411,69 +411,68 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Updates this list of UWS parameters with the given ones. No check is done on the given parameters * <p>Updates this list of UWS parameters with the given ones. No check is done on the given parameters
* (since they come from an instance of {@link UWSParameters}, they are supposed to be correct)</p> * (since they come from an instance of {@link UWSParameters}, they are supposed to be correct)</p>
* *
* <p><i><u>note:</u> This method is thread safe (synchronized on the list of parameters) !</i></p> * <p><i><u>note:</u> This method is thread safe!</i></p>
* *
* @param newParams The parameters to update. * @param newParams The parameters to update.
* *
* @return An array listing the name of the updated parameters. * @return An array listing the name of the updated parameters.
* *
* @exception UWSException If one of the given parameters is not allowed to be modified. * @exception UWSException If one of the given parameters is not allowed to be modified.
*/ */
public String[] update(final UWSParameters newParams) throws UWSException{ public String[] update(final UWSParameters newParams) throws UWSException{
if (newParams != null && !newParams.params.isEmpty()){ if (newParams != null && !newParams.params.isEmpty()){
synchronized(params){ additionalParams = null;
additionalParams = null; files = null;
files = null; String[] updated = new String[newParams.params.size()];
String[] updated = new String[newParams.params.size()]; Object oldValue;
Object oldValue; int i = 0;
int i = 0; for(Entry<String, Object> entry : newParams){
for(Entry<String,Object> entry : newParams){ // Test whether this parameter is allowed to be modified after its initialization:
// Test whether this parameter is allowed to be modified after its initialization: InputParamController controller = getController(entry.getKey());
InputParamController controller = getController(entry.getKey()); if (controller != null && !controller.allowModification())
if (controller != null && !controller.allowModification()) throw new UWSException(UWSException.FORBIDDEN, "The parameter \"" + entry.getKey() + "\" can not be modified after initialization!");
throw new UWSException(UWSException.FORBIDDEN, "The parameter \"" + entry.getKey() + "\" can not be modified after initialization!"); // Determine whether the value already exists:
// Determine whether the value already exists: if (params.containsKey(entry.getKey()) || entry.getKey().toLowerCase().matches(UWS_RW_PARAMETERS_REGEXP)){
if (params.containsKey(entry.getKey()) || entry.getKey().toLowerCase().matches(UWS_RW_PARAMETERS_REGEXP)){ // If the value is NULL, throw an error (no parameter can be removed after job creation):
// If the value is NULL, throw an error (no parameter can be removed after job creation): if (entry.getValue() == null)
if (entry.getValue() == null) throw new UWSException(UWSException.FORBIDDEN, "Removing a parameter (here: \"" + entry.getKey() + "\") from a job is forbidden!");
throw new UWSException(UWSException.FORBIDDEN, "Removing a parameter (here: \"" + entry.getKey() + "\") from a job is forbidden!"); // Else update the parameter value:
// Else update the parameter value: else{
else{ // If the parameter to replace is an uploaded file, it must be physically removed before replacement:
// If the parameter to replace is an uploaded file, it must be physically removed before replacement: oldValue = params.get(entry.getKey());
oldValue = params.get(entry.getKey()); if (oldValue != null && oldValue instanceof UploadFile){
if (oldValue != null && oldValue instanceof UploadFile){ try{
try{ ((UploadFile)oldValue).deleteFile();
((UploadFile)oldValue).deleteFile(); }catch(IOException ioe){
}catch(IOException ioe){}
} }
// Perform the replacement:
params.put(entry.getKey(), entry.getValue());
} }
}else // Perform the replacement:
// No parameter can be added after job creation: params.put(entry.getKey(), entry.getValue());
throw new UWSException(UWSException.FORBIDDEN, "Adding a parameter (here: \"" + entry.getKey() + "\") to an existing job is forbidden by the UWS protocol!"); }
// Update the list of updated parameters: }else
updated[i++] = entry.getKey(); // No parameter can be added after job creation:
} throw new UWSException(UWSException.FORBIDDEN, "Adding a parameter (here: \"" + entry.getKey() + "\") to an existing job is forbidden by the UWS protocol!");
return updated; // Update the list of updated parameters:
updated[i++] = entry.getKey();
} }
return updated;
}else }else
return new String[0]; return new String[0];
} }
/** /**
* <p>Gets the value of the specified parameter.</p> * <p>Gets the value of the specified parameter.</p>
* *
* <p><i><u>note 1:</u> The case of the parameter name MUST BE correct EXCEPT FOR the standard UWS parameters (i.e. runId, executionDuration, destructionTime).</i></p> * <p><i><u>note 1:</u> The case of the parameter name MUST BE correct EXCEPT FOR the standard UWS parameters (i.e. runId, executionDuration, destructionTime).</i></p>
* <p><i><u>note 2:</u> If the name of the parameter is {@link UWSJob#PARAM_PARAMETERS PARAMETERS}, this function will return exactly what {@link #getAdditionalParameters()} returns.</i></p> * <p><i><u>note 2:</u> If the name of the parameter is {@link UWSJob#PARAM_PARAMETERS PARAMETERS}, this function will return exactly what {@link #getAdditionalParameters()} returns.</i></p>
* <p><i><u>note 3:</u> Depending of the way the parameters are fetched from an HTTP request, the returned object may be an array. Each item of this array would then be an occurrence of the parameter in the request (MAYBE in the same order as submitted).</i></p> * <p><i><u>note 3:</u> Depending of the way the parameters are fetched from an HTTP request, the returned object may be an array. Each item of this array would then be an occurrence of the parameter in the request (MAYBE in the same order as submitted).</i></p>
* *
* @param name Name of the parameter to get. * @param name Name of the parameter to get.
* *
* @return Value of the specified parameter, or <i>null</i> if the given name is <i>null</i>, or an array or {@link Object}s if several values * @return Value of the specified parameter, or <i>null</i> if the given name is <i>null</i>, or an array or {@link Object}s if several values
* have been submitted for the same parameter, empty or has no value. * have been submitted for the same parameter, empty or has no value.
* *
* @see #normalizeParamName(String) * @see #normalizeParamName(String)
* @see #getAdditionalParameters() * @see #getAdditionalParameters()
*/ */
...@@ -489,25 +488,23 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -489,25 +488,23 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* Get the list of all uploaded files. * Get the list of all uploaded files.
* *
* @return An iterator over the list of uploaded files. * @return An iterator over the list of uploaded files.
* *
* @since 4.1 * @since 4.1
*/ */
public final Iterator<UploadFile> getFiles(){ public final Iterator<UploadFile> getFiles(){
if (files == null){ if (files == null){
files = new ArrayList<UploadFile>(3); files = new ArrayList<UploadFile>(3);
synchronized(params){ for(Object v : params.values()){
for(Object v : params.values()){ if (v == null)
if (v == null) continue;
continue; else if (v instanceof UploadFile)
else if (v instanceof UploadFile) files.add((UploadFile)v);
files.add((UploadFile)v); else if (v.getClass().isArray()){
else if (v.getClass().isArray()){ for(Object o : (Object[])v){
for(Object o : (Object[])v){ if (o instanceof UploadFile)
if (o instanceof UploadFile) files.add((UploadFile)o);
files.add((UploadFile)o);
}
} }
} }
} }
...@@ -518,20 +515,20 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -518,20 +515,20 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Sets the given value to the specified parameter. * <p>Sets the given value to the specified parameter.
* But if the given value is <i>null</i>, the specified parameter is merely removed.</p> * But if the given value is <i>null</i>, the specified parameter is merely removed.</p>
* *
* <p><i><u>note 1:</u> This method is thread safe (synchronized on the list of parameters) !</i></p> * <p><i><u>note 1:</u> This method is thread safe!</i></p>
* <p><i><u>note 2:</u> The case of the parameter name MUST BE correct EXCEPT FOR the standard UWS parameters (i.e. runId, executionDuration, destructionTime).</i></p> * <p><i><u>note 2:</u> The case of the parameter name MUST BE correct EXCEPT FOR the standard UWS parameters (i.e. runId, executionDuration, destructionTime).</i></p>
* <p><i><u>note 3:</u> If the specified parameter is a read-only UWS parameter (i.e. jobId, startTime, endTime, results, errorSummary, quote), this function does nothing and merely returns NULL.</i></p> * <p><i><u>note 3:</u> If the specified parameter is a read-only UWS parameter (i.e. jobId, startTime, endTime, results, errorSummary, quote), this function does nothing and merely returns NULL.</i></p>
* <p><i><u>note 4:</u> A value equals to NULL, means that the specified parameter must be removed.</i></p> * <p><i><u>note 4:</u> A value equals to NULL, means that the specified parameter must be removed.</i></p>
* <p><i><u>note 5:</u> If the parameter {@link UWSJob#PARAM_PARAMETERS PARAMETERS} is given, it must be a Map<String, Object>. In this case, the map is read and all its entries are added individually.</i></p> * <p><i><u>note 5:</u> If the parameter {@link UWSJob#PARAM_PARAMETERS PARAMETERS} is given, it must be a Map<String, Object>. In this case, the map is read and all its entries are added individually.</i></p>
* *
* @param name Name of the parameter to set (add, update or remove). <i><u>note:</u> not case sensitive ONLY FOR the standard UWS parameters !</i> * @param name Name of the parameter to set (add, update or remove). <i><u>note:</u> not case sensitive ONLY FOR the standard UWS parameters !</i>
* @param value The value to set. <i><u>note:</u> NULL means that the specified parameter must be removed ; several values may have been provided using an array of Objects.</i> * @param value The value to set. <i><u>note:</u> NULL means that the specified parameter must be removed ; several values may have been provided using an array of Objects.</i>
* *
* @return The old value of the specified parameter. <i>null</i> may mean that the parameter has just been added, but it may also mean that nothing has been done (because, the given name is null, empty or corresponds to a read-only parameter). * @return The old value of the specified parameter. <i>null</i> may mean that the parameter has just been added, but it may also mean that nothing has been done (because, the given name is null, empty or corresponds to a read-only parameter).
* *
* @throws UWSException If the given value is incorrect or badly formatted. * @throws UWSException If the given value is incorrect or badly formatted.
* *
* @see #normalizeParamName(String) * @see #normalizeParamName(String)
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
...@@ -547,55 +544,54 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -547,55 +544,54 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
if (normalizedName == null) if (normalizedName == null)
return null; return null;
synchronized(params){ additionalParams = null;
additionalParams = null; files = null;
files = null;
// Case of the PARAMETERS parameter: read all parameters and set them individually into this UWSParameters instance: // Case of the PARAMETERS parameter: read all parameters and set them individually into this UWSParameters instance:
if (normalizedName.equals(UWSJob.PARAM_PARAMETERS)){ if (normalizedName.equals(UWSJob.PARAM_PARAMETERS)){
// the value MUST BE a Map<String, Object>: // the value MUST BE a Map<String, Object>:
if (value instanceof Map){ if (value instanceof Map){
try{ try{
Map<String,Object> otherParams = (Map<String,Object>)value; Map<String, Object> otherParams = (Map<String, Object>)value;
HashMap<String,Object> mapOldValues = new HashMap<String,Object>(otherParams.size()); HashMap<String, Object> mapOldValues = new HashMap<String, Object>(otherParams.size());
Object oldValue = null; Object oldValue = null;
for(Entry<String,Object> entry : otherParams.entrySet()){ for(Entry<String, Object> entry : otherParams.entrySet()){
oldValue = set(entry.getKey(), entry.getValue()); oldValue = set(entry.getKey(), entry.getValue());
mapOldValues.put(entry.getKey(), oldValue); mapOldValues.put(entry.getKey(), oldValue);
}
return mapOldValues;
}catch(ClassCastException cce){
return null;
} }
}else return mapOldValues;
}catch(ClassCastException cce){
return null; return null;
}else{
// Check the value before setting it:
InputParamController controller = getController(normalizedName);
if (controller != null)
value = controller.check(value);
// If the parameter already exists and it is an uploaded file, delete it before its replacement:
Object oldValue = params.get(normalizedName);
if (oldValue != null && oldValue instanceof UploadFile){
try{
((UploadFile)oldValue).deleteFile();
}catch(IOException ioe){}
} }
}else
// Set the new value: return null;
return params.put(normalizedName, value); }else{
// Check the value before setting it:
InputParamController controller = getController(normalizedName);
if (controller != null)
value = controller.check(value);
// If the parameter already exists and it is an uploaded file, delete it before its replacement:
Object oldValue = params.get(normalizedName);
if (oldValue != null && oldValue instanceof UploadFile){
try{
((UploadFile)oldValue).deleteFile();
}catch(IOException ioe){
}
} }
// Set the new value:
return params.put(normalizedName, value);
} }
} }
/** /**
* Removes the value of the given input parameter name. * Removes the value of the given input parameter name.
* *
* @param inputParamName Name of the parameter to remove. <i><u>note:</u> not case sensitive ONLY FOR the standard UWS parameters !</i> * @param inputParamName Name of the parameter to remove. <i><u>note:</u> not case sensitive ONLY FOR the standard UWS parameters !</i>
* *
* @return The removed value. * @return The removed value.
* *
* @see #normalizeParamName(String) * @see #normalizeParamName(String)
*/ */
public Object remove(final String inputParamName){ public Object remove(final String inputParamName){
...@@ -606,27 +602,26 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -606,27 +602,26 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
if (normalizedName == null) if (normalizedName == null)
return null; return null;
synchronized(params){ additionalParams = null;
additionalParams = null; files = null;
files = null; // Remove the file:
// Remove the file: Object removed = params.remove(normalizedName);
Object removed = params.remove(normalizedName); // If the removed parameter was a file, remove it from the server:
// If the removed parameter was a file, remove it from the server: if (removed != null && removed instanceof UploadFile){
if (removed != null && removed instanceof UploadFile){ try{
try{ ((UploadFile)removed).deleteFile();
((UploadFile)removed).deleteFile(); }catch(IOException ioe){
}catch(IOException ioe){}
} }
// Return the value of the removed parameter:
return removed;
} }
// Return the value of the removed parameter:
return removed;
} }
/** /**
* Normalizes the given name. * Normalizes the given name.
* *
* @param name Name of the parameter to identify/normalize. * @param name Name of the parameter to identify/normalize.
* *
* @return Normalized name or <i>null</i> if the parameters should be ignored (i.e. empty name and UWS read-only parameters). * @return Normalized name or <i>null</i> if the parameters should be ignored (i.e. empty name and UWS read-only parameters).
*/ */
protected String normalizeParamName(final String name){ protected String normalizeParamName(final String name){
...@@ -663,18 +658,16 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -663,18 +658,16 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
/** /**
* <p>Gets the value of the parameter {@link UWSJob#PARAM_PHASE phase}.</p> * <p>Gets the value of the parameter {@link UWSJob#PARAM_PHASE phase}.</p>
* *
* <p><i><u>note:</u> This parameter is removed from the parameters list after the call of this function !</i></p> * <p><i><u>note:</u> This parameter is removed from the parameters list after the call of this function !</i></p>
* *
* @return The given phase. * @return The given phase.
*/ */
public final String getInputPhase(){ public final String getInputPhase(){
synchronized(params){ if (hasInputPhase())
if (hasInputPhase()) return params.remove(UWSJob.PARAM_PHASE).toString();
return params.remove(UWSJob.PARAM_PHASE).toString(); else
else return null;
return null;
}
} }
/** /**
...@@ -697,9 +690,7 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -697,9 +690,7 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
else if (value instanceof String){ else if (value instanceof String){
try{ try{
Long duration = Long.parseLong((String)value); Long duration = Long.parseLong((String)value);
synchronized(params){ params.put(UWSJob.PARAM_EXECUTION_DURATION, duration);
params.put(UWSJob.PARAM_EXECUTION_DURATION, duration);
}
return duration; return duration;
}catch(NumberFormatException nfe){ }catch(NumberFormatException nfe){
; ;
...@@ -721,9 +712,7 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -721,9 +712,7 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
else if (value instanceof String){ else if (value instanceof String){
try{ try{
Date destruction = ISO8601Format.parseToDate((String)value); Date destruction = ISO8601Format.parseToDate((String)value);
synchronized(params){ params.put(UWSJob.PARAM_DESTRUCTION_TIME, destruction);
params.put(UWSJob.PARAM_DESTRUCTION_TIME, destruction);
}
return destruction; return destruction;
}catch(ParseException pe){ }catch(ParseException pe){
; ;
...@@ -738,22 +727,20 @@ public class UWSParameters implements Iterable<Entry<String,Object>> { ...@@ -738,22 +727,20 @@ public class UWSParameters implements Iterable<Entry<String,Object>> {
* <p><b><u>WARNING:</u> The result of this function MUST BE used ONLY in READ-ONLY. * <p><b><u>WARNING:</u> The result of this function MUST BE used ONLY in READ-ONLY.
* Any modification applied to the returned map will NEVER be propagated to this instance of {@link UWSParameters} ! * Any modification applied to the returned map will NEVER be propagated to this instance of {@link UWSParameters} !
* <u>You should rather use {@link #set(String, Object)} to add/update/remove an additional parameters !</u></b></p> * <u>You should rather use {@link #set(String, Object)} to add/update/remove an additional parameters !</u></b></p>
* *
* @return All the UWS additional parameters. * @return All the UWS additional parameters.
*/ */
public final Map<String,Object> getAdditionalParameters(){ public final Map<String, Object> getAdditionalParameters(){
if (additionalParams == null){ if (additionalParams == null){
synchronized(params){ additionalParams = new ConcurrentHashMap<String, Object>(params.size());
additionalParams = new HashMap<String,Object>(params.size()); for(Entry<String, Object> entry : params.entrySet()){
for(Entry<String,Object> entry : params.entrySet()){ boolean uwsParam = false;
boolean uwsParam = false; for(int i = 0; !uwsParam && i < UWS_RW_PARAMETERS.length; i++)
for(int i = 0; !uwsParam && i < UWS_RW_PARAMETERS.length; i++) uwsParam = entry.getKey().equals(UWS_RW_PARAMETERS[i]);
uwsParam = entry.getKey().equals(UWS_RW_PARAMETERS[i]); for(int i = 0; !uwsParam && i < UWS_RO_PARAMETERS.length; i++)
for(int i = 0; !uwsParam && i < UWS_RO_PARAMETERS.length; i++) uwsParam = entry.getKey().equals(UWS_RO_PARAMETERS[i]);
uwsParam = entry.getKey().equals(UWS_RO_PARAMETERS[i]); if (!uwsParam)
if (!uwsParam) additionalParams.put(entry.getKey(), entry.getValue());
additionalParams.put(entry.getKey(), entry.getValue());
}
} }
} }
return additionalParams; return additionalParams;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment