package org.dancres.blitz.remote.test;

import java.io.Serializable;

import java.rmi.RMISecurityManager;

import net.jini.core.entry.Entry;

import net.jini.core.lease.Lease;

import net.jini.space.JavaSpace;

import net.jini.core.transaction.server.*;
import net.jini.core.transaction.*;

/**
   <p>A queue example derived from some work I've been doing on Executor a
   means to accelerate performance and facilitate creation of a library of
   useful patterns/constructs for various common problems.</p>
 */
public class Queue {
    public static void main(String args[]) {
        try {
            new Queue().test();
        } catch (Exception anE) {
            System.err.println("Whoops");
            anE.printStackTrace(System.err);
        }
    }

    private void test(boolean useLocal) throws Exception {
        System.setSecurityManager(new RMISecurityManager());
        space();
    }

    /**
       Creates a classic queue construct and then uses the standard
       JavaSpaces API to both push and pop a set of Entry's.
     */
    private void space() throws Exception {
        Lookup myLookup = new Lookup(JavaSpace.class);

        System.out.println("Find space");

        JavaSpace mySpace = (JavaSpace) myLookup.getService();

        System.out.println("Got me a space");

        System.out.println("Find txnmgr");

        myLookup = new Lookup(TransactionManager.class);

        TransactionManager myManager =
            (TransactionManager) myLookup.getService();

        // Initialize queue
        mySpace.write(new Meta(true), null, Lease.FOREVER);

        long myStart = System.currentTimeMillis();

        // Fill queue
        for (int i = 0; i < 10; i++) {
            // Shouldn't really lease forever and ought to catch and process
            // exceptions accordingly.
            //
            Transaction.Created myTxnC =
                TransactionFactory.create(myManager, Lease.FOREVER);

            Transaction myTxn = myTxnC.transaction;

            Meta myQueue = (Meta) 
                mySpace.take(new Meta(), myTxn, Lease.FOREVER);

            Element myNext = myQueue.newElement();

            myNext.thePayload = new Integer(i);

            mySpace.write(myQueue, myTxn, Lease.FOREVER);
            mySpace.write(myNext, myTxn, Lease.FOREVER);

            myTxn.commit();
        }

        // Empty queue
        for (int i = 0; i < 10; i++) {
            Transaction.Created myTxnC =
                TransactionFactory.create(myManager, Lease.FOREVER);

            Transaction myTxn = myTxnC.transaction;

            Meta myQueue = (Meta) 
                mySpace.take(new Meta(), myTxn, Lease.FOREVER);

            Element myNext = myQueue.nextIndex();

            Entry myResult = mySpace.take(myNext, myTxn, Lease.FOREVER);

            System.out.println(myResult);

            mySpace.write(myQueue, myTxn, Lease.FOREVER);

            myTxn.commit();
        }

        long myEnd = System.currentTimeMillis();

        System.out.println("Total time: " + (myEnd - myStart));
    }

    /**
       <p>Tracks start and end of queue.  Modifications would include giving
       this (and Element instances) an identifying name which allows more than
       one queue to co-exist within the same JavaSpace instance.</p>

       <p>There are ways to accelerate this like "code shipping" or
       "Executor" (a concept I'm prototyping with Blitz) without having to
       radically change or add to the JavaSpaces API.</p>
     */
    public static class Meta implements Entry {
        public Integer theStart = new Integer(0);
        public Integer theEnd = new Integer(0);

        public Meta() {
            theStart = null;
            theEnd = null;
        }

        public Meta(boolean isStart) {
            theStart = new Integer(0);
            theEnd = new Integer(0);
        }

        public Element newElement() {
            int myCurrent = theEnd.intValue();

            theEnd = new Integer(myCurrent + 1);

            return new Element(myCurrent);
        }

        public Element nextIndex() {
            int myCurrent = theStart.intValue();

            theStart = new Integer(myCurrent + 1);

            return new Element(myCurrent);
        }
    }

    /**
       An individual Entry in the queue.
     */
    public static class Element implements Entry {
        public Integer theIndex;
        public Serializable thePayload;

        public Element() {
        }

        public Element(int anIndex) {
            theIndex = new Integer(anIndex);
        }

        public String toString() {
            return thePayload.toString();
        }
    }
}
