package uws.job;
/*
* 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
Default implementation of a job of the UWS pattern.
* *The current phase of the job.
* Remember: A job is treated as a state machine thanks to this attribute. *This error summary gives a human-readable error message for the underlying job.
* Note: This object is intended to be a detailed error message, and consequently, * might be a large piece of text such as a stack trace. */ protected ErrorSummary errorSummary = null; /** This is a list of all results of this job. */ protected MapBuilds a job with no owner from a map of all parameters (UWS and additional parameters).
* *Note: if the parameter {@link UWSJob#PARAM_PHASE} (phase) is given with the value {@link UWSJob#PHASE_RUN} * the job execution starts immediately after the job has been added to a job list or after {@link #applyPhaseParam(JobOwner)} is called.
* * @param params UWS standard and non-standard parameters. * * @see UWSJob#UWSJob(JobOwner, UWSParameters) */ public UWSJob(final UWSParameters params){ this(null, params); } /** *Builds a job of the given owner and from a map of all parameters (UWS and additional parameters).
* *Note: if the parameter {@link #PARAM_PHASE} (phase) 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.
* * @param owner Job.owner ({@link #PARAM_OWNER}). * @param params UWS standard and non-standard parameters. * * @see UWSParameters#init() */ public UWSJob(JobOwner owner, final UWSParameters params){ this.owner = owner; phase = new JobPhase(this); results = new HashMapCONSTRUCTOR TO USE TO RESTORE A JOB whatever is its phase.
* *Builds a job of the given owner with all the given parameter.
* ** Note: The job phase is automatically set in function of the last parameters (startTime, endTime, results and error). * Only the following execution phase are possible: PENDING, ABORTED, ERROR and COMPLETED. *
* * @param jobID The ID of this job (NOT NULL). * @param owner Its owner. * @param params UWS standard and non-standard parameters. * @param quote Its quote (in seconds). * @param startTime Its start time if it has already been started. * @param endTime Its end time if it is already finished. * @param results Its results (if phase=COMPLETED). * @param error Its error (if phase=ERROR). * * @throws NullPointerException If the given ID is NULL. */ public UWSJob(final String jobID, final JobOwner owner, final UWSParameters params, final long quote, final long startTime, final long endTime, final ListThis function lets generating a unique ID.
* *By default: System.currentTimeMillis()+UpperCharacter (UpperCharacter: one upper-case character: A, B, C, ....)
* *note: 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 * yet know its jobs list (which is needed to know the UWS and so, all of the objects returned by these functions).
* * @return A unique job identifier. */ protected String generateJobId(){ synchronized(lastId){ String generatedId = 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; } } /** *Gets the value of the specified parameter.
* *note: No case sensitivity for the UWS parameters ON THE CONTRARY TO the names of the additional parameters (which are case sensitive).
* * @param name Name of the parameter to get. * * @return Its value or null if there is no parameter with the given name or if the value is null. * * @see UWSParameters#get(String) */ public Object getParameter(String name){ if (name == null || name.trim().isEmpty()) return null; name = name.trim(); if (name.equalsIgnoreCase(PARAM_JOB_ID)) return jobId; else if (name.equalsIgnoreCase(PARAM_OWNER)) return owner; else if (name.equalsIgnoreCase(PARAM_PHASE)) return phase.getPhase(); else if (name.equalsIgnoreCase(PARAM_QUOTE)) return quote; else if (name.equalsIgnoreCase(PARAM_START_TIME)) return startTime; else if (name.equalsIgnoreCase(PARAM_END_TIME)) return endTime; else return inputParams.get(name); } /** *Looks for an additional parameters which corresponds to the Execution Phase. If it exists and:
*Sets the current phase of this job.
* *IMPORTANT: *
Sets the current phase of this job, respecting or not the imposed order.
* *IMPORTANT: *
Gets the phase manager of this job.
* *Note: The phase manager manages all the transitions between all the execution phases.
* * @return Its phase manager. */ public final JobPhase getPhaseManager(){ return phase; } /** *Sets the phase manager of this job.
* *Note: The phase manager manages all the transitions between all the execution phases.
* * @param jobPhase Its new phase manager (if null this function does nothing). */ public final void setPhaseManager(JobPhase jobPhase){ if (jobPhase != null){ synchronized(phase){ phase = jobPhase; } } } /** * Gets the time at which the job execution has started. * * @return The start time of the execution of this job. */ public final Date getStartTime(){ return startTime; } /** * Sets the time at which the job execution has started. * * @param newDateTime The start time of the execution of this job. */ protected final void setStartTime(Date newDateTime){ startTime = newDateTime; } /** * Gets the time at which the job execution has finished. * * @return The end time of the execution of this job. */ public final Date getEndTime(){ return endTime; } /** * Sets the time at which the job execution has finished. * * @param newDateTime The end time of the execution of this job. */ protected final void setEndTime(Date newDateTime){ endTime = newDateTime; // Save the owner jobs list: if (phase.isFinished() && owner != null && getJobList() != null && getJobList().getUWS() != null && getJobList().getUWS().getBackupManager() != null) getJobList().getUWS().getBackupManager().saveOwner(owner); // Log the end of this job: getLogger().logJob(LogLevel.INFO, this, "END", "Job \"" + jobId + "\" ended with the status " + phase, null); } /** * Gets the duration (in seconds) for which this job shall run. * * @return The execution duration of this job. * * @see UWSParameters#getExecutionDuration() */ public final long getExecutionDuration(){ return inputParams.getExecutionDuration(); } /** *Sets the duration (in seconds) for which this job shall run ONLY IF the job can updated (considering its current execution phase, see {@link JobPhase#isJobUpdatable()}).
* *Note: A duration of 0 (or less) implies unlimited execution duration.
* * @param executionDuration The execution duration of this job. * * @see UWSParameters#set(String, Object) */ public final void setExecutionDuration(long executionDuration){ if (phase.isJobUpdatable()){ try{ inputParams.set(PARAM_EXECUTION_DURATION, executionDuration); }catch(UWSException ue){ ; } } } /** * Gets the instant when the job shall be destroyed. * * @return The destruction time of this job. * * @see UWSParameters#getDestructionTime() */ public final Date getDestructionTime(){ return inputParams.getDestructionTime(); } /** ** Sets the instant when the job shall be destroyed ONLY IF the job can updated (considering its current execution phase, see {@link JobPhase#isJobUpdatable()}). * If known the jobs list is notify of this destruction time update. *
* * @param destructionTime The destruction time of this job. MUST NOT be NULL * * @see JobList#updateDestruction(UWSJob) * @see UWSParameters#set(String, Object) */ public final void setDestructionTime(Date destructionTime){ if (destructionTime != null && phase.isJobUpdatable()){ try{ inputParams.set(PARAM_DESTRUCTION_TIME, destructionTime); if (myJobList != null) myJobList.updateDestruction(this); }catch(UWSException ue){ getLogger().logJob(LogLevel.WARNING, this, "SET_DESTRUCTION", "Can not set the destruction time of the job \"" + getJobId() + "\" to \"" + destructionTime + "\"!", ue); } } } /** * Gets the error that occurs during the execution of this job. * * @return A summary of the error. */ public final ErrorSummary getErrorSummary(){ return errorSummary; } /** *Sets the error that occurs during the execution of this job.
* *IMPORTANT: This function will have no effect if the job is finished, that is to say if the current phase is * {@link ExecutionPhase#ABORTED ABORTED}, {@link ExecutionPhase#ERROR ERROR} or {@link ExecutionPhase#COMPLETED COMPLETED}..
* * @param errorSummary A summary of the error. MUST NOT be NULL * * @throws UWSException If the job execution is finished that is to say if the phase is ABORTED, ERROR or COMPLETED. * * @see #isFinished() */ public final void setErrorSummary(ErrorSummary errorSummary) throws UWSException{ if (errorSummary == null) return; else if (!isFinished()) this.errorSummary = errorSummary; else{ getLogger().logJob(LogLevel.ERROR, this, "SET_ERROR", "Can not set an error summary when the job is finished (or not yet started)! The current phase is: " + getPhase() + " ; the summary of the error to set is: \"" + errorSummary.message + "\".", null); throw new UWSException(UWSException.NOT_ALLOWED, UWSExceptionFactory.jobModificationForbidden(jobId, getPhase(), "ERROR SUMMARY")); } } /** * Gets the ID of this job (this ID MUST be unique). * * @return The job ID (unique). */ public final String getJobId(){ return jobId; } /** *Gets the RunID of this job given by the UWS user (presumed to be the owner of this job). * This ID isn't the one used to access to this job thanks to the jobs list: it is more likely a label/name than an ID => it is not unique.
* *Warning: This ID may be used by other jobs BUT their job id (cf {@link UWSJob#getJobId()}) must be different.
* * @return The Run ID (a kind of job name/label). * * @see UWSParameters#getRunId() */ public final String getRunId(){ return inputParams.getRunId(); } /** *Sets the RunID of this job ONLY IF the job can updated (considering its current execution phase, see {@link JobPhase#isJobUpdatable()}).
* * @param name Its name/label. * * @see JobPhase#isJobUpdatable() * * @see UWSParameters#set(String, Object) */ public final void setRunId(String name){ if (!phase.isFinished()){ try{ inputParams.set(PARAM_RUN_ID, name); }catch(UWSException ue){ ; } } } /** * Gets the owner of this job. * * @return The owner. */ public final JobOwner getOwner(){ return owner; } /** * Get the quote attribute of this job. * * @return The estimated duration of the job execution (in seconds). */ public final long getQuote(){ return quote; } /** *Sets the quote attribute of this job ONLY IF the job can updated (considering its current execution phase, see {@link JobPhase#isJobUpdatable()}).
* * @param nbSeconds The estimated duration of the job execution (in seconds). * * @see JobPhase#isJobUpdatable() */ public final void setQuote(long nbSeconds){ if (!phase.isFinished()) quote = nbSeconds; } /** * Gets the list of parameters' name. * * @return The additional parameters of this job. * * @see UWSParameters#getNames() */ public final SetAdds or updates the given parameters ONLY IF the job can be updated (considering its current execution phase, see {@link JobPhase#isJobUpdatable()}).
* *At the end of this function, the method {@link #applyPhaseParam(JobOwner)} is called so that if there is an additional parameter {@link #PARAM_PHASE} with the value: *
Adds or updates the given parameters ONLY IF the job can be updated (considering its current execution phase, see {@link JobPhase#isJobUpdatable()}).
* *At the end of this function, the method {@link #applyPhaseParam(JobOwner)} is called so that if there is an additional parameter {@link #PARAM_PHASE} with the value: *
Adds the given result in the results list of this job.
* *IMPORTANT: This function will throw an error if the job is finished.
* * @param res The result to add (not null). * * @return true if the result has been successfully added, false otherwise (for instance, if a result has the same ID). * * @throws UWSException If the job execution is finished that is to say if the phase is ABORTED, ERROR or COMPLETED. * * @see #isFinished() */ public boolean addResult(Result res) throws UWSException{ if (res == null) return false; else if (isFinished()){ UWSException ue = new UWSException(UWSException.NOT_ALLOWED, UWSExceptionFactory.jobModificationForbidden(getJobId(), getPhase(), "RESULT")); getLogger().logJob(LogLevel.ERROR, this, "ADD_RESULT", "Can not add the result \"" + res.getId() + "\" to the job \"" + getJobId() + "\": this job is already finished (or not yet started). Current phase: " + getPhase(), ue); throw ue; }else{ synchronized(results){ if (results.containsKey(res.getId())) return false; else{ results.put(res.getId(), res); return true; } } } } /** * Gets the execution manager of this job, if any. * * @return Its execution manager (may be null). */ public final ExecutionManager getExecutionManager(){ return getJobList().getExecutionManager(); } /** * Gets its jobs list, if known. * * @return Its jobs list (may be null). */ public final JobList getJobList(){ return myJobList; } /** *Sets its jobs list.
* *note 1: a job can change its jobs list ONLY WHILE PENDING !
*note 2: this job is removed from its previous job list, if there is one.
*note 3: this job is NOT automatically added into the new jobs list. Indeed, this function should be called by {@link JobList#addNewJob(UWSJob)}.
* * @param jobList Its new jobs list. note: if NULL, nothing is done ! * * @throws IllegalStateException If this job is not PENDING. * * @see JobList#removeJob(String) * @see JobList#getJob(String) */ protected final void setJobList(final JobList jobList) throws IllegalStateException{ if (jobList == null) return; else if (myJobList != null && jobList.equals(myJobList)) return; else if (myJobList == null || phase.getPhase() == ExecutionPhase.PENDING){ if (myJobList != null && myJobList.getJob(jobId) != null) myJobList.removeJob(jobId); myJobList = jobList; }else throw new IllegalStateException("Impossible to move a job (here: " + jobId + ") from a jobs list (here: " + ((myJobList == null) ? "null" : myJobList.getName()) + ") to another (here: " + ((jobList == null) ? "null" : jobList.getName()) + ") if the job is not PENDING !"); } /** * Gets the UWS URL of this job in function of its jobs list. * * @return Its corresponding UWSUrl. * * @see JobList#getUrl() * @see UWSUrl#jobSummary(String, String) */ public final UWSUrl getUrl(){ if (myJobList != null){ UWSUrl url = myJobList.getUrl(); if (url != null) return url.jobSummary(myJobList.getName(), jobId); } return null; } /* ******************** */ /* EXECUTION MANAGEMENT */ /* ******************** */ /** * Gets the time to wait for the end of the thread after an interruption. * * @return The time to wait for the end of the thread (a negative or null value means no wait for the end of the thread). */ public final long getTimeToWaitForEnd(){ return waitForStop; } /** * Sets the time to wait for the end of the thread after an interruption. * * @param timeToWait The new time to wait for the end of the thread (a negative or null value means no wait for the end of the thread). */ public final void setTimeToWaitForEnd(long timeToWait){ waitForStop = timeToWait; } /** *Starts the job by using the execution manager if any.
* * @throws UWSException */ public final void start() throws UWSException{ start(getJobList() != null); } /** *Starts the job.
* *Note: This function does nothing if the job is already running !
* * @param useManager true to let the execution manager deciding whether the job starts immediately or whether it must be put in a queue until enough resources are available, false to start the execution immediately. * * @throws NullPointerException If this job is not associated with a job list or the associated job list is not part of a UWS service or if no thread is created. * @throws UWSException If there is an error while changing the execution phase or when starting the corresponding thread. * * @see #isRunning() * @see UWSFactory#createJobThread(UWSJob) * @see ExecutionManager#execute(UWSJob) * @see #setPhase(ExecutionPhase) * @see #isFinished() * @see #startTime */ public void start(boolean useManager) throws UWSException{ // This job must know its jobs list and this jobs list must know its UWS: if (myJobList == null || myJobList.getUWS() == null) throw new IllegalStateException("A UWSJob can not start if it is not linked to a job list or if its job list is not linked to a UWS."); // If already running do nothing: else if (isRunning()) return; // If asked propagate this request to the execution manager: else if (useManager){ getJobList().getExecutionManager().execute(this); }// Otherwise start directly the execution: else{ // Create its corresponding thread: thread = getFactory().createJobThread(this); if (thread == null) throw new NullPointerException("Missing job work! The thread created by the factory is NULL => The job can't be executed!"); // Change the job phase: setPhase(ExecutionPhase.EXECUTING); // Set the start time: setStartTime(new Date()); // Run the job: thread.start(); (new JobTimeOut()).start(); // Log the start of this job: getLogger().logJob(LogLevel.INFO, this, "START", "Job \"" + jobId + "\" started.", null); } } /** * Stop/Cancel this job when its maximum execution duration has been reached. * * @author Grégory Mantelet (CDS;ARI) * @version 4.1 (09/2014) */ protected final class JobTimeOut extends Thread { public JobTimeOut(){ super(JobThread.tg, "TimeOut_" + jobId); } @Override public void run(){ long maxDuration = getExecutionDuration(); if (thread != null && thread.isAlive() && maxDuration != UNLIMITED_DURATION && maxDuration > 0){ try{ thread.join(maxDuration * 1000); if (!isFinished()) UWSJob.this.abort(); }catch(InterruptedException ie){ /* Not needed to report any interruption while waiting. */ }catch(UWSException ue){ getLogger().logJob(LogLevel.WARNING, UWSJob.this, "EXECUTING", "Unexpected error while waiting the end of the execution of the job \"" + jobId + "\" (thread ID: " + thread.getId() + ")!", ue); } } } } /** *Tells whether the job is still running.
* *Note: This function tests the execution phase (see {@link JobPhase#isExecuting()}) AND the status of the thread (see {@link #isStopped()}).
* * @return true if the job is still running, false otherwise. * * @see JobPhase#isExecuting() * @see #isStopped() */ public final boolean isRunning(){ return phase.isExecuting() && !isStopped(); } /** *Tells whether the job is already finished (completed, aborted, error, ...).
* *Note: This function test the execution phase (see {@link JobPhase#isFinished()}) AND the status of the thread (see {@link #isStopped()})
* * @return true if the job is finished, false otherwise. * * @see JobPhase#isFinished() * @see #isStopped() */ public final boolean isFinished(){ return phase.isFinished() && isStopped(); } /** *Stops immediately the job, sets its phase to {@link ExecutionPhase#ABORTED ABORTED} and sets its end time.
* *IMPORTANT: If the thread does not stop immediately the phase and the end time are not modified. However it can be done by calling one more time {@link #abort()}. * Besides you should check that you test regularly the interrupted flag of the thread in {@link JobThread#jobWork()} !
* * @throws UWSException If there is an error while changing the execution phase. * * @see #stop() * @see #isStopped() * @see #setPhase(ExecutionPhase) * @see #setEndTime(Date) */ public void abort() throws UWSException{ // Interrupt the corresponding thread: stop(); if (isStopped()){ if (!phase.isFinished()){ // Try to change the phase: setPhase(ExecutionPhase.ABORTED); // Set the end time: setEndTime(new Date()); }else if (thread == null || (thread != null && !thread.isAlive())) throw new UWSException(UWSException.BAD_REQUEST, UWSExceptionFactory.incorrectPhaseTransition(getJobId(), phase.getPhase(), ExecutionPhase.ABORTED)); }else getLogger().logJob(LogLevel.WARNING, this, "ABORT", "Abortion of the job \"" + getJobId() + "\" asked but not yet effective (after having waited " + waitForStop + "ms)!", null); } /** *Stops immediately the job, sets its error summary, sets its phase to {@link ExecutionPhase#ERROR} and sets its end time.
* *IMPORTANT: If the thread does not stop immediately the phase, the error summary and the end time are not modified. * However it can be done by calling one more time {@link #error(ErrorSummary)}. * Besides you should check that you test regularly the interrupted flag of the thread in {@link JobThread#jobWork()} !
* * @param error The error that has interrupted this job. * * @throws UWSException If there is an error while setting the error summary or while changing the phase. * * @see #stop() * @see #isStopped() * @see JobPhase#isFinished() * @see #setErrorSummary(ErrorSummary) * @see #setPhase(ExecutionPhase) * @see #setEndTime(Date) */ public void error(ErrorSummary error) throws UWSException{ // Interrupt the corresponding thread: stop(); if (isStopped()){ if (!phase.isFinished()){ // Set the error summary: setErrorSummary(error); // Try to change phase: setPhase(ExecutionPhase.ERROR); // Set the end time: setEndTime(new Date()); }else if (thread != null && !thread.isAlive()) throw new UWSException(UWSException.BAD_REQUEST, UWSExceptionFactory.incorrectPhaseTransition(jobId, phase.getPhase(), ExecutionPhase.ERROR)); }else getLogger().logJob(LogLevel.WARNING, this, "ERROR", "Stopping of the job \"" + getJobId() + "\" with error asked but not yet effective (after having waited " + waitForStop + "ms)!", null); } /** Used by the thread to known whether the {@link #stop()} method has already been called, and so, that the job is stopping. */ protected boolean stopping = false; /** * Stops the thread that executes the work of this job. */ protected void stop(){ if (!isStopped()){ synchronized(thread){ stopping = true; // Interrupts the thread: thread.interrupt(); // Wait a little for its end: if (waitForStop > 0){ try{ thread.join(waitForStop); }catch(InterruptedException ie){ getLogger().logJob(LogLevel.WARNING, this, "END", "Unexpected InterruptedException while waiting for the end of the execution of the job \"" + jobId + "\" (thread ID: " + thread.getId() + ")!", ie); } } } } } /** * Tells whether the thread is different from null, is not alive, is interrupted or is finished (see {@link JobThread#isFinished()}). * * @return true if the thread is not still running, false otherwise. */ protected final boolean isStopped(){ return thread == null || !thread.isAlive() || thread.isInterrupted() || thread.isFinished(); } /** *Stops the job if running, removes the job from the execution manager, stops the timer for the execution duration * and may clear all files or any other resources associated to this job.
* *By default the job is aborted, the {@link UWSJob#thread} attribute is set to null, the timers are stopped and uploaded files, results and the error summary are deleted.
*/ public void clearResources(){ // If still running, abort/stop the job: if (isRunning()){ try{ abort(); }catch(UWSException e){ getLogger().logJob(LogLevel.WARNING, this, "CLEAR_RESOURCES", "Impossible to abort the job \"" + jobId + "\" => trying to stop it...", e); stop(); } } // Remove this job from its execution manager: if (getJobList() != null) getJobList().getExecutionManager().remove(this); thread = null; // Clear all uploaded files: IteratorGets the error (if any) which has occurred during the job execution.
* *Note: In the case an error summary can not have been published, the job has no error summary. * However the last {@link UWSException} caught during the execution of a {@link JobThread} is saved and is available thanks to {@link JobThread#getError()}. * In that case, the {@link UWSJob#getWorkError() getWorkError()} method can be used to get back the occurred error.
* * @return The error which interrupts the thread or null if there was no error or if the job is still running. */ public final UWSException getWorkError(){ return (thread == null || !thread.isAlive()) ? null : thread.getError(); } /* ************* */ /* SERIALIZATION */ /* ************* */ @Override public String serialize(UWSSerializer serializer, JobOwner user) throws UWSException, Exception{ if (user != null && !user.equals(getOwner()) && !user.hasReadPermission(this)) throw new UWSException(UWSException.PERMISSION_DENIED, UWSExceptionFactory.readPermissionDenied(user, false, getJobId())); return serializer.getJob(this, true); } /** * Serializes the specified attribute of this job by using the given serializer. * * @param attributes All the given attributes (may be null or empty). * @param serializer The serializer to use. * * @return The serialized job attribute (or the whole job if attributes is an empty array or is null). * * @throws Exception If there is an unexpected error during the serialization. * * @see UWSSerializer#getJob(UWSJob, String[], boolean) */ public String serialize(String[] attributes, UWSSerializer serializer) throws Exception{ return serializer.getJob(this, attributes, true); } /** * Serializes the specified attribute of this job in the given output stream by using the given serializer. * * @param output The output stream in which the job attribute must be serialized. * @param attributes The name of the attribute to serialize (if null, the whole job will be serialized). * @param serializer The serializer to use. * * @throws Exception If there is an unexpected error during the serialization. * * @see #serialize(String[], UWSSerializer) */ public void serialize(ServletOutputStream output, String[] attributes, UWSSerializer serializer) throws UWSException, IOException, Exception{ String errorMsgPart = null; if (attributes == null || attributes.length <= 0) errorMsgPart = "the job \"" + getJobId() + "\""; else errorMsgPart = "the given attribute \"" + attributes[0] + "\" of the job \"" + getJobId() + "\""; if (output == null) throw new NullPointerException("Missing serialization output stream when serializing " + errorMsgPart + "!"); String serialization = serialize(attributes, serializer); if (serialization == null){ getLogger().logJob(LogLevel.ERROR, this, "SERIALIZE", "Error while serializing " + errorMsgPart + ": NULL was returned.", null); throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Incorrect serialization value (=NULL) ! => impossible to serialize " + errorMsgPart + "."); }else{ output.print(serialization); output.flush(); } } @Override public String toString(){ return "JOB {jobId: " + jobId + "; phase: " + phase + "; runId: " + getRunId() + "; ownerId: " + owner + "; executionDuration: " + getExecutionDuration() + "; destructionTime: " + getDestructionTime() + "; quote: " + quote + "; NbResults: " + results.size() + "; " + ((errorSummary != null) ? errorSummary.toString() : "No error") + " }"; } @Override public int hashCode(){ return jobId.hashCode(); } /** *2 instances of AbstractJob are equals ONLY IF their ID are equals.
* *Note: If the given object is not an AbstractJob, FALSE is returned.
* * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object anotherJob){ if (anotherJob instanceof UWSJob) return jobId.equals(((UWSJob)anotherJob).jobId); else return false; } }