// ********************************************************************** // // Copyright (c) 2003-2010 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** package Glacier2; /** * An extension of Ice.Application that makes it easy to write * Glacier2 applications. * * <p> Applications must create a derived class that implements the * {@link #createSession} and {@link #runWithSession} methods.<p> * * The base class invokes {@link #createSession} to create a new * Glacier2 session and then invokes {@link #runWithSession} in * which the subclass performs its application logic. The base class * automatically destroys the session when {@link #runWithSession} * returns. * * If {@link #runWithSession} calls {@link #restart} or raises any of * the exceptions Ice.ConnectionRefusedException, * Ice.ConnectionLostException, Ice.UnknownLocalException, * Ice.RequestFailedException, or Ice.TimeoutException, the base * class destroys the current session and restarts the application * with another call to {@link #createSession} followed by * {@link #runWithSession}. * * The application can optionally override the {@link #sessionDestroyed} * callback method if it needs to take action when connectivity with * the Glacier2 router is lost. * * A program can contain only one instance of this class. * * @see Ice.Application * @see Glacier2.Router * @see Glacier2.Session * @see Ice.Communicator * @see Ice.Logger * @see #runWithSession **/ public abstract class Application extends Ice.Application { /** * This exception is raised if the session should be restarted. */ public class RestartSessionException extends Exception { } /** * Initializes an instance that calls {@link Communicator#shutdown} if * a signal is received. **/ public Application() { } /** * Initializes an instance that handles signals according to the signal * policy. * * @param signalPolicy Determines how to respond to signals. * * @see SignalPolicy **/ public Application(Ice.SignalPolicy signalPolicy) { super(signalPolicy); } /** * Called once the communicator has been initialized and the Glacier2 session * has been established. A derived class must implement <code>runWithSession</code>, * which is the application's starting method. * * @param args The argument vector for the application. <code>Application</code> * scans the argument vector passed to <code>main</code> for options that are * specific to the Ice run time and removes them; therefore, the vector passed * to <code>run</code> is free from Ice-related options and contains only options * and arguments that are application-specific. * * @return The <code>runWithSession</code> method should return zero for successful * termination, and non-zero otherwise. <code>Application.main</code> returns the * value returned by <code>runWithSession</code>. **/ public abstract int runWithSession(String[] args) throws RestartSessionException; /** * Run should not be overridden for Glacier2.Application. Instead * <code>runWithSession</code> should be used. */ final public int run(String[] args) { // This shouldn't be called. assert false; return 0; } /** * Called to restart the application's Glacier2 session. This * method never returns. * * @throws RestartSessionException This exception is always thrown. **/ public void restart() throws RestartSessionException { throw new RestartSessionException(); } /** * Creates a new Glacier2 session. A call to * <code>createSession</code> always precedes a call to * <code>runWithSession</code>. If <code>Ice.LocalException</code> * is thrown from this method, the application is terminated. * @return The Glacier2 session. **/ abstract public Glacier2.SessionPrx createSession(); /** * Called when the base class detects that the session has been destroyed. * A subclass can override this method to take action after the loss of * connectivity with the Glacier2 router. **/ public void sessionDestroyed() { } /** * Returns the Glacier2 router proxy * @return The router proxy. **/ public static Glacier2.RouterPrx router() { return _router; } /** * Returns the Glacier2 session proxy * @return The session proxy. **/ public static Glacier2.SessionPrx session() { return _session; } /** * Returns the category to be used in the identities of all of the client's * callback objects. Clients must use this category for the router to * forward callback requests to the intended client. * @return The category. * @throws SessionNotExistException No session exists. **/ public String categoryForClient() throws SessionNotExistException { if(_router == null) { throw new SessionNotExistException(); } return _category; } /** * Create a new Ice identity for callback objects with the given * identity name field. * @return The identity. * @throws SessionNotExistException No session exists. **/ public Ice.Identity createCallbackIdentity(String name) throws SessionNotExistException { return new Ice.Identity(name, categoryForClient()); } /** * Adds a servant to the callback object adapter's Active Servant Map with a UUID. * @param servant The servant to add. * @return The proxy for the servant. * @throws SessionNotExistException No session exists. **/ public Ice.ObjectPrx addWithUUID(Ice.Object servant) throws SessionNotExistException { return objectAdapter().add(servant, createCallbackIdentity(java.util.UUID.randomUUID().toString())); } /** * Creates an object adapter for callback objects. * @return The object adapter. * @throws SessionNotExistException No session exists. */ public Ice.ObjectAdapter objectAdapter() throws SessionNotExistException { if(_router == null) { throw new SessionNotExistException(); } synchronized(this) { if(_adapter == null) { _adapter = communicator().createObjectAdapterWithRouter("", _router); _adapter.activate(); } } return _adapter; } private class SessionPingThread extends Thread { SessionPingThread(Glacier2.RouterPrx router, long period) { _router = router; _period = period; _done = false; } synchronized public void run() { while(true) { try { _router.refreshSession_async(new Glacier2.AMI_Router_refreshSession() { public void ice_response() { } public void ice_exception(Ice.LocalException ex) { // // Here the session has gone. The thread terminates, and we notify the // application that the session has been destroyed. // done(); sessionDestroyed(); } public void ice_exception(Ice.UserException ex) { // // Here the session has gone. The thread terminates, and we notify the // application that the session has been destroyed. // done(); sessionDestroyed(); } }); } catch(Ice.CommunicatorDestroyedException ex) { // // AMI requests can raise CommunicatorDestroyedException directly. // break; } if(!_done) { try { wait(_period); } catch(InterruptedException ex) { } } if(_done) { break; } } } public synchronized void done() { if(!_done) { _done = true; notify(); } } private final Glacier2.RouterPrx _router; private final long _period; private boolean _done = false; } protected int doMain(Ice.StringSeqHolder argHolder, Ice.InitializationData initData) { // // Set the default properties for all Glacier2 applications. // initData.properties.setProperty("Ice.ACM.Client", "0"); initData.properties.setProperty("Ice.RetryIntervals", "-1"); boolean restart; Ice.IntHolder ret = new Ice.IntHolder(); do { // // A copy of the initialization data and the string array // needs to be passed to doMainInternal, as these can be // changed by the application. // Ice.InitializationData id = (Ice.InitializationData)initData.clone(); id.properties = id.properties._clone(); Ice.StringSeqHolder h = new Ice.StringSeqHolder(); h.value = argHolder.value.clone(); restart = doMain(h, id, ret); } while(restart); return ret.value; } private boolean doMain(Ice.StringSeqHolder argHolder, Ice.InitializationData initData, Ice.IntHolder status) { // // Reset internal state variables from Ice.Application. The // remainder are reset at the end of this method. // _callbackInProgress = false; _destroyed = false; _interrupted = false; boolean restart = false; status.value = 0; SessionPingThread ping = null; try { _communicator = Ice.Util.initialize(argHolder, initData); _router = Glacier2.RouterPrxHelper.uncheckedCast(communicator().getDefaultRouter()); if(_router == null) { Ice.Util.getProcessLogger().error("no glacier2 router configured"); status.value = 1; } else { // // The default is to destroy when a signal is received. // if(_signalPolicy == Ice.SignalPolicy.HandleSignals) { destroyOnInterrupt(); } // // If createSession throws, we're done. // try { _session = createSession(); _createdSession = true; } catch(Ice.LocalException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); status.value = 1; } if(_createdSession) { ping = new SessionPingThread(_router, (_router.getSessionTimeout() * 1000) / 2); ping.start(); _category = _router.getCategoryForClient(); status.value = runWithSession(argHolder.value); } } } // // We want to restart on those exceptions that indicate a // break down in communications, but not those exceptions that // indicate a programming logic error (i.e., marshal, protocol // failure, etc). // catch(RestartSessionException ex) { restart = true; } catch(Ice.ConnectionRefusedException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); restart = true; } catch(Ice.ConnectionLostException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); restart = true; } catch(Ice.UnknownLocalException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); restart = true; } catch(Ice.RequestFailedException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); restart = true; } catch(Ice.TimeoutException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); restart = true; } catch(Ice.LocalException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); status.value = 1; } catch(java.lang.Exception ex) { Ice.Util.getProcessLogger().error("unknown exception:\n" + IceInternal.Ex.toString(ex)); status.value = 1; } catch(java.lang.Error err) { // // We catch Error to avoid hangs in some non-fatal situations // Ice.Util.getProcessLogger().error("Java error:\n" + IceInternal.Ex.toString(err)); status.value = 1; } // // This clears any set interrupt. // if(_signalPolicy == Ice.SignalPolicy.HandleSignals) { defaultInterrupt(); } synchronized(_mutex) { while(_callbackInProgress) { try { _mutex.wait(); } catch(java.lang.InterruptedException ex) { } } if(_destroyed) { _communicator = null; } else { _destroyed = true; // // And _communicator != null, meaning will be // destroyed next, _destroyed = true also ensures that // any remaining callback won't do anything. // } } if(ping != null) { ping.done(); while(true) { try { ping.join(); break; } catch(InterruptedException ex) { } } ping = null; } if(_createdSession && _router != null) { try { _router.destroySession(); } catch(Ice.ConnectionLostException ex) { // // Expected if another thread invoked on an object from the session concurrently. // } catch(Glacier2.SessionNotExistException ex) { // // This can also occur. // } catch(Throwable ex) { // // Not expected. // Ice.Util.getProcessLogger().error("unexpected exception when destroying the session:\n" + IceInternal.Ex.toString(ex)); } _router = null; } if(_communicator != null) { try { _communicator.destroy(); } catch(Ice.LocalException ex) { Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); status.value = 1; } catch(java.lang.Exception ex) { Ice.Util.getProcessLogger().error("unknown exception:\n" + IceInternal.Ex.toString(ex)); status.value = 1; } _communicator = null; } synchronized(_mutex) { if(_appHook != null) { _appHook.done(); } } // // Reset internal state. We cannot reset the Application state // here, since _destroyed must remain true until we re-run // this method. // _adapter = null; _router = null; _session = null; _createdSession = false; _category = null; return restart; } private static Ice.ObjectAdapter _adapter; private static Glacier2.RouterPrx _router; private static Glacier2.SessionPrx _session; private static boolean _createdSession = false; private static String _category; }