C. How to customize a Job ?
3.a. Execution WITHOUT queue
How does it work ?
As seen previously the task of a job is described by the method jobWork(). During its life the status of a job may change, but always starts with PENDING. This phase is stored in the attribute phase. In addition to the getter of this attribute, isRunning() and isFinished() lets indicating whether the job is currently executing or finished.
The execution of a job can be managed thanks to the following methods:
- start() (=start(false)): Starts the execution of the job.
- if isRunning()=true then nothing is done !
- Change the execution phase to EXECUTING (setPhase(ExecutionPhase))
- Create the thread and start it (its execution will call jobWork())
- Set the start time (setStartTime(Date))
- Start the timer for the maximum execution duration if positive and different from 0
- abort(): Aborts/Interrupts the job.
- Stop the thread (stop())
- if isStopped()=false then return here
- Change the execution phase to ABORTED (setPhase(ExecutionPhase))
- Set the end time (setEndTime(Date))
- error(ErrorSummary): Stops immediately the job with the given error.
- Stop the thread (stop())
- if isStopped()=false then return here
- Set the error summary (setErrorSummary(ErrorSummary))
- Change the execution phase to ERROR (setPhase(ExecutionPhase))
- Set the end time (setEndTime(Date))
- error(UWSException): Stops immediately the job with the given UWSException.
Call UWSToolBox.publishErrorSummary(AbstractJob, String, ErrorType).
As you can notice there is no method for the case the job ends successfully. Actually it is the role of the thread ! When the call to jobWork() has just finished, it sets the phase to COMPLETED (setPhase(ExecutionPhase)) and sets the end time (setEndTime(Date)).
In a UWS to start or to abort a job, a POST request with the parameter PHASE=RUN or PHASE=ABORT must be sent to the job. As said in the part A.3. Defining the job, this parameter is stored in the additionalParameters attribute. To apply this parameter, the method applyPhaseParam() must be called, which is done by default after each update of any job parameter. Thus, we get:
All these methods can also be used if the job is executed alone (that's to say: independently from a UWS) !
Stopping job
The method stop() is used to stop the thread when the user asks to abort the job or when the maximum execution duration is elapsed or when an error occurs. However stopping a thread is not so trivial. That's why you should know some things about the way it is done in this library through the method stop().
Firstly the thread is stopped by calling the function Thread.interrupt(). If the thread is waiting (Thread.wait()) or sleeping (Thread.sleep(long) then, it will receive an InterruptedException. Otherwise its interrupted flag is set. That's why you should check as often as possible this flag in jobWork(). If it is true, it is your responsibility to throw either an InterruptedException or a UWSException. The other solution is to call yourself the abort() or error(ErrorSummary) function.
Secondly after the interruption of the thread, stop() waits until the thread is really stopped by using Thread.join(long). The time to wait is by default 1000ms (1 second) and can be changed thanks to setTimeToWaitForEnd(long).
Changing the execution phase
A job has an attribute which indicates its current status (pending, queued, executing, completed, error, ...), also called execution phase. All the default possible execution phases are listed in the enumeration class: ExecutionPhase. Transitions between phases are imposed by the IVOA Recommendation:
In order to manage more easily these transitions and particularly to allow their modifications, they are not managed by AbstractJob but by the class JobPhase. Consequently the current execution phase of a job is got and set through an instance of JobPhase. So we get the following simplified class diagram:
How to customize ?
All the above functions can be overrided. Thus with start(boolean), abort(), error(ErrorSummary) and error(UWSException) you can customize what to do when starting or aborting (with or without error) the job. Besides the interruption of the thread can also be modified by overriding stop(). You should also remember that with setTimeToWaitForEnd(long) you can change the maximum time the stop() method must wait until the end of the thread.
About the execution phases you should know that all the default transitions are already implemented, except for HELD, SUSPENDED and UNKNOWN which are not used in this library. As you have seen, to change the current phase you must use the setPhase(ExecutionPhase, boolean) function. Actually it is a kind of hub between the set...Phase(boolean) functions (i.e. setExecutingPhase(boolean)). There is one function set...Phase(boolean) per execution phase. Thus if you want to change a phase transition action:
- Override the corresponding function. For instance to change what is done when going to the EXECUTING phase you have to override setExecutingPhase(boolean).
- Once done, set the extension of JobPhase to your uws object thanks to the function setPhaseManager(JobPhase).
You must take care to the current execution phase before changing it ! For instance: it would be a non-sense to allow a job going from ERROR to COMPLETED !
Finally if some actions must trigger an action (i.e. RUN => start()):
- Override applyPhaseParam(),
- Call the super method,
- Fetch the PHASE parameter from the additionalParameters attribute,
- And in function of its value execute the appropriate action.