/* * org.openmicroscopy.shoola.env.data.login.LoginService * *------------------------------------------------------------------------------ * 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.login; //Java imports //Third-party libraries //Application-internal dependencies import org.openmicroscopy.shoola.env.event.AgentEvent; import org.openmicroscopy.shoola.env.event.AgentEventListener; /** * Defines the high-level functionality required to log onto <i>OMERO</i>. * <p>The Login Service provides an abstraction over the process of logging * onto <i>OMERO</i>. This service is only used directly by the Container * either during initialization or when an invalid link is detected to try and * reestablish it — this could happen because the connection went down, * the session expired, or the user has an invalid login. In fact, during the * initialization procedure the user is asked to enter their credentials, which * the Initializer then passes along to the Login Service so to establish the * initial link to the server. Then whenever a method in the Data Services * raises an exception due to an invalid link, the Data Services throw a <code> * DSOutOfServiceException</code> to inform the caller (typically an Agent) and * ask the Login Service to reestablish the broken link. At which point, the * Login Service attempts to reconnect to <i>OMERO</i> using the current user's * credentials — that is, the credentials that the user entered during the * initialization procedure. So many attempts are made as specified in the * Container's configuration file. If all attempts fail, then a dialog is * brought up on screen to allow the user to reenter their credentials (which * now become the current credentials) and a last attempt is made. If this * fails too, then the Login Service gives up and informs the user through an * error dialog.</p> * <p>Because the Container, through the Login Service, transparently manages * the connection to <i>OMERO</i>, Agents never need to make an explicit call * to the Login Service. However, a mechanism is provided so that Agents can * be notified if the link to <i>OMERO</i> is reestablished. This allows an * Agent to gracefully recover from a <code>DSOutOfServiceException</code>. * It works as follows. The Agent registers with the <code>Event Bus</code> * for <code>ServiceActivationResponse</code>s and then catches a <code> * DSOutOfServiceException</code> to post a <code>ServiceActivationRequest. * </code> This will be responded by the Login Service with a <code> * ServiceActivationResponse</code>, whose <code>isActivationSuccessful</code> * method can be used to find out if the link has been reestablished. If so, * the Agent can retry the original call to the Data Services. Note that if * the Agent implements this for more than one Data Services method, then * upon receipt of a <code>ServiceActivationResponse</code> some sort of * de-multiplexing could be required to find out what was the method that * originally raised the <code>DSOutOfServiceException</code> — assuming * the Agent wants to retry that call. Instead of writing endless <code>if- * else</code> or <code>switch</code> blocks, you may want to consider the * fact that a <code>ServiceActivationResponse</code> works like an Asynchronous * Completion Token (ACT). Here's a code example that illustrates a possible * recovery strategy implemented by a fictitious Agent and how to take advantage * of ACTs:</p> * <p><code><pre> * public class MyAgent * implements Agent, AgentEventListener * { * private Registry context; * * public void setContext(Registry ctx) * { * this.context = ctx; * * //Register with the Event Bus for ServiceActivationResponses * EventBus bus = ctx.getEventBus(); * bus.register(this, ServiceActivationResponse.class); * } * * public void displayProjects() * { * DataManagementService dms = context.getDataManagementService(); * try { * List projects = dms.retrieveUserProjects(); * * //Display the projects in some UI widget... * * } catch (DSOutOfServiceException e) { * //For some reason our link to OMEDS is not valid at the moment. * //The Container is working under the hood to reestablish a valid * //link, but we want to know when and if the Container succeeds. * //So we're going to post a ServiceActivationRequest. * ServiceActivationRequest req = new ServiceActivationRequest( * ServiceActivationRequest.DATA_SERVICES); * req.setSource(this); * * //Because we want to retry the call in the case the Container * //manages to reestablish a valid link, we set a CompletionHanlder * //which will be dispatched when we receive the response. * //(See eventFired below.) * req.setCompletionHandler(new CompletionHandler() { * public void handle(RequestEvent request, * ResponseEvent response) * { * ServiceActivationResponse sar = * (ServiceActivationResponse) response; * if (sar.isActivationSuccessful()) * //We have a valid link to OMEDS. Retry the call. * displayProjects(); * } * }); * * //Now we can post the request. The Container will notify us of * //the outcome of its attempts to reestablish a link with a * //ServiceActivationResponse. (See eventFired below.) * EventBus bus = context.getEventBus(); * bus.post(req); * * } catch (DSAccessException e) { * //Tell the user to retry... * } * } * * public void displayDatasets() * { * //Similar to displayProjects... * } * * public void eventFired(AgentEvent ae) * { * if (ae instanceof ServiceActivationResponse) { * ServiceActivationResponse sar = (ServiceActivationResponse) ae; * if (sar.getSource() == this) //This was our request. * sar.complete(); * } * //The complete method will eventually invoke the CompletionHandler. * //Note how we hanlde *all* cases (displayProjects, displayDatasets, * //etc.) with a *single* statement. * //Also note that we make sure we only hanlde requests that this * //Agent posted. In fact, multiple ServiceActivationResponses could * //be travelling on the Event Bus if more than one Agent posted * //ServiceActivationRequests. * } * * * //Other methods... * * } //End of MyAgent. * </pre></code></p> * <p>The Login Service is thread-safe. If multiple threads attempt to login * at the same time, then only one is allowed to proceed as all the others will * have to wait for the outcome of the login attempt. (Note that because a * <code>DataServiceView</code> usually runs asynchronously with respect to the * UI, it's possible that two or more concurrent calls to the Data Services fail * at approximately the same time, which would result in concurrent calls to the * Login Service.)</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 interface LoginService extends AgentEventListener { /** * Indicates that the client couldn't connect b/c a * <code>DNSException</code> was thrown by the server. */ public static final int DNS_INDEX = 100; /** * Indicates that the client couldn't connect b/c a * <code>PermissionException</code> was thrown by the server. */ public static final int PERMISSION_INDEX = 101; /** * Indicates that the client couldn't connect b/c a * <code>ConnectionException</code> was thrown by the server. */ public static final int CONNECTION_INDEX = 102; /** * Indicates that the client couldn't connect b/c the user is no longer * active */ public static final int ACTIVE_INDEX = 103; /** * Indicates that the client couldn't connect b/c a * <code>Ice.FileException</code> was thrown by the server. */ public static final int CONFIGURATION_INDEX = 104; /** Indicates that a failure occurred preventing the client to start.*/ public static final int SYSTEM_FAILURE_INDEX = 105; /** * Flag to denote the Idle state. * While in this state, the Login Service is waiting for a login request. */ public static final int IDLE = 0; /** * Flag to denote the Attempting Login state. * While in this state, the Login Service is carrying out a login request. */ public static final int ATTEMPTING_LOGIN = 1; /** Flag indicating that the client is connected to the server. */ public static final int CONNECTED = 1; /** Flag indicating that the client is not connected to the server. */ public static final int NOT_CONNECTED = 0; /** Flag indicating that the connection's attempt's has time out. */ public static final int TIMEOUT = 2; /** Flag indicating that the client and server are not compatible.*/ public static final int INCOMPATIBLE = 3; /** * Returns the current state of the Login Service. * * @return One of the state flags defined by this interface. */ public int getState(); /** * Attempts to login using the current user's credentials. * If the user hasn't entered their login credentials yet, then this method * just returns without attempting a connection to <i>OMEDS</i>. * Otherwise, the most recently entered user's credentials are used to * attempt establishing a valid link to the server. * Upon failure, this method retries for how many times as specified by the * Container's configuration. (The time interval between each attempt is * also specified by the Container's configuration.) If all attempts fail, * then a dialog is brought up on screen to allow the user to reenter their * credentials (which now become the current credentials) and a last attempt * is made. If this fails too, then this method gives up and informs the * user through an error dialog. */ public void login(); /** * Attempts to login using the specified user's credentials. * If <code>uc</code> is <code>null</code>, then then this method just * returns without attempting a connection to <i>OMEDS</i>. Otherwise, * <code>uc</code> becomes the current user's credentials and an attempt * is made to establish a link to the server. If the attempt fails, the * user is informed through an error dialog. * * @param uc The new user's credentials. * @return <code>true</code> upon success, <code>false</code> in the case * of failure. */ public int login(UserCredentials uc); /** * Implemented to receive notification of a <code>ServiceActivationRequest * </code> coming from an Agent. * A <code>ServiceActivationResponse</code> is posted back to inform the * Agent about the current login state. That is, if there's a valid link * to the server. * * @param serviceActivationRequest A <code>ServiceActivationRequest</code> * posted by an Agent. */ public void eventFired(AgentEvent serviceActivationRequest); /** Updates the view when failed to log in. */ public void notifyLoginFailure(); /** Updates the view when failed to log in. */ public void notifyLoginTimeout(); }