All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zeroc.Ice.Application Maven / Gradle / Ivy

//
// Copyright (c) ZeroC, Inc. All rights reserved.
//

package com.zeroc.Ice;

/**
 * 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. * * @see #run * @see Communicator * @see Logger **/ public abstract class Application { /** * Initializes an instance that calls {@link Communicator#destroy} 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(SignalPolicy signalPolicy) { _signalPolicy = signalPolicy; } /** * The application must call main after it has * instantiated the derived class. main creates * a communicator, establishes the specified signal policy, and, * once run returns, destroys the communicator. *

* The method prints an error message for any exception that propagates * out of run and ensures that the communicator is * destroyed correctly even if run completes abnormally. * * @param appName The name of the application. This parameter is used to initialize * the value of the Ice.ProgramName property. * @param args The arguments for the application (as passed to Main(String[]) * by the operating system. * @return The value returned by run. If run terminates with an exception, * the return value is non-zero. **/ public final int main(String appName, String[] args) { return main(appName, args, new InitializationData()); } /** * The application must call main after it has * instantiated the derived class. main creates * a communicator, establishes the specified signal policy, and, * once run returns, destroys the communicator. *

* The method prints an error message for any exception that propagates * out of run and ensures that the communicator is * destroyed correctly even if run completes abnormally. * * @param appName The name of the application. This parameter is used to initialize * the value of the Ice.ProgramName property. * @param args Command-line arguments * @param configFile The configuration file with which to initialize * Ice properties. * @return The value returned by run. If run terminates with an exception, * the return value is non-zero. **/ public final int main(String appName, String[] args, String configFile) { if(Util.getProcessLogger() instanceof LoggerI) { Util.setProcessLogger(new LoggerI(appName, "")); } InitializationData initData = new InitializationData(); if(configFile != null) { try { initData.properties = Util.createProperties(); initData.properties.load(configFile); } catch(LocalException ex) { Util.getProcessLogger().error(com.zeroc.IceInternal.Ex.toString(ex)); return 1; } catch(java.lang.Exception ex) { Util.getProcessLogger().error("unknown exception: " + com.zeroc.IceInternal.Ex.toString(ex)); return 1; } } return main(appName, args, initData); } /** * The application must call main after it has * instantiated the derived class. main creates * a communicator, establishes the specified signal policy, and, * once run returns, destroys the communicator. *

* The method prints an error message for any exception that propagates * out of run and ensures that the communicator is * destroyed correctly even if run completes abnormally. * * @param appName The name of the application. This parameter is used to initialize * the value of the Ice.ProgramName property. * @param args The arguments for the application (as passed to Main(String[]). * @param initializationData Additional data used to initialize the communicator. * @return The value returned by run. If run terminates with an exception, * the return value is non-zero. * * @see InitializationData **/ public final int main(String appName, String[] args, InitializationData initializationData) { if(Util.getProcessLogger() instanceof LoggerI) { Util.setProcessLogger(new LoggerI(appName, "")); } if(_communicator != null) { Util.getProcessLogger().error("only one instance of the Application class can be used"); return 1; } _appName = appName; // // We parse the properties here to extract Ice.ProgramName. // InitializationData initData; if(initializationData != null) { initData = initializationData.clone(); } else { initData = new InitializationData(); } java.util.List remainingArgs = new java.util.ArrayList<>(); try { initData.properties = Util.createProperties(args, initData.properties, remainingArgs); } catch(LocalException ex) { Util.getProcessLogger().error(com.zeroc.IceInternal.Ex.toString(ex)); return 1; } catch(java.lang.Exception ex) { Util.getProcessLogger().error("unknown exception: " + com.zeroc.IceInternal.Ex.toString(ex)); return 1; } _appName = initData.properties.getPropertyWithDefault("Ice.ProgramName", _appName); // // If the process logger is the default logger, we replace it with a // a logger which is using the program name for the prefix. // if(!initData.properties.getProperty("Ice.ProgramName").equals("") && Util.getProcessLogger() instanceof LoggerI) { Util.setProcessLogger(new LoggerI(initData.properties.getProperty("Ice.ProgramName"), "")); } return doMain(remainingArgs.toArray(new String[remainingArgs.size()]), initData); } protected int doMain(String[] args, InitializationData initData) { int status = 0; try { java.util.List remainingArgs = new java.util.ArrayList<>(); _communicator = Util.initialize(args, initData, remainingArgs); // // The default is to destroy when a signal is received. // if(_signalPolicy == SignalPolicy.HandleSignals) { destroyOnInterrupt(); } status = run(remainingArgs.toArray(new String[remainingArgs.size()])); } catch(LocalException ex) { Util.getProcessLogger().error(com.zeroc.IceInternal.Ex.toString(ex)); status = 1; } catch(java.lang.Exception ex) { Util.getProcessLogger().error("unknown exception: " + com.zeroc.IceInternal.Ex.toString(ex)); status = 1; } catch(java.lang.Error err) { // // We catch Error to avoid hangs in some non-fatal situations // Util.getProcessLogger().error("Java error: " + com.zeroc.IceInternal.Ex.toString(err)); status = 1; } // This clears any set interrupt. if(_signalPolicy == SignalPolicy.HandleSignals) { defaultInterrupt(); } synchronized(_mutex) { boolean interrupted = false; while(_callbackInProgress) { try { _mutex.wait(); } catch(InterruptedException ex) { interrupted = true; } } if(interrupted) { Thread.currentThread().interrupt(); } 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(_communicator != null) { try { try { _communicator.destroy(); } catch(OperationInterruptedException ex) { Util.getProcessLogger().error(com.zeroc.IceInternal.Ex.toString(ex)); // Retry communicator destroy in case of an operation // interrupt exception, but don't do so in a loop // otherwise it could go on forever. _communicator.destroy(); } } catch(LocalException ex) { Util.getProcessLogger().error(com.zeroc.IceInternal.Ex.toString(ex)); status = 1; } catch(java.lang.Exception ex) { Util.getProcessLogger().error("unknown exception: " + com.zeroc.IceInternal.Ex.toString(ex)); status = 1; } _communicator = null; } synchronized(_mutex) { if(_appHook != null) { _appHook.done(); } } return status; } /** * Called once the communicator has been initialized. The derived class must * implement run, which is the application's starting method. * * @param args 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 run is free from Ice-related options and contains only options * and arguments that are application-specific. * * @return The run method should return zero for successful termination, and * non-zero otherwise. Application.main returns the value returned by run. **/ public abstract int run(String[] args); /** * Returns the value of appName that is passed to main (which is also the * the value of Ice.ProgramName). This method is useful mainly for error messages that * include the application name. Because appName is a static method, it is available from anywhere * in the program. * * @return The name of the application. **/ public static String appName() { return _appName; } /** * Returns the communicator for the application. Because communicator is a static method, * it permits access to the communicator from anywhere in the program. Note that, as a consequence, * you cannot have more than one instance of Application in a program. * * @return The communicator for the application. **/ public static Communicator communicator() { return _communicator; } /** * Instructs Application to call {@link Communicator#destroy} on receipt of a signal. * This is default signal handling policy established by the default constructor. * * @see Communicator#destroy **/ public static void destroyOnInterrupt() { if(_signalPolicy == SignalPolicy.HandleSignals) { synchronized(_mutex) { // // As soon as the destroy hook ends all the threads are // terminated. So the destroy hook will join the current // thread before to end. // try { changeHook(new DestroyHook()); } catch(java.lang.IllegalStateException ex) { if(_communicator != null) { _communicator.destroy(); } } } } else { Util.getProcessLogger().warning( "interrupt method called on Application configured to not handle interrupts."); } } /** * Instructs Application to call {@link Communicator#shutdown} on receipt of a signal. * * @see Communicator#shutdown **/ public static void shutdownOnInterrupt() { if(_signalPolicy == SignalPolicy.HandleSignals) { synchronized(_mutex) { // // As soon as the shutdown hook ends all the threads are // terminated. So the shutdown hook will join the current // thread before to end. // try { changeHook(new ShutdownHook()); } catch(java.lang.IllegalStateException ex) { if(_communicator != null) { _communicator.shutdown(); } } } } else { Util.getProcessLogger().warning( "interrupt method called on Application configured to not handle interrupts."); } } /** * Installs a custom shutdown hook. The implementation of the shutdown * hook can do whatever cleanup is necessary to shut down the application. * The hook is unregistered once run returns. * Note that the hook must obey the rules for shutdown hooks; specifically, * it must not call exit. * * @param newHook The Runnable to run on shutdown. * * @see java.lang.Runtime#addShutdownHook **/ public static void setInterruptHook(Runnable newHook) { if(_signalPolicy == SignalPolicy.HandleSignals) { try { changeHook(new CustomHook(newHook)); } catch(java.lang.IllegalStateException ex) { // Ignore. } } else { Util.getProcessLogger().warning( "interrupt method called on Application configured to not handle interrupts."); } } /** * Clears any shutdown hooks, including any hook established with {@link #destroyOnInterrupt} or * {@link #shutdownOnInterrupt}. **/ public static void defaultInterrupt() { if(_signalPolicy == SignalPolicy.HandleSignals) { changeHook(null); } else { Util.getProcessLogger().warning( "interrupt method called on Application configured to not handle interrupts."); } } /** * Determines whether the application shut down intentionally or was forced to shut down by the JVM. This * is useful for logging purposes. * * @return true if a shutdown hook caused the communicator to shut down; false otherwise. **/ public static boolean interrupted() { synchronized(_mutex) { return _interrupted; } } private static void changeHook(AppHook newHook) { synchronized(_mutex) { // // Remove any existing shutdown hooks. // try { if(_appHook != null) { Runtime.getRuntime().removeShutdownHook(_appHook); _appHook.done(); _appHook = null; } } catch(java.lang.IllegalStateException ex) { // // Expected if we are in the process of shutting down. // } // // Note that we let the IllegalStateException propagate // out if necessary. // if(newHook != null) { Runtime.getRuntime().addShutdownHook(newHook); _appHook = newHook; } } } private static boolean setCallbackInProgress(boolean destroy) { synchronized(_mutex) { if(_destroyed) { // // Being destroyed by main thread // return false; } _callbackInProgress = true; _destroyed = destroy; _interrupted = true; return true; } } private static void clearCallbackInProgress() { synchronized(_mutex) { _callbackInProgress = false; _mutex.notify(); } } // For use by Glacier2.Application /** @hidden */ static public class AppHook extends Thread { public void done() { synchronized(_doneMutex) { _done = true; _doneMutex.notify(); } } protected boolean _done = false; protected final java.lang.Object _doneMutex = new java.lang.Object(); } static class DestroyHook extends AppHook { @Override public void run() { synchronized(_doneMutex) { if(!setCallbackInProgress(true)) { return; } Communicator communicator = communicator(); if(communicator != null) { communicator.destroy(); } clearCallbackInProgress(); while(!_done) { try { _doneMutex.wait(); } catch(InterruptedException ex) { break; } } } } } static class ShutdownHook extends AppHook { @Override public void run() { synchronized(_doneMutex) { if(!setCallbackInProgress(false)) { return; } Communicator communicator = communicator(); if(communicator != null) { communicator.shutdown(); } clearCallbackInProgress(); while(!_done) { try { _doneMutex.wait(); } catch(InterruptedException ex) { break; } } } } } // Although this class doesn't actually use any of the AppHook // done stuff, its more trouble than its worth to add all of the // code necessary to support another hook member variable and // support code. static class CustomHook extends AppHook { CustomHook(Runnable hook) { _hook = hook; } @Override public void run() { synchronized(_doneMutex) { if(!setCallbackInProgress(false)) { return; } _hook.run(); clearCallbackInProgress(); // // Don't bother to join with main, we're done. // } } private Runnable _hook; } /** @hidden */ protected static String _appName; /** @hidden */ protected static Communicator _communicator; /** @hidden */ protected static AppHook _appHook; /** @hidden */ protected final static java.lang.Object _mutex = new java.lang.Object(); /** @hidden */ protected static boolean _callbackInProgress = false; /** @hidden */ protected static boolean _destroyed = false; /** @hidden */ protected static boolean _interrupted = false; /** @hidden */ protected static SignalPolicy _signalPolicy = SignalPolicy.HandleSignals; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy