A Generic Compute Engine



This page is based on the Java rmi tutorial on java.sun.com.

The compute engine server:

It is comprised of an interface and a class.  The interface defines the client's view of the remote object, the compute server; the class provides the server's implementation.

The Interface

At the heart of the compute engine is a protocol that: This protocol is expressed in 2 interfaces, one supported by the compute engine; the other supported by the objects that are submitted to the compute engine.

     The Compute interface defines the remotely accessible part---the compute engine itself:


package compute;
import java.rmi.*;

public interface Compute extends Remote
{
    Object executeTask(Task t) throws RemoteException;
}


The Task interface defines the interface between the compute engine and the work that it needs to do, providing the way to start the work.



package compute;
import java.io.Serializable;

public interface Task extends Serializable
{
    Object execute();
}


Implementing the Compute Server



package engine;
import java.rmi.*;
import java.rmi.server.*;
import compute.*;

public class ComputeEngine extends UnicastRemoteObject implements Compute
{
    public ComputeEngine() throws RemoteException { }

    public Object executeTask(Task t)
    {
        return t.execute();
    }

    public static void main(String[] args)
    {
        if (System.getSecurityManager() == null)
        {
            System.setSecurityManager(new RMISecurityManager()); //server loads Task objects
        }
        try
        {
            Compute engine = new ComputeEngine(); // engine is a Compute not a ComputeEngine
            Naming.rebind("Compute", engine); // register compute server on this machine
            System.out.println("ComputeEngine bound");
        }
        catch (Exception e)
        {
            System.err.println("ComputeEngine exception: " + e.getMessage());
            e.printStackTrace();
        }
    }
}


Creating a Compute Client



package client;
import java.rmi.*;
import java.math.*;
import compute.*;

public class ComputePi
{
    public static void main(String args[])
    {
        if (System.getSecurityManager() == null)
        {
            System.setSecurityManager(new RMISecurityManager()); //client loads executeTask stub
        }
        try
        {
            String name = "//" + args[0] + "/Compute";
            Compute comp = (Compute) Naming.lookup(name);
            Pi task = new Pi(Integer.parseInt(args[1])); // Pi implements Task
           BigDecimal pi = (BigDecimal) (comp.executeTask(task));
            System.out.println(pi);
        }
        catch (Exception e)
        {
            System.err.println("ComputePi exception: " +  e.getMessage());
            e.printStackTrace();
        }
    }
}


     Create & Install a Security Manager



package client;
import compute.*;
import java.math.*;

public class Pi implements Task
{
    /** constants */
    private static final BigDecimal ZERO = BigDecimal.valueOf(0);
    private static final BigDecimal  ONE = BigDecimal.valueOf(1);
    private static final BigDecimal FOUR = BigDecimal.valueOf(4);
    private static final int roundingMode = BigDecimal.ROUND_HALF_EVEN;

    /** digits of precision after the decimal point */
    private int digits;

    /**
     * Construct a task to calculate pi to the specified precision.
     */
    public Pi(int digits) { this.digits = digits; }

    /**
     * Calculate pi.
     */
    public Object execute() { return computePi(digits); }

    /**
     * Compute the value of pi to the specified number of
     * digits after the decimal point.  The value is
     * computed using Machin's formula:
     *
     *          pi/4 = 4*arctan(1/5) - arctan(1/239)
     *
     * & a power series expansion of arctan(x) to sufficient precision.
     */
    public static BigDecimal computePi(int digits)
    {
        int scale = digits + 5;
        BigDecimal arctan1_5 = arctan(5, scale);
        BigDecimal arctan1_239 = arctan(239, scale);
        BigDecimal pi = arctan1_5.multiply(FOUR).subtract(arctan1_239).multiply(FOUR);
        return pi.setScale(digits, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * Compute the value, in radians, of the arctangent of
     * the inverse of the supplied integer to the speficied
     * number of digits after the decimal point.  The value
     * is computed using the power series expansion for the arc tangent:
     *
     * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + *     (x^9)/9 ...
     */
    public static BigDecimal arctan(int inverseX, int scale)
    {
        BigDecimal result, numer, term;
        BigDecimal invX = BigDecimal.valueOf(inverseX);
        BigDecimal invX2 =
        BigDecimal.valueOf(inverseX * inverseX);
        numer = ONE.divide(invX, scale, roundingMode);

        result = numer;
        int i = 1;

        do
        {
            numer = numer.divide(invX2, scale, roundingMode);
            int denom = 2 * i + 1;
            term = numer.divide(BigDecimal.valueOf(denom), scale, roundingMode);
            if ((i % 2) != 0)
                result = result.subtract(term);
            else
                result = result.add(term);
            i++;
        }
        while (term.compareTo(ZERO) != 0);
        return result;
    }
}