/* * org.openmicroscopy.shoola.env.data.login.LoginServiceImpl * *------------------------------------------------------------------------------ * Copyright (C) 2006-2013 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 import java.awt.Toolkit; import java.util.Timer; import java.util.TimerTask; import javax.swing.JFrame; import Glacier2.PermissionDeniedException; import Ice.ConnectionRefusedException; import Ice.DNSException; import org.openmicroscopy.shoola.util.CommonsLangUtils; import omero.SecurityViolation; import org.openmicroscopy.shoola.env.Container; import org.openmicroscopy.shoola.env.LookupNames; import omero.gateway.exception.DSOutOfServiceException; import org.openmicroscopy.shoola.env.data.DataServicesFactory; import org.openmicroscopy.shoola.env.data.events.ServiceActivationRequest; import org.openmicroscopy.shoola.env.data.events.ServiceActivationResponse; import org.openmicroscopy.shoola.env.event.AgentEvent; import org.openmicroscopy.shoola.env.event.EventBus; import omero.log.LogMessage; import omero.log.Logger; import org.openmicroscopy.shoola.env.ui.IconManager; import org.openmicroscopy.shoola.util.ui.NotificationDialog; import org.openmicroscopy.shoola.util.ui.UIUtilities; /** * Implements the Login Service's logic. * This class ignores threading issues altogether and just focuses on providing * the service's logic. The {@link LoginManager} should be used to decorate an * instance of this class to obtain a thread-safe service. * * @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 * @since OME2.2 */ public class LoginServiceImpl implements LoginService { /** * Holds one of the state flags defined by the {@link LoginService} * interface to indicate the current state of the service. */ private int state; /** Reference to the runtime environment. */ private Container container; /** * The timer used to establish a valid link to an <code>OMERO</code> * server. */ private Timer timer; /** Flag indicating if the attempt to connect has started. */ private boolean connAttempt; /** The index set if an error occurred while trying to connect. */ private int failureIndex; /** Allows to easily access the service's configuration. */ protected LoginConfig config; /** * Suspends execution for as many milliseconds as specified by the * service's configuration. */ private void pause() { try { Thread.sleep(config.getRetryInterval()); } catch (InterruptedException e) {} } /** * Attempts to log onto <i>OMERO</i> using the current user's * credentials. * Failure or success is reported to the Log Service. * * @return <code>true</code> on success, <code>false</code> on failure. */ private int attempt() { UserCredentials uc = config.getCredentials(); try { if (uc == null) throw new DSOutOfServiceException( "No user's credentials have been entered yet."); //NOTE: This will never happen if the Splash Screen always blocks //waiting for the user's credentials and the login dialog never //passes null credentials along. DataServicesFactory factory = DataServicesFactory.getInstance( container); if (factory.isConnected()) return CONNECTED; //factory.connect(uc); if (timer == null) { timer = new Timer(); timer.schedule(new LoginTask(), config.getTimeout()); } factory.connect(uc); if (!factory.isCompatible()) return INCOMPATIBLE; if (factory.isConnected() && connAttempt) { //Log success. LogMessage msg = new LogMessage(); msg.println("Logged onto OMERO at: "+uc.getHostName()); msg.println(uc); Logger logger = container.getRegistry().getLogger(); logger.info(this, msg); timer.cancel(); return CONNECTED; } if (!connAttempt) { timer.cancel(); //Log success. LogMessage msg = new LogMessage(); msg.println("Cannot connect OMERO at: "+uc.getHostName()); msg.println(uc); Logger logger = container.getRegistry().getLogger(); logger.info(this, msg); return TIMEOUT; } timer.cancel(); LogMessage msg = new LogMessage(); msg.println("Cannot connect OMERO at: "+uc.getHostName()); msg.println(uc); Logger logger = container.getRegistry().getLogger(); logger.info(this, msg); return NOT_CONNECTED; } catch (Exception exception) { //Log failure. if (exception instanceof DSOutOfServiceException) { Throwable cause = exception.getCause(); if (cause instanceof ConnectionRefusedException) { failureIndex = CONNECTION_INDEX; } else if (cause instanceof DNSException) { failureIndex = DNS_INDEX; } else if (cause instanceof PermissionDeniedException) { failureIndex = PERMISSION_INDEX; } else if (cause instanceof Ice.FileException) { failureIndex = CONFIGURATION_INDEX; } else if (cause instanceof DSOutOfServiceException) { if (cause.getCause() instanceof SecurityViolation) failureIndex = ACTIVE_INDEX; } } else failureIndex = SYSTEM_FAILURE_INDEX; LogMessage msg = new LogMessage(); msg.println("Failed to log onto OMERO."); msg.println("Reason: "+exception.getMessage()); if (uc != null) { msg.println("OMERO address: "+uc.getHostName()); msg.println(uc); } msg.print(exception); Logger logger = container.getRegistry().getLogger(); logger.debug(this, msg); } return NOT_CONNECTED; } /** * Brings up a login dialog to let the user enter their credentials. * The dialog will then call the <code>login</code> method passing * along the new user's credentials. */ protected void askForCredentials() { } //NOTE: This method is protected so that subclasses can get rid of the //dependencies on Swing. This is useful in test mode. /** * Creates a new instance. * It is assumed that the Container's configuration has already been read * in and that the Event Bus is available. * * @param c Reference to the runtime environment. * Mustn't be <code>null</code>. */ public LoginServiceImpl(Container c) { if (c == null) throw new NullPointerException("No container."); config = new LoginConfig(c.getRegistry()); this.container = c; EventBus bus = c.getRegistry().getEventBus(); bus.register(this, ServiceActivationRequest.class); state = IDLE; connAttempt = true; failureIndex = 0; } /** * Implemented as specified by the {@link LoginService} interface. * @see LoginService#getState() */ public int getState() { return state; } /** * Implemented as specified by the {@link LoginService} interface. * @see LoginService#login() */ public void login() { state = ATTEMPTING_LOGIN; int max = config.getMaxRetry(); while (0 < max--) { if (attempt() == CONNECTED) { state = IDLE; return; } pause(); } askForCredentials(); } /** * Implemented as specified by the {@link LoginService} interface. * @see LoginService#login(UserCredentials) */ public int login(UserCredentials uc) { if (uc == null) return NOT_CONNECTED; String name = uc.getUserName(); if (CommonsLangUtils.isBlank(name)) return NOT_CONNECTED; state = ATTEMPTING_LOGIN; config.setCredentials(uc); int succeeded = attempt(); state = IDLE; if (succeeded == CONNECTED) container.getRegistry().bind(LookupNames.USER_CREDENTIALS, uc); return succeeded; } /** * Implemented as specified by the {@link LoginService} interface. * @see LoginService#eventFired(AgentEvent) */ public void eventFired(AgentEvent ae) { //Can only be a ServiceActivationRequest, as we only registered //for that kind of event. Check if we're connected and reply. ServiceActivationRequest sar = (ServiceActivationRequest) ae; boolean connected = false; try { DataServicesFactory factory = DataServicesFactory.getInstance( container); connected = factory.isConnected(); } catch (DSOutOfServiceException dsose) {} ServiceActivationResponse resp = new ServiceActivationResponse( sar, connected); EventBus bus = container.getRegistry().getEventBus(); bus.post(resp); } /** * Implemented as specified by the {@link LoginService} interface. * @see LoginService#notifyLoginFailure() */ public void notifyLoginFailure() { JFrame f = container.getRegistry().getTaskBar().getFrame(); String text = ""; switch (failureIndex) { case LoginService.DNS_INDEX: text = "Please check the server address\nor try again later."; break; case LoginService.CONNECTION_INDEX: text = "Please check the port\nor try again later."; break; case LoginService.ACTIVE_INDEX: text = "Your user account is no longer active.\nPlease" + " contact your administrator."; break; case LoginService.CONFIGURATION_INDEX: text = "Please unset ICE_CONFIG."; break; case LoginService.SYSTEM_FAILURE_INDEX: text = "Error: System Failure."; break; case LoginService.PERMISSION_INDEX: default: text = "Please check your user name\nand/or password " + "or try again later."; } NotificationDialog dialog = new NotificationDialog( f, "Login Failure", "Failed to log onto OMERO.\n"+text, IconManager.getDefaultErrorIcon()); dialog.pack(); UIUtilities.centerAndShow(dialog); } /** * Implemented as specified by the {@link LoginService} interface. * @see LoginService#notifyLoginTimeout() */ public void notifyLoginTimeout() { JFrame f = container.getRegistry().getTaskBar().getFrame(); //Need to do it that way to keep focus on login dialog NotificationDialog dialog = new NotificationDialog( f, "Login Failure", "Failed to log onto OMERO.\n" + "The server entered is not responding.\n"+ "Please check the server address or try again later.", IconManager.getDefaultErrorIcon()); dialog.pack(); UIUtilities.centerAndShow(dialog); } /** Helper inner class. */ class LoginTask extends TimerTask { /** * Sets the {@link #connAttempt} flag. * @see TimerTask#run() */ public void run() { Toolkit.getDefaultToolkit().beep(); connAttempt = false; timer.cancel(); } } }