/*
*------------------------------------------------------------------------------
* Copyright (C) 2015-2016 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 omero.gateway.facility;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.net.UnknownHostException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import ome.conditions.SessionTimeoutException;
import omero.AuthenticationException;
import omero.DatabaseBusyException;
import omero.ResourceError;
import omero.SecurityViolation;
import omero.SessionException;
import omero.gateway.Gateway;
import omero.gateway.exception.ConnectionStatus;
import omero.gateway.exception.DSAccessException;
import omero.gateway.exception.DSOutOfServiceException;
import omero.log.LogMessage;
import Glacier2.CannotCreateSessionException;
import Ice.CommunicatorDestroyedException;
import Ice.ConnectionLostException;
import Ice.ConnectionRefusedException;
import Ice.ConnectionTimeoutException;
import Ice.DNSException;
import Ice.ObjectNotExistException;
import Ice.SocketException;
import Ice.TimeoutException;
import Ice.UnknownException;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* A Facility encapsulates a certain set of functionality for dealing with an
* OMERO server
*
* @author Dominik Lindner <a
* href="mailto:d.lindner@dundee.ac.uk">d.lindner@dundee.ac.uk</a>
* @since 5.1
*/
public abstract class Facility {
/** Holds references to the different facilities so that they can be reused */
private static final Cache<String, Facility> cache = CacheBuilder
.newBuilder().build();
/** Reference to the {@link Gateway} */
final Gateway gateway;
/** The PropertyChangeSupport */
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/**
* Creates a new instance
*
* @param gateway
* Reference to the {@link Gateway}
*/
Facility(Gateway gateway) {
this.gateway = gateway;
}
/**
* Get a reference to a certain Facility
*
* @param type
* The type of the Facility
* @param gateway
* Reference to the {@link Gateway}
* @return See above
* @throws ExecutionException
* If the {@link Facility} can't be retrieved or instantiated
*/
public static <T extends Facility> T getFacility(final Class<T> type,
final Gateway gateway) throws ExecutionException {
return (T) cache.get(type.getSimpleName(), new Callable<Facility>() {
@Override
public Facility call() throws Exception {
gateway.getLogger().debug(this,
"Created new " + type.getSimpleName());
Facility facility = type.getDeclaredConstructor(Gateway.class).newInstance(
gateway);
for (PropertyChangeListener l : gateway
.getPropertyChangeListeners()) {
facility.addPropertyChangeListener(l);
facility.pcs.firePropertyChange(
Gateway.PROP_FACILITY_CREATED, null, type.getName());
}
return facility;
}
});
}
/**
* Clears the Facility object cache
*/
public static void clear() {
Facility.cache.invalidateAll();
}
/**
* Adds a {@link PropertyChangeListener}
* @param listener The listener
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.pcs.addPropertyChangeListener(listener);
}
/**
* Removes a {@link PropertyChangeListener}
* @param listener The listener
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.pcs.removePropertyChangeListener(listener);
}
/**
* Helper method to simplify logging
*
* @param originator
* The source of the log message
* @param msg
* The message
* @param t
* The exception
*/
public void logDebug(Object originator, String msg, Throwable t) {
gateway.getLogger().debug(originator, new LogMessage(msg, t));
}
/**
* Helper method to simplify logging
*
* @param originator
* The source of the log message
* @param msg
* The message
* @param t
* The exception
*/
public void logInfo(Object originator, String msg, Throwable t) {
gateway.getLogger().info(originator, new LogMessage(msg, t));
}
/**
* Helper method to simplify logging
*
* @param originator
* The source of the log message
* @param msg
* The message
* @param t
* The exception
*/
public void logWarn(Object originator, String msg, Throwable t) {
gateway.getLogger().warn(originator, new LogMessage(msg, t));
}
/**
* Helper method to simplify logging
*
* @param originator
* The source of the log message
* @param msg
* The message
* @param t
* The exception
*/
public void logError(Object originator, String msg, Throwable t) {
gateway.getLogger().error(originator, new LogMessage(msg, t));
}
/**
* Helper method to handle exceptions thrown by the connection library.
* Methods in this class are required to fill in a meaningful context
* message. This method is not supposed to be used in this class'
* constructor or in the login/logout methods.
*
* @param t
* The exception.
* @param message
* The context message.
* @throws DSOutOfServiceException
* A connection problem.
* @throws DSAccessException
* A server-side error.
*/
void handleException(Object originator, Throwable t, String message)
throws DSOutOfServiceException, DSAccessException {
logError(originator, message, t);
ConnectionStatus b = getConnectionStatus(t);
if (b != ConnectionStatus.OK)
return;
if (!gateway.isConnected())
return;
Throwable cause = t.getCause();
if (cause instanceof SecurityViolation) {
String s = "For security reasons, cannot access data. \n";
throw new DSAccessException(s + message, cause);
} else if (cause instanceof SessionException) {
String s = "Session is not valid. \n";
throw new DSOutOfServiceException(s + message, cause);
} else if (cause instanceof AuthenticationException) {
String s = "Cannot initialize the session. \n";
throw new DSOutOfServiceException(s + message, cause);
} else if (cause instanceof ResourceError) {
String s = "Fatal error. Please contact the administrator. \n";
throw new DSOutOfServiceException(s + message, t);
}
throw new DSAccessException("Cannot access data. \n" + message, t);
}
/**
* Returns one of the constants defined by this class or <code>-1</code>.
*
* @param e
* The exception to handle.
* @return See above.
*/
private ConnectionStatus getConnectionStatus(Throwable e) {
if (e instanceof DSOutOfServiceException) {
DSOutOfServiceException dso = (DSOutOfServiceException) e;
if (dso.getConnectionStatus() != null)
return dso.getConnectionStatus();
}
Throwable cause = e.getCause();
if (cause instanceof ConnectionLostException
|| e instanceof ConnectionLostException
|| cause instanceof SessionTimeoutException
|| e instanceof SessionTimeoutException
|| cause instanceof TimeoutException
|| e instanceof TimeoutException
|| cause instanceof ObjectNotExistException
|| e instanceof ObjectNotExistException
|| cause instanceof DNSException || e instanceof DNSException)
return ConnectionStatus.LOST_CONNECTION;
else if (cause instanceof CommunicatorDestroyedException
|| e instanceof CommunicatorDestroyedException)
return ConnectionStatus.DESTROYED_CONNECTION;
else if (cause instanceof SocketException
|| e instanceof SocketException
|| e instanceof UnknownHostException)
return ConnectionStatus.NETWORK;
else if (cause instanceof ConnectionRefusedException
|| e instanceof ConnectionRefusedException
|| cause instanceof ConnectionTimeoutException
|| e instanceof ConnectionTimeoutException
|| cause instanceof DatabaseBusyException
|| e instanceof DatabaseBusyException
|| e instanceof CannotCreateSessionException
|| cause instanceof CannotCreateSessionException)
return ConnectionStatus.SERVER_OUT_OF_SERVICE;
else if (cause instanceof UnknownException)
return handleIceUnknownException(cause);
else if (e instanceof UnknownException)
return handleIceUnknownException(e);
return ConnectionStatus.OK;
}
/**
* Handles the <code>Ice.UnknownException</code>. Returns the index
* depending on the unknown message.
*
* @param e
* The exception to handle.
* @return See above.
*/
private ConnectionStatus handleIceUnknownException(Throwable e) {
UnknownException ex = (UnknownException) e;
if (ex.unknown.contains("Ice::ConnectionRefusedException"))
return ConnectionStatus.SERVER_OUT_OF_SERVICE;
else if (ex.unknown.contains("Ice::ConnectionLostException"))
return ConnectionStatus.LOST_CONNECTION;
return ConnectionStatus.OK;
}
}