[Home] [Index] [Previous] [Next]

More on Jobs

Comletion States

So far we've written a nice job that run's without errors - but what happens if something goes wrong?

Oddjob will catch all Throwables during job execution and set the job state to Exception. Let see with an example.

package org.oddjob.devguide;

public class NaughtyJob implements Runnable {

    public void run() {
        throw new RuntimeException("I won't run. I won't!");
    }
}

And run it...

A Naughty Job

You can try resetting the job and re-running it however this job will not start behaving! The import thing is that it's naughtiness is contained.

Oddjob will also recognise the result property and use it to either set a job complete or not complete. If the result is 0 the job is state is complete, any other value and it's not complete.

package org.oddjob.devguide;

public class NotCompleteJob implements Runnable {

    public int getResult() {
        return 1;
    }
    
    public void run() {
    }
}

And run it...

A Naughty Job

Stopping a Job

To stop a job Oddjob will interrupt the thread it's running. Long running jobs should be written to cope with this.

Here's an example.

package org.oddjob.devguide;

public class StoppingJob implements Runnable {

    public void run() {
        synchronized(this) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("OK - I'll stop.");
            }
        }
    }
}

Logging

Oddjob uses log4j. If you also use Log4j, any logging in your job's run() method will be captured by Oddjob and displayed in the log panel of Oddjob Explorer.

Services

Sometimes you don't want a job to run and complete, but to keep running in the background, probably providing some kind of service, such as Scheduling.

Oddjob will recognise the method signature of public void start() and public void stop() and treat that object as a service. When you run the job, the start method will be called, but when it returns the state will still be seen as executing. The service will be stopped when you stop the job.

Here's an example.

package org.oddjob.devguide;

import java.util.concurrent.CountDownLatch;

public class SimpleService {

    private volatile boolean stop;

    private volatile Thread thread;
    
    public void start() throws InterruptedException {
        
        final CountDownLatch serviceStarted = new CountDownLatch(1);
        
        thread = new Thread(new Runnable() {
            public void run() {
                while (!stop) {
                    System.out.println("I could be useful.");
                    synchronized (SimpleService.this) {
                        serviceStarted.countDown();
                        try {
                            SimpleService.this.wait();
                        } catch (InterruptedException e) {
                            break;
                        }
                    }
                }
                System.out.println("Service Stopping.");
            }});
        thread.start();
        
        serviceStarted.await();
    }

    public void stop() throws InterruptedException {
        synchronized(this) {
            stop = true;
            notifyAll();
        }
        if (thread != null) {
            thread.join();
        }
    }
}

A service has the advantage that when used in a sequential job, jobs after the service will run once the service has started, and can then use that service for something.

Because the job that runs immediately after the service may consume that service, the writer of the service we must ensure that the service is guaranteed to be available once the start method completes (or an Exception is thrown). In our example we ensure this is case by using a CountDownLatch.

The observant reader may be wondering what happens if the thread executing start has been interrupted? Won't the service be started but an Exception also thrown? Oddjob will ensure that the interrupted flag is cleared before it enters the start method so you don't need to check.

Ensuring that a service is stopped before the stop method completes isn't as important. It depends on the nature of the service. What happens if a service is stopped and started quickly for instance? In our example it could be argued that it wouldn't matter that a new thread was created before the previous one had completed - however if we wished to write a unit test that relied on the order of output (which you can find in the source distribution) we need to guarantee the behaviour of stop which we do by using thread.join().

Here's a configuration that uses this service.

<oddjob>
    <job>
      <sequential>
      <jobs>
        <bean id="service" class="org.oddjob.devguide.SimpleService"/>
        <echo>Service Has Started.</echo>
        <stop job="${service}"/>
        <echo>Service Has Stopped.</echo>
      </jobs>
      </sequential>
    </job>
</oddjob>

First the service is started, next an echo tells us the service has started, then the service is stopped, and finally another echo tells us the service has stopped. Here's the result of running this configuration with Oddjob.

I could be useful.
Service Has Started.
Service Stopping.
Service Has Stopped.
 

Because of the way we wrote our service this order is guaranteed. To summarise: You need to guarantee a service is started in start() but you can use your discretion as to whether it's really stopped by stop().

Serialization

If your job implements java.io.Serializable and Oddjob is running with a persister then Oddjob will serialise the state of your job when it completes. When Oddjob next runs, it re-creates the job from the serialised form.

After your job is restored, Oddjob will continue to configure your job from the configuration file. Thus any serialised properties that appear in the configuration will be overwritten.


[Home] [Index] [Previous] [Next]