/*
* 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(); }
}