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 6 of 7  >>

Advertisement

The Master

I am going to develop a simple master that generates two kinds of tasks: addition and multiplication tasks. In each case, the master initializes the tasks with two integers and then writes the task into a space. For addition tasks, the worker adds the two integers together and returns the result; likewise, for multiplication tasks, the two numbers are multiplied and then the results are written to the space. These tasks are overly simple and not particularly interesting in and of themselves, but will serve as a means of testing the compute server.

Let's start by defining the tasks. Here is the AddTask:


package javaworld.simplecompute;

import net.jini.core.entry.Entry;

public class AddTask extends TaskEntry {
    public Integer a;
    public Integer b;
    
    public AddTask() {}
    
    public AddTask(Integer a, Integer b) {
        this.a = a;
        this.b = b;
    }
    
    public Entry execute() {
        Integer answer = new Integer(a.intValue() +
            b.intValue());
        AddResult result = new AddResult(a, b, answer);
        return result;  
    }
}

Here, we first define two fields to hold the values that are to be added, a and b. Recall from Part 1 of our series that these fields must be objects and declared public if we want them to be useful in JavaSpaces applications. Next, we define two constructors: the no-arg constructor required by all entries, and a constructor that accepts the two integer values.

We then define the execute method, which simply adds the two integers together and then creates an AddResult object that is returned as a result of the method.

It should be obvious that defining the multiplication task will follow exactly the same pattern, except that the execute method will multiply the two values rather than adding them, and return a MultResult object rather than an AddResult object. You can see the definition of MultTask in MultTask.java (see Resources).

Now let's look at the AddResult and MultResult result entries, both of which are derived from a base class called ResultEntry. (We'll see why we've created this base class shortly.) Here is the definition of ResultEntry:


package javaworld.simplecompute;

import net.jini.core.entry.Entry;

public class ResultEntry implements Entry {
    public Integer a;
    public Integer b;
    public Integer answer;
    
    public ResultEntry() {}
}

The ResultEntry class defines three fields: the two integer inputs to the computation (called a and b), and an integer output (called answer). We also provide the mandatory no-arg constructor.

The AddResult entry is subclassed from ResultEntry, and looks like this:


package javaworld.simplecompute;

public class AddResult extends ResultEntry {
    
    public AddResult() {}
    
    public AddResult(Integer a, Integer b, Integer answer) {
        this.a = a;
        this.b = b;
        this.answer = answer;
    }
    
    public String toString() {
        return a + " plus " + b + " = " + answer;
    }
}

This class provides two constructors and a toString method that prints the values of a, b, and the total value. As you might expect, the MultResult class is very similar to AddResult (you can find the definition for MultResult in Resources as well).

Now we can define the Master class:


package javaworld.simplecompute;

import jsbook.util.*;

import net.jini.core.entry.*;
import net.jini.core.lease.Lease;
import net.jini.space.JavaSpace;

public class Master {
    private JavaSpace space;
    
    public static void main(String[] args) {
        Master master = new Master();
        master.startComputing();
    }

    private void startComputing() {
        space = SpaceAccessor.getSpace();

        generateTasks();
        collectResults();
    }

    private void generateTasks() {
        for (int i = 0; i < 10; i++) {          
            writeTask(new AddTask(new Integer(i), new Integer(i)));
        }
        for (int i = 0; i < 10; i++) {
            writeTask(new MultTask(new Integer(i), new Integer(i)));
        }
    }

    private void collectResults() {
        for (int i = 0; i < 20; i++) {
            ResultEntry result = takeResult();
            if (result != null) {
                System.out.println(result);
            }
        }
    }

    private void writeTask(Command task) {
        try {
            space.write(task, null, Lease.FOREVER);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected ResultEntry takeResult() {
        ResultEntry template = new ResultEntry();
        try {
            ResultEntry result = (ResultEntry)
                space.take(template, null, Long.MAX_VALUE);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }     
}

The Master class is similar to the Worker class. You include the same imports and provide a static main class that creates an instance of the Master class, on which it calls the startComputing method. This method calls two of Master's methods: generateTasks and collectResults.

The generateTasks method writes 10 addition tasks and 10 multiplication tasks into the space for the workers to compute. It does this by performing two for loops from 0 to 9, calling writeTask to write a newly instantiated task entry into the space on each pass through the loop. In the first loop, an AddTask is written; a MultTask is written in the second. Both tasks are instantiated with the values (0,0), (1,1), and so on. The writeTask method simply writes the task into the space with an indefinite lease -- that is, the task will remain in the space as long as the space is running.

The collectResults method retrieves the results of these tasks from the space. It does this by iterating through the total number of tasks (in this case, 20) and calling the takeResult method. Once a result is returned from this method, it is printed, which results in its toString method being called. If the result is a AddResult, then we should get output that shows that addition took place; if it is MultResult, we should get output that shows the products of multiplication.

Let's quickly look at the takeResult method. This method first creates a template to match results. Note that it creates a ResultEntry, the superclass of both AddResult and MultResult. By doing this, the method can match any entry that subclasses the ResultEntry, and thus can match both of the result-entry types with which we are dealing here. The method then performs a take operation, waiting as long as necessary for a result entry to arrive in the space.

We now have the complete master code, with its associated tasks, so let's take it all out for a spin. With our Jini and JavaSpaces services running, we now run a couple instances of the worker and one instance of the master. We should see something like the following as output from the master:


0 plus 0 = 0
1 plus 1 = 2
2 plus 2 = 4
3 plus 3 = 6
4 plus 4 = 8
0 times 0 = 0
1 times 1 = 1
5 plus 5 = 10
2 times 2 = 4
3 times 3 = 9
4 times 4 = 16
5 times 5 = 25
6 times 6 = 36
7 times 7 = 49
8 times 8 = 64
9 times 9 = 81
6 plus 6 = 12
7 plus 7 = 14
8 plus 8 = 16
9 plus 9 = 18

Each task was computed and its result was returned in the result entry. Note that, because of the semantics of the take operation on the space, the tasks may be computed and returned in an arbitrary order. In general, this isn't a problem; in those cases in which it is, you can write more sophisticated code to enforce an ordering.

<<  Page 6 of 7  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us