/* * org.openmicroscopy.shoola.env.data.views.BatchCallTree * *------------------------------------------------------------------------------ * Copyright (C) 2006 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.env.data.views; //Java imports //Third-party libraries //Application-internal dependencies import org.openmicroscopy.shoola.env.LookupNames; import org.openmicroscopy.shoola.env.config.Registry; import org.openmicroscopy.shoola.env.event.AgentEventListener; import org.openmicroscopy.shoola.util.concur.tasks.CmdProcessor; import org.openmicroscopy.shoola.util.concur.tasks.ExecHandle; import org.openmicroscopy.shoola.util.concur.tasks.ExecMonitor; /** * Maintains and executes a {@link BatchCall} tree. * <p>This class is abstract, it requires subclasses to actually * {@link #buildTree() build} the tree and provide the result of the computation * after the tree has been {@link #exec(AgentEventListener) executed}. A tree * represents a coarse-grained task to carry out within the data services and * maps to a single call in a data services view.</p> * <p>A <code>BatchCallTree</code> object can't be * {@link #exec(AgentEventListener) executed} more than once — any such * attempt would result in an exception being thrown. So you have to discard * the object after execution.</p> * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br>Andrea Falconi      * <a href="mailto:a.falconi@dundee.ac.uk"> * a.falconi@dundee.ac.uk</a> * @version 2.2 * <small> * (<b>Internal version:</b> $Revision$ $Date$) * </small> * @since OME2.2 */ public abstract class BatchCallTree { /** * Tells whether the {@link #exec(AgentEventListener) exec} method has * already been executed. * It latches to <code>true</code> after the first execution. Don't * access this field directly, use {@link #hasExecuted()} instead. */ private boolean executed; /** The root of the call tree. */ private CompositeBatchCall root; /** Subclasses use it to gain access to the container's services. */ protected final Registry context; /** * Creates a new instance. */ protected BatchCallTree() { root = new CompositeBatchCall(); context = DataViewsFactory.getContext(); } /** * Queries the current {@link #executed execution} state and then * marks it as executed. * This method will return <code>false</code> (not executed) only the first * time it is invoked and then <code>true</code> (executed) thereafter. * The purpose of this method is to make sure the tree is executed at most * once by one thread. This is always the case if each view's method * creates a <i>new</i> tree upon each call, but we check anyway to prevent * nasty safety failures if they don't (this would be a <i>bug</i>). * * @return The current {@link #executed execution} state. */ private synchronized boolean hasExecuted() { boolean currentState = executed; executed = true; return currentState; } //NOTE: This method is sync in order to avoid problems in the case both the //following conditions are met: // * A view's method uses a BatchCallTree object which is not method-scoped. // * The UI spawns two threads to call that method. /** * Counts the actual calls. * That is, this method counts all the leaf nodes in the tree. * * @return The count. */ int countCalls() { return root.countCalls(); } /** * Returns the leaf call that is currently processed by the execution * algorithm visiting the call tree. * * @return See above. * @see CompositeBatchCall#getCurCall() */ BatchCall getCurCall() { return root.getCurCall(); } /** * Asynchronously executes this task. * All leaf {@link BatchCall}s within the tree will be executed * <i>sequentially</i>, respecting the order in which nodes were added to * the tree. * The specified observer is notified of the execution progress and of the * eventual outcome of the computation. In particular if the computation * is not cancelled or no exception is raised, then the result of the * whole computation, as returned by the {@link #getResult() getResult} * method, is dispatched to the observer. * * @param observer Monitors progress and gets the outcome of the execution. * Mustn't be <code>null</code>. * @return A handle to the computation. Can be used to cancel execution. * @see BatchCallMonitor */ CallHandle exec(AgentEventListener observer) { if (hasExecuted()) throw new IllegalStateException(); //Only one thread will ever make it here b/c hasExecuted is sync. buildTree(); ExecHandle handle = getProcessor().exec(root, getMonitor(observer)); return new CallHandle(handle); } /** * Returns a concrete {@link CmdProcessor} to * {@link #exec(AgentEventListener) execute} the call tree. * * @return See above. */ protected CmdProcessor getProcessor() { return (CmdProcessor) context.lookup(LookupNames.CMD_PROCESSOR); } /** * Returns an implementation of {@link ExecMonitor} that works as an * adapter to notify the specified <code>observer</code> of execution * events. * Specifically, the returned adapter will notify the <code>observer</code> * of the tree's execution progress and of the eventual outcome of the * computation. * * @param observer The adaptee. * @return The adapter. * @see BatchCallMonitor */ protected ExecMonitor getMonitor(AgentEventListener observer) { MonitorFactory mf = (MonitorFactory) context.lookup(LookupNames.MONITOR_FACTORY); return mf.makeNew(this, observer); } /** * Adds a new child node to the root. * The root node maintained by <code>BatchCallTree</code> is an instance * of {@link CompositeBatchCall}, so refer to that for the semantics of * adding and the resulting execution order. * * @param bc The child node. Mustn't be <code>null</code>. * @see CompositeBatchCall#add(BatchCall) */ protected void add(BatchCall bc) { root.add(bc); } /** * Builds the call tree. * The root of the tree is owned by <code>BatchCallTree</code>, you use * the {@link #add(BatchCall) add} method to add nodes to the root. * The call tree could be as easy as one call; for example you could set * it to be: * <pre><code> * add(new BatchCall("Loading some data.") { * void doCall() { loadSomeData(); } * }); * </code></pre> * <p>Where <code>loadSomeData</code> is a call that retrieves some data * from the data services and stores the result somewhere within this object * — the call description (<code>"Loading some data"</code>) will be * used to provide feedback to the execution observer.</p> * <p>However, you would typically set up an execution sequence, by adding * several leaf {@link BatchCall}s to the root node. Further composition is * also possible because {@link BatchCall}s can be aggregated into sub-trees * and then these trees can be {@link #add(BatchCall) added} to the root. * Even dynamic composition (as the tree is executed) is available, should * you ever need it.</p> * <p>As hinted above, call results have to be stored in a place accessible * to this object. This is because a concrete <code>BatchCallTree</code> is * responsible for providing the final result of the computation.</p> * <p>It is imperative that a subclass never let escape references to the * nodes that were added to the tree during the invocation of this method. * Ideally, nodes would be created, added, and de-referenced within the * scope of this method. Failure to comply would result in safety issues. * </p> * * @see BatchCall * @see CompositeBatchCall * @see #getResult() * @see #exec(AgentEventListener) */ protected abstract void buildTree(); /** * Provides the final result of the computation. * This method is called after the call tree built by {@link #buildTree()} * has been {@link #exec(AgentEventListener) executed} to provide the * result of the whole execution. * * @return The final result of the computation. * @see #getPartialResult() */ protected abstract Object getResult(); /** * Useful for subclasses that want to make partial results available to * the invoker. * As the tree computation proceeds, this method will be called after each * leaf node is executed and whatever is returned will be packed into a * feedback event. * As a example, a tree that retrieves all thumbnails in a dataset could * return the lastly retrieved thumbnail. This would give the invoker the * possibility to display the thumbnails progressively as feedback events * are received. * Most subclasses don't need this extra feature, so we provide a default * no-op implementation. * * @return <code>null</code>. */ protected Object getPartialResult() { return null; } }