// // Copyright (c) ZeroC, Inc. All rights reserved. // using System; using System.Diagnostics; namespace Glacier2 { /// /// Utility base class that makes it easy to correctly initialize and finalize /// the Ice run time, as well as handle signals. Unless the application specifies /// a logger, Application installs a per-process logger that logs to the standard /// error output. /// /// Applications must create a derived class that implements the {@link #run} method. /// /// A program can contain only one instance of this class. /// public abstract class Application : Ice.Application { /// /// This exception is raised if the session should be restarted. /// public class RestartSessionException : Exception { } /// /// Initializes an instance that handles signals according to the signal policy. /// If not signal policy is provided the default SinalPolicy.HandleSignals /// will be used, which calls Communicator.shutdown if a signal is received. /// /// Determines how to respond to signals. public Application(Ice.SignalPolicy signalPolicy = Ice.SignalPolicy.HandleSignals) : base(signalPolicy) { } /// /// Creates a new Glacier2 session. A call to createSession always /// precedes a call to runWithSession. If Ice.LocalException is thrown /// from this method, the application is terminated. /// /// The Glacier2 session. public abstract Glacier2.SessionPrx createSession(); /// /// Called once the communicator has been initialized and the Glacier2 session /// has been established. A derived class must implement runWithSession, /// which is the application's starting method. /// /// The argument vector for the application. Application /// scans the argument vector passed to main for options that are /// specific to the Ice run time and removes them; therefore, the vector passed /// to runWithSession is free from Ice-related options and contains /// only options and arguments that are application-specific. /// /// The runWithSession method should return zero for successful /// termination, and non-zero otherwise. Application.main returns the /// value returned by runWithSession. /// public abstract int runWithSession(string[] args); /// /// Run should not be overridden for Glacier2.Application. Instead /// runWithSession should be used. /// public override int run(string[] args) { // // This shouldn't be called. // Debug.Assert(false); return 0; } /// /// Called when the session refresh thread 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. This method is called /// according to the Ice invocation dipsatch rules (in other words, it /// uses the same rules as an servant upcall or AMI callback). /// public virtual void sessionDestroyed() { } /// /// Called to restart the application's Glacier2 session. This /// method never returns. The exception produce an application restart /// when called from the Application main thread. /// /// throws RestartSessionException This exception is /// always thrown. /// public static void restart() { throw new RestartSessionException(); } /// /// Returns the Glacier2 router proxy. /// /// The router proxy. public static Glacier2.RouterPrx router() { return _router; } /// /// Returns the Glacier2 session proxy. /// /// The session proxy. public static 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. /// Throws SessionNotExistException if no session exists. /// /// The category. public static string categoryForClient() { if(_router == null) { throw new SessionNotExistException(); } return _category; } /// /// Create a new Ice identity for callback objects with the given /// identity name field. /// /// The identity. public static Ice.Identity createCallbackIdentity(string name) { return new Ice.Identity(name, categoryForClient()); } /// /// Adds a servant to the callback object adapter's Active Servant Map with a UUID. /// /// The servant to add. /// The proxy for the servant. public static Ice.ObjectPrx addWithUUID(Ice.Object servant) { return objectAdapter().add(servant, createCallbackIdentity(Guid.NewGuid().ToString())); } /// /// Returns an object adapter for callback objects, creating it if necessary. /// /// The object adapter. public static Ice.ObjectAdapter objectAdapter() { if(_router == null) { throw new SessionNotExistException(); } lock(iceMutex) { if(_adapter == null) { _adapter = communicator().createObjectAdapterWithRouter("", _router); _adapter.activate(); } } return _adapter; } protected override int doMain(string[] originArgs, Ice.InitializationData initData) { // // Set the default properties for all Glacier2 applications. // initData.properties.setProperty("Ice.RetryIntervals", "-1"); bool restart; int ret = 0; 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.ice_clone_(); string[] args = (string[]) originArgs.Clone(); restart = doMain(args, id, out ret); } while(restart); return ret; } private bool doMain(string[] args, Ice.InitializationData initData, out int status) { // // Reset internal state variables from Ice.Application. The // remainder are reset at the end of this method. // iceCallbackInProgress = false; iceDestroyed = false; iceInterrupted = false; bool restart = false; bool sessionCreated = false; status = 0; try { iceCommunicator = Ice.Util.initialize(ref args, initData); _router = RouterPrxHelper.uncheckedCast(communicator().getDefaultRouter()); if(_router == null) { Ice.Util.getProcessLogger().error(iceAppName + ": no Glacier2 router configured"); status = 1; } else { // // The default is to destroy when a signal is received. // if(iceSignalPolicy == Ice.SignalPolicy.HandleSignals) { destroyOnInterrupt(); } // // If createSession throws, we're done. // try { _session = createSession(); sessionCreated = true; } catch(Ice.LocalException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); status = 1; } if(sessionCreated) { int acmTimeout = 0; try { acmTimeout = _router.getACMTimeout(); } catch(Ice.OperationNotExistException) { } if(acmTimeout <= 0) { acmTimeout = (int)_router.getSessionTimeout(); } if(acmTimeout > 0) { Ice.Connection connection = _router.ice_getCachedConnection(); Debug.Assert(connection != null); connection.setACM(acmTimeout, Ice.Util.None, Ice.ACMHeartbeat.HeartbeatAlways); connection.setCloseCallback(_ => sessionDestroyed()); } _category = _router.getCategoryForClient(); status = runWithSession(args); } } } // // 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) { restart = true; } catch(Ice.ConnectionRefusedException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); restart = true; } catch(Ice.ConnectionLostException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); restart = true; } catch(Ice.UnknownLocalException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); restart = true; } catch(Ice.RequestFailedException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); restart = true; } catch(Ice.TimeoutException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); restart = true; } catch(Ice.LocalException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); status = 1; } catch(Exception ex) { Ice.Util.getProcessLogger().error("unknown exception:\n" + ex.ToString()); status = 1; } // // Don't want any new interrupt. And at this point // (post-run), it would not make sense to release a held // signal to run shutdown or destroy. // if(iceSignalPolicy == Ice.SignalPolicy.HandleSignals) { ignoreInterrupt(); } lock(iceMutex) { while(iceCallbackInProgress) { System.Threading.Monitor.Wait(iceMutex); } if(iceDestroyed) { iceCommunicator = null; } else { iceDestroyed = true; // // And iceCommunicator != null, meaning will be // destroyed next, iceDestroyed = true also ensures that // any remaining callback won't do anything // } } if(sessionCreated && _router != null) { try { _router.destroySession(); } catch(Ice.ConnectionLostException) { // // Expected if another thread invoked on an object from the session concurrently. // } catch(SessionNotExistException) { // // This can also occur. // } catch(Exception ex) { // // Not expected. // Ice.Util.getProcessLogger().error("unexpected exception when destroying the session:\n" + ex.ToString()); } _router = null; } if(iceCommunicator != null) { try { iceCommunicator.destroy(); } catch(Ice.LocalException ex) { Ice.Util.getProcessLogger().error(ex.ToString()); status = 1; } catch(Exception ex) { Ice.Util.getProcessLogger().error("unknown exception:\n" + ex.ToString()); status = 1; } iceCommunicator = null; } // // Reset internal state. We cannot reset the Application state // here, since iceDestroyed must remain true until we re-run // this method. // _adapter = null; _router = null; _session = null; _category = null; return restart; } private static Ice.ObjectAdapter _adapter; private static RouterPrx _router; private static SessionPrx _session; private static string _category; } }