Distributed Computation Framework

      Developers Guide

Home

Download

CVS

Javadoc

SourceForge

Links

Screenshots
About this guide
This guide is intended for developers who wish to write their own Task-Solver pairs to be used with DCF. It will give a breif overview of the system and its components and will guide you by giving an example of a Task-Solver pair.

DCF overview
In this section I will go over the main components of the DCF and their functions.

1. Task-Solver - Are a pair of classes. The Task holds the information to be processed. It also, has a method to handle the data once the task is returned to the server, and has the ability to create new tasks. The Solver gets the Task's data, does its thing and sets the results back in the task.

2. Distributer - This is the heart of the DCF. It handles the distribution of tasks to clients, holds a remote reference to each client and handles the tasks as they are returned from the clients.

3. AutoLoader - Is a helper class to the Distributer. It automatically loads Task-Solver pairs as the server starts.

4. ClassServer - Is a simple HTTP server, used by the Distributer to dynamically serve classes to clients.

5. ClientsUpdater - Is a thread activated by the Distributer. It goes over the clients list, every once in a while, and checks the task state. If the connection to the clients was lost, it removes the client from the list and restores the task back to the tasks list.

6. DCFClassLoader - Is a utility class to load classes from the .class file without knowing its name.

7. Worker - Does all the client side work. It connects to the server, downloads the next available Task, gets the Solver for the task, solves the task and then sends the task back to the server.

Data flow
This diagram ilustrates the data flow of the system.

First the Work (Task) is assigned to the Worker. Then the Worker gets additional files from the ClassServer and starts working. Meanwhile, the ClientsUpdater monitors the client progress. When the client is done it returns the Task to the Distributer.

Sample Task-Solver
In this part we will create a Randomizer Task-Solver. The complete source code for this example can be found under the tasks directory in the DCF distribution. The randomizer Task-Solver is does nothing useful, the Task is an array of ints and the Solver simply puts other ints into the array.

Task
We will start by importing the needed packages for this class.

package dcf.tasks;
import dcf.server.*; import dcf.client.Worker; import java.io.*; import java.util.*;

Now we will declare the class. Note that it must implement the Task interface and the Serializable interface so that it can be transported to the client.
public class RandomizerTask implements Task, Serializable {

We need to create some variables:
1. name is the Task name which will be used to index the task on the server.
2. t is a Vector to hold the list of tasks.
3. r is a Vector to hold the list of results.
4. parent is a reference to the parent Worker. We need this so that the Solver could compunicate with the Worker through the Task.
  private String name = "Randomizer Task"; 
  private Vector t = new Vector();
  private Vector r = new Vector();
  private Worker parent;

In the constructor we enter some sample data to the task.
  public RandomizerTask() {
    for (int i = 0; i < 100; i++) {
      t.addElement(new Integer(i));
    }
  }

The following methods are very simple so I will not explain them.
  public void setData(Object obj) {
    t = (Vector)obj;
  }

  public Object getData() {
    return t;
  }

  public int getSize() {
    return t.size();
  }

  public void addResult(Object obj) {
    r.addElement(obj);
  }

  public Object getResults() {
    return r;
  }

  public void setName(String n) {
    name = n;
  }

  public String getName() {
    return name;
  }

  public void setParent(Worker w) {
    parent = w;
  }

This method is used if the task data is not already divided into a list of subtasks (t). For instance the raw data for the task is a long String and we want to process it by first breaking it to a few smaller parts. This method is called by the Worker before it start processing the task.
  public void divide() {
  }

This will allow the Solver to see if there is more data to process in this task.
  public boolean hasMore() {
    return (t.size() > 0);
  }

Returns the next piece of data to process. In this case will return an Integer to work with. It removes it from t. If there is no more data to process it will return null.
  public Object next() {
    if (hasMore()) {
      Object o = t.firstElement();
      t.remove(0);
      return o;
    }
    return null;
  }

If a task is circular, the Distributer will call the generate() method to create new tasks once the task is returned to the server.
  public boolean isCircular() {
    return true;
  }

This task is defined to be circular so when it is returned to the server the next method will be called.
In this example we create a new RandomizerTask with the results from this task as its data.
The generate() method should return a Vector of Tasks. The solver for these tasks will be automatically attached by the server
* We assume that the server "knows" this kind of task.
  public Vector generate() {
    if (!isCircular()) return null;

    RandomizerTask rt = new RandomizerTask();
    rt.setData(getResults());
    Vector gen = new Vector();
    gen.addElement(rt);
    return gen;
  }

Process results is also called by the Distributer as the task is returned. It can be used to save the tasks results, or do any other things needed before the task dies.
  public void processResults() {
  }

This method is used by the Solver to signal the Worker the progress of this task.
  public void setProgress(float i) {
    parent.setProgress(i);
  }

}

Solver
Now we'll write the Solver for the RandomizerTask.

Here are the import declaration.
package dcf.tasks;

import dcf.server.*;
import java.io.*;

The Solver too, should implement the Serializable interface and it should also implement the Solver interface.
public class RandomizerSolver implements Solver, Serializable {

We hold a reference to the Task this solver should solve.
  Task task;

The constructor does nothing special.
  public RandomizerSolver() {
  }

This method allows the Worker to assign this Solver to a Task.
  public void setFor(Task t) {
    task = t;
  }

This method will solve the task.
  public void solve(Task t) {
The next three lines are used by the Solver to indicate its progress. The step is set to 100f/size since we want the progress as a percentage.
    int size = t.getSize();
    float step = 100f/size;
    float progress = 0;

The next lines do the actual "solving".
    java.util.Random rnd = new java.util.Random();
    while (t.hasMore()) {
      Object o = t.next();
      if (o instanceof Integer) {
        int i = ((Integer)o).intValue();
        setResult(new Integer(rnd.nextInt(100)));
The try-catch block is only here so that the progress of solving the task will viewable by the user.
        try {
          Thread.sleep(100); 
        } catch (InterruptedException err) {}
      }

The next two lines update the progress count and notify the Worker through the Task.
      progress += step;
      ((RandomizerTask)task).setProgress(progress);

    }
  }

Returns the Task that the Solver is currently assigned to.
  public Task getTask() {
    return task;
  }

Adds a result to the task.
  public void setResult(Object obj) {
    task.addResult(obj);
  }

}

Conclusion
There are a few intersting things you can do with DCF. For instance, the generate() method can generate a Task of a a different type so you can create a series of Task-Solvers to solve a complicated task. As you can see it is very simple to create Task-Solver pairs for almost every task you need to solve.
For more information please send an e-mail to a DCF team member.


Last updated:

SourceForge.net Logo