/*
* Copyright 2008 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.services.blitz;
import java.util.concurrent.locks.ReentrantLock;
import ome.system.OmeroContext;
import ome.util.messages.ShutdownMessage;
import ome.util.messages.UserSignalMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
* OMERO.blitz startup code. Replaces the standard <code>Main</code> class as
* the main application entry point. Uses Sun-specific APIs to handle signals.
*/
public class Entry {
private final static Logger log = LoggerFactory.getLogger(Entry.class);
/**
* Return code status. Initially -1. On successful start, 0. > 1 on
* exception.
*/
int status = -1;
/**
* Name of the {@link OmeroContext} to
*/
final String name;
/**
* Prevents multiple calls to {@link #shutdown()}
*/
final ReentrantLock lock = new ReentrantLock();
/**
* Context for the OMERO application
*/
volatile OmeroContext ctx = null;
/**
* {@link Ice.Communicator} which will be waited on.
*/
volatile Ice.Communicator ic = null;
private static void waitOnStartup() {
int ms = 10000; // 10 seconds by default
try {
String prop = System.getenv("OMERO_STARTUP_WAIT");
ms = Integer.valueOf(prop);
} catch (Exception e) {
log.debug(e.toString()); // slf4j migration: toString()
}
try {
log.info(String.format("Waiting %s ms on startup", ms));
Thread.sleep(ms);
} catch (InterruptedException e) {
log.debug(e.toString()); // slf4j migration: toString()
}
}
/**
* Entry point to the server. The first argument on the command line will be
* used as the name for the {@link OmeroContext}. Other options include:
*
* -s Check status (all args passed to {@link Ice.Util#initialize(String[])}
*
*/
public static void main(final String[] args) {
String name = "OMERO.blitz";
if (args != null && args.length > 0) {
if ("-s".equals(args[0])) {
try {
new Status(args).run();
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
System.exit(0);
}
// Now we find the first non-"--Ice.Config" argument and
// pass that to main(). The last --Ice.Config value will be
// seen by the Ice.Communicator. The last non-Ice.Config argument
// will be used as a beanRefContext lookup.
for (String string : args) {
if (string.startsWith("--Ice.Config")) {
System.setProperty("ICE_CONFIG", string.substring(13));
} else {
name = string;
waitOnStartup();
}
}
}
final Entry instance = new Entry(name);
SignalHandler handler = new SignalHandler() {
public void handle(Signal sig) {
log.info(sig.getName() + ": Shutdown requested.");
instance.lock.lock();
try {
instance.status = sig.getNumber();
instance.shutdown(true);
} finally {
// As with the try/finally block around shutdown in the
// start method, execution should never reach this point.
// But just in case, future code changes should introduce
// an exception (and to make findbugs happy) we'll add the
// try/finally
instance.lock.unlock();
}
}
};
registerSignal(handler, "INT");
registerSignal(handler, "TERM");
registerSignal(handler, "BREAK");
// ticket:#4210
class UserSignalHandler implements SignalHandler {
int signal;
UserSignalHandler(int signal) {
this.signal = signal;
}
public void handle(Signal arg0) {
try {
UserSignalMessage msg = new UserSignalMessage(this, signal);
instance.ctx.publishMessage(msg);
} catch (Throwable e) {
log.error("Error on user signal " + signal, e);
}
}
};
registerSignal(new UserSignalHandler(1), "USR1");
registerSignal(new UserSignalHandler(2), "USR2");
instance.start();
}
private static void registerSignal(SignalHandler handler, String sig) {
try {
Signal.handle(new Signal(sig), handler);
} catch (IllegalArgumentException iae) {
// Ok. BREAK will not exist on non-Windows systems, for example.
}
}
/**
* Stores name of the {@link OmeroContext} which is to be used by this
* instance.
*/
public Entry(String name) {
this.name = name;
}
/**
* Obtains the {@link #name named} {@link OmeroContext}, creating it if
* necessary, and then delegates to
* {@link Ice.Communicator#waitForShutdown()} until either it is externally
* shutdown, or until a signal is caught.
*/
public void start() {
try {
log.info("Creating " + name + ". Please wait...");
// TEMPORARY WORKAROUND
String ICE_CONFIG = System.getProperty("ICE_CONFIG");
// Parse out any omero.* properties from ICE_CONFIG
// and set them in the System
Ice.InitializationData id = new Ice.InitializationData();
id.properties = Ice.Util.createProperties();
if (ICE_CONFIG != null) {
id.properties.load(ICE_CONFIG);
for (String k : id.properties.getPropertiesForPrefix("omero").keySet()) {
System.setProperty(k, id.properties.getProperty(k));
}
}
ctx = OmeroContext.getInstance(name);
if (ctx.containsBean("Ice.Communicator")) {
ic = (Ice.Communicator) ctx.getBean("Ice.Communicator");
} else {
// TODO This should be adapted to work for any process
// that doesn't need to add servants. Here "Indexer" could
// be replaced by omero.name or similar.
ic = Ice.Util.initialize(id);
String adapterName = ctx.getBean("adapterName", String.class);
Ice.ObjectAdapter oa = ic.createObjectAdapter(adapterName);
oa.activate();
}
log.info(name + " now accepting connections.");
ic.waitForShutdown();
status = 0;
} catch (Ice.LocalException e) {
log.error("Error on startup.", e);
status = 1;
} catch (Exception e) {
log.error("Error on startup.", e);
status = 2;
}
System.out.flush();
System.err.flush();
lock.lock();
try {
shutdown(true);
} finally {
// This will never be called since System.exit is called in
// shutdown where no exception can be thrown, but just in case
// the code paths are ever changed and the exit doesn't get called
// we'll unlock so other threads won't hang the server.
lock.unlock();
}
}
public int status() {
return status;
}
/**
* Calls {@link OmeroContext#closeAll()} to recursively close all
* OMERO.blitz resources in the reverse order that they were created.
*
* Throws no exceptions.
*
* If true is passed for callSystemExit, then {@link System#exit(int)} will
* be called with the current status.
*/
public void shutdown(final boolean callSystemExit) {
// Finally shutdown the whole context
if (ctx != null) {
try {
ctx.publishMessage(new ShutdownMessage(this));
log.info("Calling close on context " + name);
OmeroContext forClose = ctx;
ctx = null;
forClose.closeAll();
log.info("Finished shutdown.");
} catch (Throwable t) {
log.error("Error shutting down " + name, t);
status = 3;
}
}
if (callSystemExit) {
System.exit(status);
}
}
}