Understanding the server side architecture.

The serverside architecture can be taught of as two use cases. The first is to find the factor of a mersenne number to check if it is a mersenne prime or not. The second it to take a mersenne prime number and calculated the corresponding perfect number.

Use Case 1: Factorising a Mersenne Number.

The first of these two use cases, find the factors of a mersenne number is by far the more complicated of the two and can be taught of in terms of a number on sub use cases. It involves taking in a mersenne number and finding it factors, since this is computationally a very intensive process, the client is informed of the result asynchronously by email when the calculation is finished.

Furthermore this now involves the notion of state. Since with multiple client invocations, each mersenne number, and it corresponding email address and factors (once it has finished factorising) , must be remembered. Since Web Service are largely stateless services (a consequence of using http as the stateless protocol) then this stateless service must facade some type of stateful service. The client must also be informed of the results, so there must be the ability to email the results to the calling client. Finally some sort of state lifecycle management is also desirable. On a server restart the mersenne number not finished factorising should at a minimum be restarted. The sub use cases can therefore me details at follows:

  1. The Basic Service
  2. Making a stateless service stateful
  3. Informing the Client
  4. Making the calculation
  5. Adding a persistence mechanism

These uses case are discussed in more detail below. However before discussion them individually it is worth looking at an over view of the sequence diagrams and class diagrams for this use case.

The Basic Service

The basic service is the PerfectCalc interface (here is the WSDL). It defines two method:

  1. public boolean checkMersenneNumber(MersenneNumber k, String email) takes a mersenne number (a number of the form (2^n -1) not necessarily a prime number) and an email address or the invoker, and factors the mersenne number and emails the result to the invokers. If the mersenne number has no other factor other than itself and one, then it is a mersenne prime
  2. public PerfectNumber calcPerfectNumber(MersennePrime mersennePrime) takes a mersenne prime and calculates the corrosponding perfect number

However, the second method belongs to the second use case and will be discussed below.

Making a stateless service stateful

The FactorFactory object is factory pattern responsible for making the stateless PerfectCalc service stateful. The FactorFactory object is also a singleton with a protected constructor and a public static initialiser. Here is the code listing for the FactorFactory object.

/**
 * A Factory class that is also a singleton object for creating multiple instances of the stateful Factor class
 * @testcase test.TestFactorFactory
 * @version 1.0
 * @author Eoin Lane
 */
public class FactorFactory implements IFactorFactory, Observer {
    /** This contructor is only called once and when it is it the Caretaker is created and initalised */
    protected FactorFactory() {
        // Initalise the Map
        this.map = new HashMap();
        //factors = new Vector();
        caretaker = new SimpleCaretaker();
        System.out.println("Caretaker created and initialised");
    }

    /**
     * Factory method to create instances of IFactor object
     * @param mersenneNumber number of the form (2^k-1)
     * @param email of the invoking client
     */
    public IFactor getFactor(MersenneNumber mersenneNumber, String email) {
        // Store the Mersenne number k value into the Map
        Double d = new Double(mersenneNumber.getK());
        this.map.put(d, email);
        Factor factor = new Factor(mersenneNumber);
        factor.attach(this);
        factor.factorize();
        // Create a Memento of the newly created factor object
        IMemento memento = new SimpleMemento(mersenneNumber.getK(), email);
        //Tell the caretaker about it
        this.caretaker.addElement(memento);
        return (IFactor)factor;
    }

    /** Returns a one and only one instance of this class */
    public static FactorFactory getInstance() {
        if (instance == null) {
            synchronized(FactorFactory.class) {
                if (instance == null) {
                    instance = new FactorFactory();
                }
            }
        }
        return instance;
    }

    /**
     * The callback method that the subject will call on this listener
     * @param factor the finished Factor class
     */
    public void update(Factor factor) {
        // Get the k value from the mersenne number in the factor class
        double k_value = factor.getMersenneNumber().getK();
        System.out.println("Observer informed of finished calculation of mersenne number: " + k_value);
        // Get the email corrosponding th the Factor's Mersenne k value
        String email = (String)(this.map.get(new Double(k_value)));
        Collection bag = factor.getFactors();
        mail(bag, email, k_value);
        // since this Factor class has now finished process remove it it's corrosponding memento object from the caretaker
        // Create a Memento of the newly created factor object
        IMemento memento = new SimpleMemento(k_value, email);
        this.caretaker.removeElement(factor);
    }

  
    private static FactorFactory instance = null;
    // Looks after and tracks all the created Factor objects
    private ICaretaker caretaker;
    // A map containing the Mersenne number, email value pair
    Map map;
}

The important things to note is the protected constructor where a Vector of Factor class is created and initialised. This Vector keeps a record of all the created Factor classes. The Factor classes are then created with the getFactor(MersenneNumber mersenneNumber) method. This pattern of using a singleton factory object to create and keep track of the generated Factor object, allows a stateless facade to be exposed to the outside while internally stateful classes can be created and their life cycle managed.

Informing the Client

The observer pattern is used here as a callback to the the FactorFactory object to tell it that the Factor obejct created to factorise a particular mersenne number is now finished. To achieve this FactorFactory implements the observer interface while tthe Factor object implements the

/**
 * This object is used to factorise a mersenne number. There are potentially many of these created in memory at run time.
 * @testcase test.TestFactor
 * @author Eoin Lane
 * @version 1.0
 */
public class Factor implements IFactor, Subject {
    /**
     * Constructor
     * @param mersenneNumber a number of the form (2^k-1)
     */
    public Factor(MersenneNumber mersenneNumber) {
        strategy = new LLFactorer();
        this.mersenneNumber = mersenneNumber;
    }

    /** Factorise this object */
    public void factorize() {
        factors = this.strategy.factorize(this.mersenneNumber);
        this.inform();
    }

    public MersenneNumber getMersenneNumber() {
        return this.mersenneNumber;
    }

    /**
     * Register an observer with this object.
     * @param observer an observer
     */
    public void attach(Observer observer) {
        observersVector.addElement(observer);
    }

    /**
     * detach an observer from this object.
     * @param observer an observer
     */
    public void detach(Observer observer) {
        observersVector.removeElement(observer);
    }

    /** Inform all the observers of an event */
    public void inform() {
        java.util.Enumeration enumeration = observers();
        Collection bag = this.getFactors();
        while (enumeration.hasMoreElements()) {
            ((Observer)enumeration.nextElement()).update(this);
        }
    }

    public Enumeration observers() {
        return ((java.util.Vector) observersVector.clone()).elements();
    }

    public Collection getFactors() {
        return factors;
    }

    /*# private FactorFactory _factorFactory; */

    // The (2^k-1) object
    private MersenneNumber mersenneNumber;
    // The factors returned by the factorisation method
    private Collection factors;
    // The strategy to be used to factorise this object
    private IFactorer strategy = null;
   
    /** @associates <{Observer}> */
    private Vector observersVector = new java.util.Vector();
}

Making the calculation

A strategy pattern is used to implement the factorisation. The strategy pattern allows an algorithmic change of the implemention at run time, depending on the best choice of strageties (i.e.a different algorithm maybe used depending on the size of the number to be factored). Here is the code for LLFactoer a simple algormetic implementation of a the IFactor inferface.

import java.util.Collection;
import primes.PrimeNumber;
import java.lang.Math;
import perfectNumbers.MersenneNumber;

/**
 * A simple concrete implementation of the strategy IFactorer interface
 * @testcase test.TestLLFactorer
 * @author Eoin Lane
 * @version 1.0
 */
public class LLFactorer implements IFactorer {
    public Collection factorize(Number number) {
        return new java.util.ArrayList(null);
    }

    // We can check a potential factor n of Mp by seeing if 2^p (mod n) =1
    public Collection factorize(MersenneNumber mersenneNumber) {
        System.out.println("Got Mersenne number for processsing");
        // A Collection bag to collect the factors
        Collection bag = new java.util.ArrayList();
        double maxNumberOfIteration = Math.pow((double)2, (double)(mersenneNumber.getK()));
        double siveNumberOfIteration = sive(maxNumberOfIteration);
        for (long i = 1; i < siveNumberOfIteration; i++) {
            if ((factorize(mersenneNumber.getK(), (long)i) == 0)) {
                bag.add(new Long(i));
                System.out.println(i + "\t" + factorize(mersenneNumber.getK(), i));
            }
        }
        return bag;
    }

    //a simple algorithm for checking for find a^b (mod c)
    public static double factorize(double b, double c) {
        double a = 2;
        double res = 1;
        double d;
        while (b > 0) {
            d = b % 2;
            if (d == 1) {
                res = a * res;
                if (res > c) res = res % c;
            }
            a = a * a;
            if (a > c)
                a = a % c;
            b = (b - d) / 2;
        }
        return res;
    }

    // A simple sive to cut down the number of potential factors
    public static double sive(double a) {
        return (a + 1) / 2;
    }
}

The Factor object calls the public Collection factorize(MersenneNumber mersenneNumber) method defined in the Factorer interface to do the factorisation. The LLFactorer object (shown above) implements this method by first calling the public static long sive(long a) method to cut down on the potential factor. This is a very simple method that just cuts the number of factors in half (i.e. if (2^4-1)=15 is to be checked, then only factors up to half that number need to be checked). These potential factors are then iterated through and individually checked using the factorized algorithm found in the public static double factorize(double b, double c) method. Most of the material for the above strategy code can be found the following Mersenne FAQ

Adding a persistence mechanism

In case of a server restart the current mersenne numbers being factored need to be persisted. To achieve this a memento pattern is used. This is conjenction with a Caretaker object which manages and persists the Memeto objects. The Memento objects represent the state of the Factor objects currently being computed and the Caretaker object manages and persists these object. On startup the Caretaker checks if all the previous Factor objects had finished. Unfinished Factor object are reinitialised and their calculations restated. In a later iteration, the stage of factorization will also be persisted.

/**
 * A simple interface of the Caretaker interface. This Caretaker implementation is used to persist Factor object by keeping a
 * list of Memento representing the state of the current Factor instance. It writes that list to disk using a a JDO DAO object
 * @author Eoin Lane
 * @version 1.0
 */
public class SimpleCaretaker implements ICaretaker {
    /** The current list of Memento objects */
    private Vector currentList;

    /** The initalisign list of factor objects */
    private Vector factors;

    /**
     * @link dependency
     * @label mementos
     */

    /*# IMemento lnkIMemento; */

    /**
     * Initialise the caretaker load Memento object using JDO from disk if necessary
     * @param a vector of factory objects
     */
    public SimpleCaretaker(Vector factors) {
        this.currentList = new Vector();
        this.factors = factors;
        System.out.println("Loading Memento objects using JDO from if necessary disk");
    }

    /** Clears the current Vector list */
    public void clear(Vector factor) {
        currentList = new Vector();
        this.factors = factors;
    }

    /** Adds an element */
    public void addElement(Object obj) {
        currentList.addElement(obj);
    }

    /** Removes an element */
    public void removeElement(Object obj) {
        currentList.removeElement(obj);
    }

    //------------------------------------
    private void remove(IMemento obj) {
        IMemento m = (IMemento)obj;
        m.restore(); //and restore the old position
    }
}

The persistence mechanism used here is the new java data object mechanism and since this is a relatively new topic, here is a simple resource to get stated http://www.jdocentral.com/index.html. A JDO data access object (DAO) is used to encapsulate the persistence mechanism and the Caretaker obejct uses the JDODOA object to persist the object graph of Menento objects (these Memento objects contain the state of the Factor objects currently in memory)

Use Case 2: Calculate a Prefect Number from a Mersenne Prime.

This use is also defined in the IPerfectCalc interface and implemented in PerfectCalc. As a reminder, here are the method delcared in the IPerfectCalc interface again: It defines two method:

  1. public boolean checkMersenneNumber(MersenneNumber k, String email) takes a mersenne number (a number of the form (2^n -1) not necessarily a prime number) and an email address or the invoker, and factors the mersenne number and emails the result to the invokers. If the mersenee number has no other factor other than itself and one, then it is a mersenne prime
  2. public PerfectNumber calcPerfectNumber(MersennePrime mersennePrime) takes a mersenne prime and calculates the corresponding perfect number

As is evident from the service description this, service takes a mersenne prime and returns the corresponding perfect number. Here is the sequence diagrams and class diagram for this use case.

The Patterns

This server side architecture used a number of GOF patterns and is detailed below.

A stateless facde service class expose to the client via a Web Service. This facde calls the FactorFactory (which is a factory class, a mediator and a singleton) to create a Factor stateful objects. There will be potentially many of these objects, depending on the number of client requests from factorization. The Factor stateful object uses a strategy pattern to find the factors of the mersenne number and informs the FactorFactory of the state change using an observer pattern. The FactorFactory will then email the result. Finally the FactorFactoryalso uses a memento pattern to persist the sate of the mersenne number factorisation calculation, so the it may be reinstated in the case of a server restart.

Here is a list of and references to the GOF patterns used:

  1. The facde pattern (the stateless service exposes a simple interface of checkMersenne number to the client)
  2. The Factory method (an FactorFactory object for creating Factor (or any other type of object implementing the IFactor interface) at runtime.
  3. The Singleton pattern is used for the FactorFactory object to ensure there is only one object creating and managing Factor objects
  4. The Strategy pattern (for factorising the MersenneNumber, decoupling the Factor object from how it factors)
  5. The Mediator pattern is used in the FactorFactory again to control and manage the life cycle of the Factor objects that it is responsible for creating.
  6. The Observer pattern (a call back to the FactorFactoy object that the statefull Factor object has finished factoring and the results should be emailed to the calling client)
  7. The Memento pattern to persist the state of the Factor object in case of a server restart