diff --git a/src/tap/AbstractTAPFactory.java b/src/tap/AbstractTAPFactory.java index 624b386a4ab2548b76159c45d54b876c0ef6b7f9..e96636c5ac1a6511e7c2547994fbe22834027ee3 100644 --- a/src/tap/AbstractTAPFactory.java +++ b/src/tap/AbstractTAPFactory.java @@ -16,7 +16,7 @@ package tap; * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 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) */ @@ -39,6 +39,7 @@ import uws.UWSException; import uws.job.ErrorSummary; import uws.job.Result; import uws.job.user.JobOwner; +import uws.service.UWS; import uws.service.UWSService; import uws.service.backup.UWSBackupManager; import uws.service.error.ServiceErrorWriter; @@ -54,7 +55,7 @@ import adql.query.ADQLQuery; * Only the functions related with the database connection stay abstract. * * @author Grégory Mantelet (CDS;ARI) - * @version 2.0 (04/2015) + * @version 2.1 (01/2016) */ public abstract class AbstractTAPFactory extends TAPFactory { @@ -256,8 +257,16 @@ public abstract class AbstractTAPFactory extends TAPFactory { @Override protected TAPJob createTAPJob(final HttpServletRequest request, final JobOwner owner) throws UWSException{ try{ + // Extract the HTTP request ID (the job ID should be the same, if not already used by another job): + String requestID = null; + if (request.getAttribute(UWS.REQ_ATTRIBUTE_ID) != null && request.getAttribute(UWS.REQ_ATTRIBUTE_ID) instanceof String) + requestID = request.getAttribute(UWS.REQ_ATTRIBUTE_ID).toString(); + + // Extract the TAP parameters from the HTTP request: TAPParameters tapParams = createTAPParameters(request); - return new TAPJob(owner, tapParams); + + // Create the job: + return new TAPJob(owner, tapParams, requestID); }catch(TAPException te){ if (te.getCause() != null && te.getCause() instanceof UWSException) throw (UWSException)te.getCause(); diff --git a/src/tap/TAPJob.java b/src/tap/TAPJob.java index c2e2ea974465d22cf5d3fbfc1d49911a7c858b9e..7640e83856f24c46e9a640d357d0a14bf0fd35f7 100644 --- a/src/tap/TAPJob.java +++ b/src/tap/TAPJob.java @@ -16,7 +16,7 @@ package tap; * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 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> * * @author Grégory Mantelet (CDS;ARI) - * @version 2.1 (11/2015) + * @version 2.1 (01/2016) */ public class TAPJob extends UWSJob { private static final long serialVersionUID = 1L; @@ -111,6 +111,28 @@ public class TAPJob extends UWSJob { tapParams.check(); } + /** + * <p>Build a pending TAP job with the given parameters. + * The given HTTP request ID will be used as Job ID if not already used by another job.</p> + * + * <p><i><u>Note:</u> if the parameter {@link #PARAM_PHASE} (</i>phase<i>) is given with the value {@link #PHASE_RUN} + * the job execution starts immediately after the job has been added to a job list or after {@link #applyPhaseParam(JobOwner)} is called.</i></p> + * + * @param owner User who owns this job. <i>MAY BE NULL</i> + * @param tapParams Set of parameters. + * @param requestID ID of the HTTP request which has initiated the creation of this job. + * <i>Note: if NULL, empty or already used, a job ID will be generated thanks to {@link #generateJobId()}.</i> + * + * @throws TAPException If one of the given parameters has a forbidden or wrong value. + * + * @since 2.1 + */ + public TAPJob(final JobOwner owner, final TAPParameters tapParams, final String requestID) throws TAPException{ + super(owner, tapParams, requestID); + this.tapParams = tapParams; + tapParams.check(); + } + /** * <p>Restore a job in a state defined by the given parameters. * The phase must be set separately with {@link #setPhase(uws.job.ExecutionPhase, boolean)}, where the second parameter is true.</p> diff --git a/src/tap/TAPSyncJob.java b/src/tap/TAPSyncJob.java index 01421a36b60a5fa3f237d139a57aecb36f1924ab..bb6f867ca27e070f8e7eafc83c6ab8549ee14f76 100644 --- a/src/tap/TAPSyncJob.java +++ b/src/tap/TAPSyncJob.java @@ -16,7 +16,7 @@ package tap; * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 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) */ @@ -48,7 +48,7 @@ import uws.service.log.UWSLog.LogLevel; * </p> * * @author Grégory Mantelet (CDS;ARI) - * @version 2.0 (02/2015) + * @version 2.1 (01/2016) */ public class TAPSyncJob { @@ -56,7 +56,7 @@ public class TAPSyncJob { protected long waitForStop = 1000; /** Last generated ID of a synchronous job. */ - protected static String lastId = null; + protected static String lastId = "S" + System.currentTimeMillis() + "A"; /** Description of the TAP service in charge of this synchronous job. */ protected final ServiceConnection service; @@ -100,6 +100,39 @@ public class TAPSyncJob { ID = generateId(); } + /** + * Create a synchronous TAP job. + * The given HTTP request ID will be used as Job ID if not already used by another job. + * + * @param service Description of the TAP service which is in charge of this synchronous job. + * @param params Parameters of the query to execute. It must mainly contain the ADQL query to execute. + * @param requestID ID of the HTTP request which has initiated the creation of this job. + * <i>Note: if NULL, empty or already used, a job ID will be generated thanks to {@link #generateId()}.</i> + * + * @throws NullPointerException If one of the 2 first parameters is NULL. + * + * @since 2.1 + */ + public TAPSyncJob(final ServiceConnection service, final TAPParameters params, final String requestID) throws NullPointerException{ + if (params == null) + throw new NullPointerException("Missing TAP parameters ! => Impossible to create a synchronous TAP job."); + tapParams = params; + tapParams.init(); + + if (service == null) + throw new NullPointerException("Missing the service description ! => Impossible to create a synchronous TAP job."); + this.service = service; + + synchronized(lastId){ + if (requestID == null || requestID.trim().length() == 0 || lastId.equals(requestID)) + ID = generateId(); + else{ + ID = requestID; + lastId = requestID; + } + } + } + /** * <p>This function lets generating a unique ID.</p> * @@ -112,13 +145,15 @@ public class TAPSyncJob { * @return A unique job identifier. */ protected String generateId(){ - String generatedId = "S" + System.currentTimeMillis() + "A"; - if (lastId != null){ - while(lastId.equals(generatedId)) - generatedId = generatedId.substring(0, generatedId.length() - 1) + (char)(generatedId.charAt(generatedId.length() - 1) + 1); + synchronized(lastId){ + String generatedId = "S" + System.currentTimeMillis() + "A"; + if (lastId != null){ + while(lastId.equals(generatedId)) + generatedId = generatedId.substring(0, generatedId.length() - 1) + (char)(generatedId.charAt(generatedId.length() - 1) + 1); + } + lastId = generatedId; + return generatedId; } - lastId = generatedId; - return generatedId; } /** diff --git a/src/tap/resource/Sync.java b/src/tap/resource/Sync.java index 9b93392fe39ed677bb292b9c2df473a0b425d5db..a1a1c0d9c4b92a36d8a2aa02aaa6491843794a38 100644 --- a/src/tap/resource/Sync.java +++ b/src/tap/resource/Sync.java @@ -16,7 +16,7 @@ package tap.resource; * 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) */ @@ -33,6 +33,7 @@ import tap.TAPJob; import tap.TAPSyncJob; import tap.parameters.TAPParameters; import uws.UWSException; +import uws.service.UWS; /** * <p>Synchronous resource of a TAP service.</p> @@ -44,7 +45,7 @@ import uws.UWSException; * </p> * * @author Grégory Mantelet (CDS;ARI) - * @version 2.0 (09/2014) + * @version 2.1 (01/2016) */ public class Sync implements TAPResource { @@ -105,8 +106,13 @@ public class Sync implements TAPResource { if (!service.isAvailable()) throw new TAPException("Can not execute a query: this TAP service is not available! " + service.getAvailability(), UWSException.SERVICE_UNAVAILABLE); + // Extract the HTTP request ID (if any): + String requestID = null; + if (request != null && request.getAttribute(UWS.REQ_ATTRIBUTE_ID) != null && request.getAttribute(UWS.REQ_ATTRIBUTE_ID) instanceof String) + requestID = (String)request.getAttribute(UWS.REQ_ATTRIBUTE_ID); + // Execute synchronously the given job: - TAPSyncJob syncJob = new TAPSyncJob(service, params); + TAPSyncJob syncJob = new TAPSyncJob(service, params, requestID); syncJob.start(response); return true; diff --git a/src/tap/resource/TAP.java b/src/tap/resource/TAP.java index eb02b184c117fb2c6f9daf23e661088b0a9e0f04..f5aea1dbb05beaa411f93a9236eb6b72914feb92 100644 --- a/src/tap/resource/TAP.java +++ b/src/tap/resource/TAP.java @@ -16,7 +16,7 @@ package tap.resource; * 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-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) */ @@ -53,7 +53,7 @@ import adql.db.FunctionDef; * <p>At its creation it is creating and configuring the other resources in function of the given description of the TAP service.</p> * * @author Grégory Mantelet (CDS;ARI) - * @version 2.0 (06/2015) + * @version 2.1 (01/2016) */ public class TAP implements VOSIResource { @@ -737,8 +737,9 @@ public class TAP implements VOSIResource { */ protected synchronized String generateRequestID(final HttpServletRequest request){ String id; + String prefix = (request != null && request.getRequestURI() != null && request.getRequestURI().contains("/sync") ? "S" : ""); do{ - id = System.currentTimeMillis() + ""; + id = prefix + System.currentTimeMillis() + ""; }while(lastRequestID != null && lastRequestID.startsWith(id)); lastRequestID = id; return id; diff --git a/src/uws/job/UWSJob.java b/src/uws/job/UWSJob.java index 37c09951e6da9aca01bfd26712ac4de3017d0841..598a5c222fd2b5e4481439dd31b07651da6ae1fa 100644 --- a/src/uws/job/UWSJob.java +++ b/src/uws/job/UWSJob.java @@ -16,7 +16,7 @@ package uws.job; * You should have received a copy of the GNU Lesser General Public License * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2012-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) */ @@ -89,7 +89,8 @@ import uws.service.request.UploadFile; * <b>{@link #generateJobId()}:</b> * This function is called at the construction of any {@link UWSJob}. It allows to generate a unique job ID. * By default: time (in milliseconds) + a upper-case letter (A, B, C, ....). - * <u>If you want customizing the job ID of your jobs</u>, you need to override this function. + * <u>If you want customizing the job ID of your jobs</u>, you need to override this function or to use the new function + * {@link #UWSJob(JobOwner, UWSParameters, String)}. * </li> * <br /> * <li> @@ -113,7 +114,7 @@ import uws.service.request.UploadFile; * </ul> * * @author Grégory Mantelet (CDS;ARI) - * @version 4.1 (12/2014) + * @version 4.2 (01/2016) */ public class UWSJob extends SerializableUWSObject { private static final long serialVersionUID = 1L; @@ -310,6 +311,52 @@ public class UWSJob extends SerializableUWSObject { } } + /** + * <p>Builds a job of the given owner and from a map of all parameters (UWS and additional parameters). + * The given HTTP request ID will be used as Job ID if not already used by another job.</p> + * + * <p><i><u>Note:</u> if the parameter {@link #PARAM_PHASE} (</i>phase<i>) is given with the value {@link #PHASE_RUN} + * the job execution starts immediately after the job has been added to a job list or after {@link #applyPhaseParam(JobOwner)} is called.</i></p> + * + * @param owner Job.owner ({@link #PARAM_OWNER}). + * @param params UWS standard and non-standard parameters. + * @param requestID ID of the HTTP request which has initiated the creation of this job. + * <i>Note: if NULL, empty or already used, a job ID will be generated thanks to {@link #generateJobId()}.</i> + * + * @see UWSParameters#init() + * + * @since 4.2 + */ + public UWSJob(JobOwner owner, final UWSParameters params, final String requestID){ + this.owner = owner; + + phase = new JobPhase(this); + + results = new HashMap<String,Result>(); + + inputParams = params; + inputParams.init(); + + // Set the Job ID with the value of the HTTP request ID (if not already used by a job): + synchronized(lastId){ + if (requestID == null || requestID.trim().length() == 0 || lastId.equals(requestID)) + jobId = generateJobId(); + else{ + jobId = requestID; + lastId = requestID; + } + } + restorationDate = null; + + // Move all uploaded files in a location related with this job: + Iterator<UploadFile> files = inputParams.getFiles(); + while(files.hasNext()){ + try{ + files.next().move(this); + }catch(IOException ioe){} + } + } + /** * <p><b>CONSTRUCTOR TO USE TO RESTORE A JOB whatever is its phase.</b></p> * @@ -383,7 +430,7 @@ public class UWSJob extends SerializableUWSObject { /** * <p>This function lets generating a unique ID.</p> * - * <p><i><b>By default:</b> System.currentTimeMillis()+UpperCharacter (UpperCharacter: one upper-case character: A, B, C, ....)</i></p> + * <p><i><b>By default:</b> System.currentTimeMillis()+UpperCharacter (UpperCharacter: one upper-case character chosen in order to guarantee the unicity of the ID: A, B, C, ....)</i></p> * * <p><i><u>note: </u> DO NOT USE in this function any of the following functions: {@link #getLogger()}, * {@link #getFileManager()} and {@link #getFactory()}. All of them will return NULL, because this job does not diff --git a/src/uws/service/AbstractUWSFactory.java b/src/uws/service/AbstractUWSFactory.java index 86d1112601db72eacc434cf3238760b137a34544..332254a9014638dbc5c35795765ed544b394986d 100644 --- a/src/uws/service/AbstractUWSFactory.java +++ b/src/uws/service/AbstractUWSFactory.java @@ -16,7 +16,7 @@ package uws.service; * You should have received a copy of the GNU Lesser General Public License * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2012,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) */ @@ -48,7 +48,7 @@ import uws.service.request.UWSRequestParser; * Only the function which creates a {@link JobThread} from a {@link UWSJob} needs to be implemented.</p> * * @author Grégory Mantelet (CDS;ARI) - * @version 4.1 (11/2014) + * @version 4.2 (01/2016) */ public abstract class AbstractUWSFactory implements UWSFactory { @@ -84,7 +84,13 @@ public abstract class AbstractUWSFactory implements UWSFactory { @Override public UWSJob createJob(final HttpServletRequest request, final JobOwner user) throws UWSException{ - return new UWSJob(user, createUWSParameters(request)); + // 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