/* * org.openmicroscopy.shoola.env.ui.SplashScreenProxy * *------------------------------------------------------------------------------ * 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.ui; //Java imports import javax.swing.SwingUtilities; //Third-party libraries //Application-internal dependencies import org.openmicroscopy.shoola.env.Container; import org.openmicroscopy.shoola.env.data.login.UserCredentials; /** * Proxy for the splash screen component. * <p>Takes care, with the help of the {@link UserCredentials} class, of * threading issues that rise from having the initialization thread access the * splash screen and, at the same time, the <i>Swing</i> dispatching thread * manage that component's event handling.</p> * <p>In order to separate threading issues from the actual component's * functionality, we use Active Object. This class plays the role of the Proxy, * implementing the Active Object interface ({@link SplashScreen}, which * declares what is the functionality provided to clients) and forwarding * requests for executions of methods on the Servant — * {@link SplashScreenManager}, which provides the actual splash screen's * functionality and runs within the <i>Swing</i> dispatching thread as * opposite to the proxy, which runs within the initialization thread.</p> * <p>Requests for method execution are constructed by extending the * {@link Runnable} interface with anonymous inner classes and are scheduled * for execution by using the <code>invokeLater</code> method of * {@link SwingUtilities} — this represents our interface to the Scheduler * (which is part of the <i>Swing</i> innards). Notice that even though * method requests are constructed and forwarded within the initialization * thread, the Servant's methods are always executed within the <i>Swing</i> * dispatching thread — this frees the splash screen component from dealing * with threads.</p> * <p>The last thing that we have to deal with is how to collect the result * of a method call on the Servant. We only have one method that returns * a value to the client: {@link #getUserCredentials(boolean)}. * However, this one has no corresponding method on the Servant — * obviously enough because the user's credentials are entered by the user. * So we would need a sort of Publisher-Subscriber mechanism in order to * retrieve them when they are available. This is not so straightforward * because these two methods above are supposed to be blocking. Instead we * post a request to the Servant to fill in the credentials when available. * We pass a {@link SplashScreenFuture} along with the request so that the * Servant may set the credentials in it as soon as the user enters them and * the Proxy may block on the <code>get</code> method of the Future waiting * for this event to occur.</p> * <p><small> * Our Future is not exactly the same as the one in Active Object: in our case * the Future is known by the Servant and never returned to clients. * </small></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 */ class SplashScreenProxy implements SplashScreen { /** The real subject where the component's functionality sits. */ private SplashScreenManager servant; /** * Tells whether or not the reference to the servant is still valid. * This reference is valid any time between the first call to * {@link #open()} and the first call to {@link #close()}. * If the reference is not valid, then calls silently return without * forwarding requests. This will prevent deadlock if * {@link #getUserCredentials(boolean)} is called before {@link #open()}. */ private boolean isValid; /* NOTE: Even though we send requests in the order: * open ... update ... update ... close * they may be received in a different order (we make no assumption on * Swing scheduling), for example: * update open update ... update close update * This is not big deal however, b/c SplashScreenManager will ignore * the first and last call above b/c the window is not open (isOpen field). * If update is called b/f setTotalTasks no big harm is made and we prefer * to avoid writing the code for checking this. */ /** * Creates the proxy, the servant and configures the servant with a * Future for later collection of user credentials. * * @param c Reference to the singleton {@link Container}. */ SplashScreenProxy(Container c) { servant = new SplashScreenManager(this, c); isValid = false; } /** * Implemented as specified by {@link SplashScreen}. * @see SplashScreen#open() */ public void open() { if (isValid) return; //Somebody's already called open(). /* NOTE: If open() is called again after close(), then we get in * here. However, no big harm can be made. */ //Construct request of method execution. Runnable doOpen = new Runnable() { public void run() { servant.open(); } }; //Schedule execution within Swing dispatching thread. SwingUtilities.invokeLater(doOpen); isValid = true; } /** * Implemented as specified by {@link SplashScreen}. * @see SplashScreen#close() */ public void close() { if (!isValid) return; //Somebody's already called close(). //Construct request of method execution. Runnable doClose = new Runnable() { public void run() { servant.close(); } }; //Schedule execution within Swing dispatching thread. SwingUtilities.invokeLater(doClose); isValid = true; } /** * Implemented as specified by {@link SplashScreen}. * @see SplashScreen#setTotalTasks(int) */ public void setTotalTasks(final int value) { if (!isValid) return; //Somebody's already called close(). //Construct request of method execution. Runnable doSetTotalTasks = new Runnable() { public void run() { servant.setTotalTasks(value); } }; //Schedule execution within Swing dispatching thread. SwingUtilities.invokeLater(doSetTotalTasks); } /** * Implemented as specified by {@link SplashScreen}. * @see SplashScreen#updateProgress(String) */ public void updateProgress(final String task) { if (!isValid) return; //Somebody's already called close(). //Construct request of method execution. Runnable doUpdateProgress = new Runnable() { public void run() { servant.updateProgress(task); } }; //Schedule execution within Swing dispatching thread. SwingUtilities.invokeLater(doUpdateProgress); } /** * Implemented as specified by {@link SplashScreen}. * @see SplashScreen#getUserCredentials(boolean) */ public UserCredentials getUserCredentials(final boolean init) { //First off, let's make sure that this.open() has already been called. //If not we return to prevent deadlock. if (!isValid) return null; //Construct request of method execution. final SplashScreenFuture future = new SplashScreenFuture(); Runnable doCollectUserCredentials = new Runnable() { public void run() { servant.collectUserCredentials(future, init); } }; //Schedule execution within Swing dispatching thread. SwingUtilities.invokeLater(doCollectUserCredentials); //Now we can safely wait for the user to enter their credentials. return (UserCredentials) future.get(); } /** * Implemented as specified by {@link SplashScreen}. * @see SplashScreen#onLoginFailure() */ public void onLoginFailure() { servant.onLoginFailure(); } }