From a5ddcb1e8e00639146ebded2ca16e33c78dc7128 Mon Sep 17 00:00:00 2001 From: gmantele <gmantele@ari.uni-heidelberg.de> Date: Wed, 13 Jan 2016 15:37:43 +0100 Subject: [PATCH] [TAP,UWS] Set the same ID for the job (asynchronous or not) and for the HTTP request which has initiated the job creation. Actually the HTTP request is generated as before, and then, if a job is created, it is set to the ID of the HTTP request. This modification aims to greatly help the log analysis. --- src/tap/AbstractTAPFactory.java | 15 +++++-- src/tap/TAPJob.java | 26 +++++++++++- src/tap/TAPSyncJob.java | 53 ++++++++++++++++++++---- src/tap/resource/Sync.java | 12 ++++-- src/tap/resource/TAP.java | 7 ++-- src/uws/job/UWSJob.java | 55 +++++++++++++++++++++++-- src/uws/service/AbstractUWSFactory.java | 12 ++++-- 7 files changed, 153 insertions(+), 27 deletions(-) diff --git a/src/tap/AbstractTAPFactory.java b/src/tap/AbstractTAPFactory.java index 624b386..e96636c 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 c2e2ea9..7640e83 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 01421a3..bb6f867 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 9b93392..a1a1c0d 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 eb02b18..f5aea1d 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 37c0995..598a5c2 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 86d1112..332254a 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 -- GitLab