package com.arjuna.services.framework.startup; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A manager which allows initializaton callabcks for a suite of related web apps to be registered and subsequently * run in a fixed order, irrespective of the order in which the application server initializes the web apps. * * Sequencer maintains two lists of independently managed callbacks, one for the WSCOOR 1.0 web app suite and one * for the WSCOOR 1.1 web app suite. When registering a callback listeners or servlets for a given web app supply a * sequence id and a web app id to identify i) the required sequence and ii) the entry in the sequence at which the * callback should be stored. If a web app registers multiple callbacks they are stored in the sequence entry as a * list in registration order. Once a web app has registered all its callbacks it closes the relevant entry. When * all entries in a sequence have been closed the entries are traversed in order and the callbacks in each entry are * triggered, thus ensuring that callbacks for web apps with lower sequence indices complete before callbacks with * higher indices are called. * * This version also includes a global latch which can be used to ensure that no callbacks are not processed until a * client lifts the latch. If the latch has already been lifted when the final element of a sequencer is closed the * callbacks are run straight away. If the latch is closed then the sequencer is installed in a deferred list. When * the latch is lifted the deferred list is scanned and callbacks are run for each sequencer found in the list. * * User: adinn * Date: Nov 30, 2007 * Time: 4:05:13 PM */ public class Sequencer { // public API /* * indices for the independent startup sequences which we manage */ /** * index of startup sequence used for serialization of WSCOOR 1.0 initialization callbacks */ public static final int SEQUENCE_WSCOOR10 = 0; /** * index of startup sequence used for serialization of WSCOOR 1.1 initialization callbacks */ public static final int SEQUENCE_WSCOOR11 = SEQUENCE_WSCOOR10 + 1; /** * count of independent startup sequences. */ public static final int SEQUENCE_MAX = SEQUENCE_WSCOOR11 + 1; /* * indices for sequence of web apps we need to ensure start in the correct order */ /** * index of WS-C web app which initialises first in 1.0 */ public static final int WEBAPP_WSC10 = 0; /** * index of WS-T web app which initialises second in 1.0 */ public static final int WEBAPP_WST10 = WEBAPP_WSC10 + 1; /** * index of WSCF web app which intiialises third in 1.0 */ public static final int WEBAPP_WSCF10 = WEBAPP_WST10 + 1; /** * index of WSTX web app which initialises last in 1.0 */ public static final int WEBAPP_WSTX10 = WEBAPP_WSCF10 + 1; /** * there are four startup apps in the 1.0 sequence */ public static final int WEBAPP_MAX10 = WEBAPP_WSTX10 + 1; /** * index of WS-C web app which initialises first in 1.1 */ public static final int WEBAPP_WSC11 = 0; /** * index of WS-T web app which initialises second in 1.1 */ public static final int WEBAPP_WST11 = WEBAPP_WSC11 + 1; /** * index of WSCF web app which intiialises third in 1.1 */ public static final int WEBAPP_WSCF11 = WEBAPP_WST11 + 1; /** * index of WSTX web app which intiialises fourth in 1.1 */ public static final int WEBAPP_WSTX11 = WEBAPP_WSCF11 + 1; /** * there are three startup apps in the 1.1 sequence */ public static final int WEBAPP_MAX11 = WEBAPP_WSTX11 + 1; /** * notify end of callback registration for a web app associated with a given sequence * @param webapp */ public static final void close(int sequence, int webapp) { SEQUENCERS[sequence].close(webapp); } /** * a callback class specialised by client web apps to execute startup code */ public static abstract class Callback { /** * construct a callback associated with a web app in a given startup sequence and automatically register it * for subsequent execution * @param sequence the sequence in which the web app is contained either WSCOOR_1_0 or WSCOOR_1_1 * @param webapp the web app with which this callback is associated */ public Callback(int sequence, int webapp) { register(this, sequence, webapp); } /** * a callback method which is invoked invoked per callback in registration order per web app in web app * order per sequence as soon as all callback lists in the sequence have been closed */ public abstract void run(); } /** * undo the latch which initially delays running of any callback sequences. this is provided * to enable the Service start routine to configure any necessary parameters before the * listener callbacks are run. */ public static void unlatch() { synchronized (Sequencer.class) { latched = false; } Iterator<Sequencer> iterator = deferred.iterator(); while (iterator.hasNext()) { Sequencer sequencer = iterator.next(); sequencer.runCallbacks(); } } // private implementation /** * a global latch used to defer running of callbacks until the XTS service is ready to * let them run */ private static boolean latched = true; /** * a global list of all startup sequences */ private static final Sequencer[] SEQUENCERS = { new Sequencer(WEBAPP_MAX10), new Sequencer(WEBAPP_MAX11) }; /** * a list of sequencers for which invocation of callbacks has been deferred pending lifting of the sequencer latch */ private static List<Sequencer> deferred = new ArrayList<Sequencer>(); /** * method called by the Callback constructor to append a startup callback to the list for a web * app in the appropriate startup sequence * @param callback a callback to add to the list for the web app * @param sequence the sequence in which the web app is contained either WSCOOR_1_0 or WSCOOR_1_1 * @param webapp the web app with which this callback is associated */ private static final void register(Callback callback, int sequence, int webapp) { SEQUENCERS[sequence].register(callback, webapp); } /** * each sequence contians an array of lists to hold callbacks registered per web app */ private List<Callback>[] callbacks; /** * each sequence contains an array of flags per web app initially false and set to true when a web app has * registered all its callbacks and called close */ private boolean[] closed; /** * counter incremented each time a callback list is closed. callback processing is triggered when this reaches the * sequence size count */ private int closedCount; /** * count which sizes the callbacks list and closed array. */ private int sequenceSize; /** * insert a callback into the list for a given web app associated with this sequence * @param callback the callback to be registerd * @param webapp the web app with which the callback is associated */ private final void register(Callback callback, int webapp) { callbacks[webapp].add(callback); } /** * close the callback list associated with a given web app in this sequence and, if this is the last unclosed * list, trigger execution of all callbacks in sequence order * @param webapp the web app whose callback list is to be closed */ private final void close(int webapp) { closed[webapp] = true; closedCount++; if (closedCount == sequenceSize) { runCallbacks(); } } private void runCallbacks() { // if the latch is lifted then run the callbacks otherwise defer them for the // thread which raises the latch to run synchronized(Sequencer.class) { if (latched) { deferred.add(this); return; } } for (int i = 0; i < sequenceSize; i++) { Iterator<Callback> iter = callbacks[i].iterator(); while (iter.hasNext()) { Callback cb = iter.next(); cb.run(); } } } /** * construct a Sequencer to manage registration and execution of callbacks associated with an ordered sequence * of web apps * @param sequenceSize count of the number of web apps for which callbacks are to be registered */ private Sequencer(int sequenceSize) { this.sequenceSize = sequenceSize; callbacks = new ArrayList[sequenceSize]; for (int i = 0; i < sequenceSize; i++) { callbacks[i] = new ArrayList<Callback>(); } closed = new boolean[sequenceSize]; closedCount = 0; } }