The Artima Developer Community
Sponsored Link

Make Room for JavaSpaces, Part II
Build a Compute Server with JavaSpaces
by Eric Freeman
First Published in JavaWorld, January 2000

<<  Page 5 of 7  >>


The Worker

The worker is a straightforward concept: it continually looks for a task, takes it from the space, computes it, returns a result (if the task specifies that this is to be done), and then starts over. Here is the code:

package javaworld.simplecompute;

import jsbook.util.*;

import net.jini.core.entry.Entry;

public class Worker {
    JavaSpace space;
    public static void main(String[] args) {
        Worker worker = new Worker();
    public Worker() {
        space = SpaceAccessor.getSpace();
    public void startWork() {
        TaskEntry template = new TaskEntry();
        for (;;) {
            try {
                TaskEntry task = (TaskEntry)
                    space.take(template, null, Long.MAX_VALUE);
                Entry result = task.execute();
                if (result != null) {
                    space.write(result, null, 1000*60*10);
            } catch (Exception e) {
                System.out.println("Task cancelled");

I started by importing the Entry and Lease core Jini packages, along with the JavaSpace class. I also imported the utility package jsbook.util, from JavaSpaces Principles, Patterns, and Practice by Susanne Hupfer, Ken Arnold, and myself, which is freely available on the Web (see Resources). I will say more about this class shortly; it contains some utility methods with which you can access a space via Jini.

With the imports out of the way, let's step through the class definition. First, you define a static main method, which simply instantiates a new Worker and calls its startWork method. Before looking at this startWork method, lets first take a closer look at the Worker constructor. This constructor includes a call to SpaceAccessor.getSpace, which is supplied in the jsbook.util package. This method looks for a JavaSpace service on the local Jini network with the name JavaSpace, obtains a handle to the space, and then returns it as a result of the call. I am leaving out some of the behind-the-scenes details, but will return to them later in the series. (If you can't wait, you can find further details in JavaSpaces Principles, Patterns, and Practice.) For now, let's assume that, once this call has finished, you have a handle to a space.

Now let's go back to our startWork method. In this method, we first want to create a template to retrieve tasks from the space. However, there's a bit of a problem, since we must use an actual class as a template; we can't use interfaces (or abstract classes) as templates, so our Command interface cannot be used to retrieve tasks from the space. To remedy this, we need to create a base task class, which can, as the name implies, be used as the basis for all tasks. Here is such a class:

package javaworld.simplecompute;

import net.jini.core.entry.Entry;

public class TaskEntry implements Command {
    public Entry execute() {
        throw new RuntimeException(
            "TaskEntry.execute() is not implemented.");

The TaskEntry implements the Command interface. Since TaskEntry can't be an abstract class (if it were, the worker wouldn't be able to instantiate it and use it as a template), we need to provide an implementation of the execute method. Rather than providing a concrete implementation that does nothing, we instead throw a runtime exception if this method is ever invoked. This encourages subclasses to implement this method, even though they have no compile-time requirement to do so.

Now that we have a TaskEntry, let's return to the startWork method. The method first instantiates a template from TaskEntry that can be used to retrieve any task written into the space. Recall from Part 1 of our series that a template can match an entry of the same type or subtype, so any subclasses of TaskEntry will match this template.

With a template in hand, we then enter a continual for loop. Within this loop, we call take on the space to retrieve a task entry, waiting as long as necessary for one to arrive. Once we've retrieved a task, we then call its execute method. If a nonnull result is returned, we then write it back into the space with a lease time of 10 minutes (this is expressed in the code as 1000*60*10 -- time here is measured in milliseconds). If the master takes longer than 10 minutes to retrieve the result, then it will be removed by the space in order to keep things tidy. After writing back the result, the worker then continues with the next task. If any exceptions occur along the way (if the space throws an exception on the take or write, say, or if the execute method throws an exception), then the task is ignored and the worker looks for the next task. This isn't the optimal way to handle these exceptions, since the task will be lost forever if it's not computed and thrown away. Susanne and I will fix this flaw in an upcoming article, using Jini's transactions.

Our compute server is now complete! This is the power of Jini and JavaSpaces: in only about 30 lines of code, we've implemented a complete compute server. I'd like to add some things, such as the transactions mentioned above, to improve some aspects of the server; but even at this early stage of development, we'll find that the space takes care of almost everything for us. It handles the communication between master and worker, the persistence of the task and result entries, and the automatic class loading. When we do add the transactions (a task that won't involve a lot of code), it will even handle the problem of partial failure.

Of course, the server isn't much good without a master to generate some tasks; so let's flesh out our project and develop one.

<<  Page 5 of 7  >>

Sponsored Links

Copyright © 1996-2018 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use