001    // Copyright 2007 Waterken Inc. under the terms of the MIT X license
002    // found at http://www.opensource.org/licenses/mit-license.html
003    package org.waterken.bang;
004    
005    import static org.ref_send.test.Logic.join;
006    import static org.ref_send.test.Logic.was;
007    
008    import org.ref_send.list.List;
009    import org.ref_send.promise.Eventual;
010    import org.ref_send.promise.Promise;
011    
012    /**
013     * An introduction to eventual operations in Java.
014     * <p>
015     * This class provides an introduction to eventual operations by using them to
016     * update and query a counter held in an object of type {@link Drum}.
017     * </p>
018     */
019    public final class
020    Beat {
021        private Beat() { /* no instance interface */ }
022        
023        /**
024         * Runs a unit test.
025         * <p>
026         * This method is called by the infrastructure code that manages the
027         * lifecycle of vats. The return from this method is an object that will be
028         * returned to the creator of the new vat. In this case, the vat creator
029         * will get a promise for a boolean.
030         * </p>
031         * <p>
032         * By convention, an instance of {@link Eventual} is held in a variable
033         * named "_" and referred to as the "eventual operator". The eventual
034         * operator provides all eventual control flow operations, as well as the
035         * ability to produce "eventual references", which are references that
036         * schedule future invocation of a method, instead of invoking a method
037         * immediately. All references known to be eventual are also stored in
038         * variables whose name is suffixed with the '_' character. Consequently,
039         * you can scan down a page of code, looking for the character sequence "_."
040         * to find all the operations that are expected to be eventual.
041         * </p>
042         * @param _     eventual operator
043         * @param drum  test subject
044         */
045        static public Promise<?>
046        make(final Eventual _, final Drum drum) {
047            /*
048             * First, ensure that we have an eventual reference to the drum, since
049             * the caller may have provided an immediate reference. The _()
050             * operation takes a reference that may be either immediate or eventual
051             * and returns a reference that is guaranteed to be eventual.
052             */ 
053            final Drum drum_ = _._(drum);
054            
055            /*
056             * Start the test sequence by checking that the caller provided a new
057             * drum. We get the initial hit count by doing an eventual invocation of
058             * the getHits() method. If the provided drum is in another vat, this
059             * invocation will result in an HTTP GET request being sent to the
060             * hosting server. The return from the getHits() invocation is a promise
061             * for the number of hits. Using the when() operation, we register an
062             * observer on this promise, to receive a notification after the HTTP
063             * GET response has come back. The observer, constructed by the was()
064             * method, produces a promise for a boolean, indicating whether or not
065             * the number of hits specified in the HTTP GET response was the number
066             * expected. We'll hold onto this promise and use it to produce the
067             * promise returned to our caller.
068             */
069            final Promise<?> zero = _.when(drum_.getHits(), was(0));
070            
071            /*
072             * Increment the hit counter by doing an eventual invocation of
073             * the bang() method. If the provided drum is in another vat,
074             * this invocation will result in an HTTP POST request being
075             * sent to the hosting server.
076             */
077            drum_.bang(1);
078            
079            /*
080             * Requests sent on an eventual reference are sent in order, and
081             * so another check of the drum's hit count will see a value 1
082             * more than the previous check.
083             */
084            final Promise<?> one = _.when(drum_.getHits(), was(1));
085            
086            // We can queue up as many requests as we like...
087            drum_.bang(2);
088            
089            // ...and they will all be sent in order.
090            final Promise<?> three = _.when(drum_.getHits(), was(3));
091            
092            /*
093             * The Waterken server can log the causal chaining of all these
094             * events. Comments can be inserted into this log via the
095             * eventual operator.
096             */
097            _.log.comment("all bang requests queued");
098            
099            /*
100             * We now have 3 promises for checks on the expected value of the drum's
101             * hit count. We'll combine these 3 promises into 1 by doing an eventual
102             * join operation on them. The promise returned to our caller will
103             * resolve as soon as any one of the promises is rejected, or after all
104             * of them are fulfilled. Note that none of the HTTP requests have
105             * actually been sent yet; we've just scheduled them to be sent and
106             * setup code to be run when the responses eventually come back.
107             */
108            return join(_, zero, one, three);
109            
110            /*
111             * In total, we've scheduled 3 GET requests and 2 POST requests. If any
112             * of these communications are interrupted, due to a server crash or
113             * lost connection, the software will remember where it left off and
114             * resume as soon as network connections can be re-established, which
115             * the software will also periodically retry. Regardless of how often
116             * this process is interrupted, or for how long, the hit count on the
117             * drum will only be incremented by 3, the number specified by the
118             * algorithm above.
119             */
120        }
121        
122        // Command line interface
123    
124        /**
125         * Executes the test.
126         * <p>
127         * This class can also be run from the command line, to run tests against a
128         * local, transient {@link Drum}. Most factory classes won't provide a
129         * command line test suite and so won't have a {@link #main} method.
130         * </p>
131         * @param args  ignored
132         * @throws Exception    test failed
133         */
134        static public void
135        main(final String[] args) throws Exception {
136            /*
137             * All the eventual control flow operations bottom out in runnable tasks
138             * on an event loop. This method provides a simple implementation for
139             * the event loop. The Waterken Server provides a more complete
140             * implementation that supports multiple concurrent event loops with
141             * transparent persistence and across the network messaging.
142             */
143            final List<Promise<?>> work = List.list();
144            final Eventual _ = new Eventual(work.appender());
145            final Promise<?> result = make(_, Bang.make());
146            while (!work.isEmpty()) { work.pop().call(); }
147            result.call();
148        }
149    }