/* $HeadURL$
* $Id$
*
* Copyright (c) 2009-2010 DuraSpace
* http://duraspace.org
*
* In collaboration with Topaz Inc.
* http://www.topazproject.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.akubraproject.rmi;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.akubraproject.BlobStore;
import org.akubraproject.rmi.server.Exportable;
import org.akubraproject.rmi.server.Exporter;
import org.akubraproject.rmi.server.ServerConnection;
import org.akubraproject.rmi.server.ServerStore;
/**
* Akubra RMI server.
*
* @author Pradeep Krishnan
*/
public class AkubraRMIServer {
/**
* A default name for akubra-rmi-servers. Used for RMI registration when none supplied.
*/
public static final String DEFAULT_SERVER_NAME = "akubra-rmi";
private static final Logger log = LoggerFactory.getLogger(AkubraRMIServer.class);
private static final long serialVersionUID = 1L;
private final Registry registry;
private final String name;
private final ServerStore store;
/**
* Exports the given Blob Store for remote access.
*
* @param store the store to export.
*
* @throws AccessException when the caller does not have permission to export
* @throws RemoteException on any other error in export
*/
public AkubraRMIServer(BlobStore store) throws AccessException, RemoteException {
this(store, DEFAULT_SERVER_NAME, 0);
}
/**
* Creates an AkubraRMIServer that exports the given Blob Store for remote access.
*
* @param store the store to export.
* @param port the RMI registry port
*
* @throws AccessException when the caller does not have permission to export
* @throws RemoteException on any other error in export
*/
public AkubraRMIServer(BlobStore store, int port)
throws AccessException, RemoteException {
this(store, DEFAULT_SERVER_NAME, port);
}
/**
* Creates an AkubraRMIServer that exports the given Blob Store for remote access.
*
* @param store the store to export.
* @param name the name to register with RMI registry
*
* @throws AccessException when the caller does not have permission to export
* @throws RemoteException on any other error in export
*/
public AkubraRMIServer(BlobStore store, String name)
throws AccessException, RemoteException {
this(store, name, 0);
}
/**
* Creates an AkubraRMIServer that exports the given Blob Store for remote access.
*
* @param store the store to export.
* @param name the name to register with RMI registry
* @param port the RMI registry port
*
* @throws AccessException when the caller does not have permission to export
* @throws RemoteException on any other error in export
*/
public AkubraRMIServer(BlobStore store, String name, int port)
throws AccessException, RemoteException {
this(store, name, port, port);
}
/**
* Creates an AkubraRMIServer that exports the given Blob Store for remote access.
*
* @param store the store to export.
* @param name the name to register with RMI registry
* @param registryPort the RMI registry port
* @param port the port where the BlobStore is exported at
*
* @throws AccessException when the caller does not have permission to export
* @throws RemoteException on any other error in export
*/
public AkubraRMIServer(BlobStore store, String name, int registryPort, int port)
throws AccessException, RemoteException {
this(store, name, ensureRegistry(registryPort), registryPort, port);
}
/**
* Creates an AkubraRMIServer that exports the given Blob Store for remote access.
*
* @param store the store to export.
* @param name the name to register with RMI registry
* @param registry the RMI registry
* @param registryPort the RMI registry port
* @param port the port where the BlobStore is exported at
*
* @throws AccessException when the caller does not have permission to export
* @throws RemoteException on any other error in export
*/
public AkubraRMIServer(BlobStore store, String name, Registry registry, int registryPort,
int port) throws AccessException, RemoteException {
this(store, name, registry, registryPort, new Exporter(port));
}
/**
* Creates an AkubraRMIServer that exports the given Blob Store for remote access.
*
* @param store the store to export.
* @param name the name to register with RMI registry
* @param registry the RMI registry
* @param registryPort the RMI registry port
* @param exporter the exporter for the BlobStore
*
* @throws AccessException when the caller does not have permission to export
* @throws RemoteException on any other error in export
*/
public AkubraRMIServer(BlobStore store, String name, Registry registry, int registryPort,
Exporter exporter) throws AccessException, RemoteException {
log.info("Starting server '" + name + "' ...");
this.store = new ServerStore(store, exporter);
this.registry = registry;
this.name = name;
registry.rebind(name, this.store);
log.info("Server '" + name + "' is bound to registry on port " + registryPort);
}
/**
* Shutdown this server.
*
* @param abort if true, will abort all current in progress calls and close all connections.
*
* @throws AccessException when the caller does not have permission to shutdown
* @throws RemoteException on any other error in registry unbind
*/
public void shutDown(boolean abort) throws AccessException, RemoteException {
if (abort)
log.info("Starting shutdown(abort) of server '" + name + "'");
else
log.info("Starting shutdown of server '" + name + "'");
try {
log.info("Ubinding from registry ...");
registry.unbind(name);
} catch (NotBoundException e) {
log.info("Server '" + name + "' was already unbound.", e);
}
log.info("Unexporting server instance...");
store.unExport(true);
Set<ServerConnection> cons = new HashSet<ServerConnection>();
do {
int others = 0;
cons.clear();
for (Exportable exported : store.getExporter().getExportedObjects()) {
if (exported instanceof ServerConnection)
cons.add((ServerConnection) exported);
else
others++;
}
if (cons.isEmpty() && (others == 0)) {
log.info("No connections are open and no exported objects. Shutdown is complete.");
return;
}
log.info("There are " + cons.size() + " open connections and " + others + " exported objects.");
if (!cons.isEmpty() && !abort) {
log.info("Shutdown completed. Existing connections will continue till closed.");
return;
}
if (!cons.isEmpty()) {
log.info("Shutting down connections ...");
for (ServerConnection con : cons) {
try {
con.unExport(true);
con.close();
} catch (Exception e) {
log.info("Ignoring failure in connection close for " + con, e);
}
}
}
} while (!cons.isEmpty());
log.info("Unexporting all exported objects ...");
for (Exportable exported : store.getExporter().getExportedObjects()) {
try {
exported.unExport(true);
} catch (Exception e) {
log.info("Ignoring failure in unexport for " + exported, e);
}
}
if (abort)
log.info("Shutdown(abort) completed.");
else
log.info("Shutdown completed.");
}
/**
* Ensures that an RMI registry is running at the given port on localhost.
*
* @param port the RMI registry port
*
* @return the client side stub to RMI Rgistry
*
* @throws RemoteException on an error in starting up the registry
*/
public static Registry ensureRegistry(int port) throws RemoteException {
return ensureRegistry("localhost", port, null, null);
}
/**
* Ensures that an RMI registry is running at the given port on the given server.
*
* @param host the RMI registry host
* @param port the RMI registry port
* @param csf the client-side socket factory for making calls to the remote object
* @param ssf the server-side socket factory for receiving remote calls
*
* @return the client side stub to RMI Rgistry
*
* @throws RemoteException on an error in starting up the registry
*/
public static Registry ensureRegistry(String host, int port, RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws RemoteException {
if (port <= 0)
port = Registry.REGISTRY_PORT;
Registry reg = LocateRegistry.getRegistry(host, port, csf);
if (exists(reg))
log.info("Located an RMI registry at '" + host + ":" + port + "'");
else {
log.info("Starting RMI registry on 'localhost:" + port + "'");
reg = LocateRegistry.createRegistry(port, csf, ssf);
}
return reg;
}
private static boolean exists(Registry reg) {
try {
reg.lookup("akubra-server");
} catch (NotBoundException e) {
log.debug("'akubra-server' not bound", e);
} catch (RemoteException e) {
log.debug("failed to communicate with registry - assuming it's not running", e);
reg = null;
}
return (reg != null);
}
@Override
protected void finalize() throws Throwable {
if (store.getExported() != null) {
log.info("Shutting down due to finalize()");
try {
shutDown(true);
} catch (Exception e) {
log.info("Shutdown failed", e);
}
}
super.finalize();
}
}