Package com.polarion.platform.jobs


package com.polarion.platform.jobs
Provides classes and interfaces for handling jobs.

Usage Scenarios

Simple job without parameters

Let's say we want to provide job named my.job (convention says that job name has logical components delimited by periods). This job will not be configurable (or even visible) by job configuration UI (like scheduler UI or similar).

Step 1 - provide job unit

We will provide job based on AbstractJobUnit, because it implements everything we need:
  private class MyJobUnit extends AbstractJobUnit {
  
     public int getWorkLength() {
         // can be based on some parameters and set during activate()
         return 10;
     }
  
     protected void runInternal(IProgressMonitor progress) {
       getLogger().info("Started");
       try {
         progress.beginTask("Starting", 0); // totalWork is ignored
         // since getWorkLength() returns 10 we must work at most 10 work units
         for (int i = 1; i <= 10; i++) {
           if (progress.isCanceled()) {
             return getStatusCancelled("Cancelled");
           }
           progress.subtask("Tick number " + i);
           // here some work can be done, possibly with temporary output written to getWorkDir()
           progress.worked(1);
         }
       } catch (Exception e) {
         return getStatusFailed("Failure", e);
       } finally {
         progress.setTaskName("Finished");
         progress.done();
       }
       return getStatusOK("Success");
     }
  
  }

Step 2 - register proper factory for your job unit

Place into some hivemodule.xml:
        <contribution configuration-id="com.polarion.platform.jobs.configuration">
                <jobUnitFactory name="my.job" factory="myJobUnitFactory"/>
        </contribution>
        
        <service-point id="myJobUnitFactory" interface="com.polarion.platform.jobs.IJobUnitFactory">
                <invoke-factory>
                        <construct class="com.polarion.platform.jobs.spi.GenericJobUnitFactory">
                                <string>my.job</string>
                                <string>My Job</string>
                                <object>class:package.MyJobUnit</object>
                        </construct>
                </invoke-factory>
        </service-point>
This will register new job unit factory for job unit my.job creating instances of package.MyJobUnit.

Step 3 - spawn job based on your job unit

  IJobService jobService = ...;
  IJobManager mgr = jobService.getJobManager();
  IJobUnitRepository repo = jobService.getJobUnitRepository();
  IJobUnit unit = (IJobUnit) repo.getJobUnitFactory("my.job")
    .createJobUnit("My job started at " + System.currentTimeMillis());
  IJob job = mgr.spawnJob(unit, null);
  job.schedule();
  // now the activation phase finished (see next usage scenarios) and job started (or is waiting for start)
  // we can wait for the job to end or just leave it alone and use state listeners on IJob
  job.join();
  // now the job finished and its status can be inspected
  if (job.getStatus().getType() != JobStatusType.STATUS_TYPE_OK) {
    // deal with failure
  }

Names explained

Jobs have various names (all are required):
job factory name
Returned from IJobUnitFactory.getName() and passed to IJobUnitRepository.getJobUnitFactory(). Is kind of job unit class name (but more human-readable). Should be unique (although that fact is not checked). Examples are "master.build", "cleanup".
job name
Returned from IJobUnit.getName() and IJob.getName(). Name of given instance of job unit (and thus the whole job). Should be short, human-readable and contain some information based on job's configuration. Is immutable during job's life. Examples are "Build of org.polarion.subversive", "Cleanup of /tmp".
job label
Returned from IJobDescriptor.getLabel(). Short, human-readable name of given job unit. Kind of job factory name, but even more human-readable. Is visible in job configuration UI (like scheduler UI). Examples are "Project builds", "Directory cleanup".

Job with typed interface

It is considered good practice to provide typed interface for job units if those jobs have some parameters:
  public interface IMyJobUnit extends IJobUnit {
    
    public static final String JOB_NAME = "my.job";
    
    void setSomeParameter(String value);
    String getSomeResult();
    ...
    
  }
  
  private class MyJobUnit extends AbstractJobUnit implements IMyJobUnit {
  
     private String someParameter;
     private String someResult;
  
     public void setSomeParameter(String value) {
       someParameter = value;
     }
     
     public String getSomeResult() {
       return someResult;
     }
  
     public void activate() {
       // check parameters
       if (someParameter == null) {
         throw new IllegalArgumentException("setSomeParameter() was not called");
       }
       ...
     }
     
     protected void runInternal(...) {
       ...
       // set results (of course this can be set even during activation or at start of runInternal())
       someResult = ...;
       return getStatusOK(...);
     }
  
  }
Those who want to spawn jobs based on this job unit may do this:
  IJobService jobService = ...;
  IJobManager mgr = jobService.getJobManager();
  IJobUnitRepository repo = jobService.getJobUnitRepository();
  IMyJobUnit unit = (IMyJobUnit) repo.getJobUnitFactory(IMyJobUnit.JOB_NAME).createJobUnit(...);
  unit.setSomeParameter(...);
  IJob job = mgr.spawnJob(unit, null);
  job.schedule();
  // let's wait for the result
  job.join();
  return job.getSomeResult();

Job with dependencies

Job may depend on arbitrary number of other jobs (which are called child jobs).
  public void activate() {
    IJobUnit unit = ...; // normal job unit creation using some job unit factory
    unit.setWorkDir(getWorkDir()); // pass it the same work dir
    getJob().getJobManager().spawnJob(unit, getJob());
    ...
  }
Child jobs can be spawned only inside activate(). Parent job's run() will be executed after child jobs complete regardless of their status. Job units based on AbstractJobUnit will get their runInternal() executed only if all child jobs finish successfully.

Job with resource locking

Job may lock resources to which it wants to have exclusive access. Job manager will ensure that no two jobs claiming the same resource (or, better said, conflicting resources) run at the same time. Resources are locked since the first child runs until the job is finished. Resources locked by children are automatically locked by parents.
  public void activate() {
    IJobResource resource = new SomeJobResourceImplementation(...);
    getJob().addResource(resource);
    ...
  }
Resources can be locked only inside activate().

Platform-aware job

For more complicated jobs, especially those who want to have injected services and configuration, special IJobUnitFactory should be created and job unit placed as nested non-static class with access to factory's injected parameters. Example:
  public class MyJobUnitFactory implements IJobUnitFactory {
  
    protected ISomeService someService;
    
    public void setSomeService(ISomeService someService) {
      this.someService = someService;
    }
  
    private class MyJobUnit extends AbstractJobUnit {
      
     protected void runInternal(...) {
       ...
       someService.someMethod();
       ...
     }
    
    }
  
    public IJobUnit createJobUnit(String name) throws GenericJobException {
      return new MyJobUnit(name, this);
    }
  
  }
ISomeService can be auto-wired or set explicitly in hivemodule.xml:
        <service-point id="myJobUnitFactory" interface="com.polarion.platform.jobs.IJobUnitFactory">
                <invoke-factory>
                        <construct class="package.MyJobUnitFactory">
                          <set-service property="someService" service-id="someServiceId"/>
                        </construct>
                </invoke-factory>
        </service-point>