diff options
Diffstat (limited to 'py/python/Ice.py')
-rw-r--r-- | py/python/Ice.py | 1355 |
1 files changed, 1355 insertions, 0 deletions
diff --git a/py/python/Ice.py b/py/python/Ice.py new file mode 100644 index 00000000000..2bfb3123004 --- /dev/null +++ b/py/python/Ice.py @@ -0,0 +1,1355 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2011 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. +# +# ********************************************************************** + +""" +Ice module +""" + +import sys, exceptions, string, imp, os, threading, warnings, datetime + +# +# RTTI problems can occur in C++ code unless we modify Python's dlopen flags. +# Note that changing these flags might cause problems for other extensions +# loaded by the application (see bug 3660), so we restore the original settings +# after loading IcePy. +# +_dlopenflags = -1 +try: + _dlopenflags = sys.getdlopenflags() + + try: + import dl + sys.setdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL) + except ImportError: + # + # If the dl module is not available and we're running on a Linux + # platform, use the hard coded value of RTLD_NOW|RTLD_GLOBAL. + # + if sys.platform.startswith("linux"): + sys.setdlopenflags(258) + pass + +except AttributeError: + # + # sys.getdlopenflags() is not supported (we're probably running on Windows). + # + pass + +# +# Import the Python extension. +# +import IcePy + +# +# Restore the dlopen flags. +# +if _dlopenflags >= 0: + sys.setdlopenflags(_dlopenflags) + +# +# Add some symbols to the Ice module. +# +ObjectPrx = IcePy.ObjectPrx +stringVersion = IcePy.stringVersion +intVersion = IcePy.intVersion +generateUUID = IcePy.generateUUID +loadSlice = IcePy.loadSlice +AsyncResult = IcePy.AsyncResult + +# +# This value is used as the default value for struct types in the constructors +# of user-defined types. It allows us to determine whether the application has +# supplied a value. (See bug 3676) +# +_struct_marker = object() + +# +# Core Ice types. +# +class Object(object): + def ice_isA(self, id, current=None): + '''Determines whether the target object supports the interface denoted +by the given Slice type id. +Arguments: + id The Slice type id +Returns: + True if the target object supports the interface, or false otherwise. +''' + return id in self.ice_ids(current) + + def ice_ping(self, current=None): + '''A reachability test for the target object.''' + pass + + def ice_ids(self, current=None): + '''Obtains the type ids corresponding to the Slice interface +that are supported by the target object. +Returns: + A list of type ids. +''' + return [ self.ice_id(current) ] + + def ice_id(self, current=None): + '''Obtains the type id corresponding to the most-derived Slice +interface supported by the target object. +Returns: + The type id. +''' + return '::Ice::Object' + + def ice_staticId(): + '''Obtains the type id of this Slice class or interface. +Returns: + The type id. +''' + return '::Ice::Object' + ice_staticId = staticmethod(ice_staticId) + + # + # Do not define these here. They will be invoked if defined by a subclass. + # + #def ice_preMarshal(self): + # pass + # + #def ice_postUnmarshal(self): + # pass + +# +# LocalObject is deprecated; use the Python base 'object' type instead. +# +class LocalObject(object): + pass + +class Blobject(Object): + '''Special-purpose servant base class that allows a subclass to +handle synchronous Ice invocations as "blobs" of bytes.''' + + def ice_invoke(self, bytes, current): + '''Dispatch a synchronous Ice invocation. The operation's +arguments are encoded in the bytes argument. The return +value must be a tuple of two values: the first is a +boolean indicating whether the operation succeeded (True) +or raised a user exception (False), and the second is +the encoded form of the operation's results or the user +exception. +''' + pass + +class BlobjectAsync(Object): + '''Special-purpose servant base class that allows a subclass to +handle asynchronous Ice invocations as "blobs" of bytes.''' + + def ice_invoke_async(self, cb, bytes, current): + '''Dispatch an asynchronous Ice invocation. The operation's +arguments are encoded in the bytes argument. When the +dispatch is complete, the subclass can invoke either +ice_response or ice_exception on the supplied callback +object. +''' + pass + +# +# Exceptions. +# +class Exception(exceptions.Exception): + '''The base class for all Ice exceptions.''' + def __str__(self): + return self.__class__.__name__ + + def ice_name(self): + '''Returns the type name of this exception.''' + return self._ice_name + +class LocalException(Exception): + '''The base class for all Ice run-time exceptions.''' + def __init__(self, args=''): + self.args = args + +class UserException(Exception): + '''The base class for all user-defined exceptions.''' + pass + +def getSliceDir(): + '''Convenience function for locating the directory containing the Slice files.''' + + # + # Get the parent of the directory containing this file (Ice.py). + # + pyHome = os.path.join(os.path.dirname(__file__), "..") + + # + # For an installation from a source distribution, a binary tarball, or a + # Windows installer, the "slice" directory is a sibling of the "python" + # directory. + # + dir = os.path.join(pyHome, "slice") + if os.path.exists(dir): + return os.path.normpath(dir) + + # + # In a source distribution, the "slice" directory is one level higher. + # + dir = os.path.join(pyHome, "..", "slice") + if os.path.exists(dir): + return os.path.normpath(dir) + + iceVer = stringVersion() + + if sys.platform[:5] == "linux": + # + # Check the default RPM location. + # + dir = os.path.join("/", "usr", "share", "Ice-" + iceVer, "slice") + if os.path.exists(dir): + return dir + + return None + +# +# Utilities for use by generated code. +# + +_pendingModules = {} + +def openModule(name): + if sys.modules.has_key(name): + result = sys.modules[name] + elif _pendingModules.has_key(name): + result = _pendingModules[name] + else: + result = createModule(name) + + return result + +def createModule(name): + l = string.split(name, ".") + curr = '' + mod = None + + for s in l: + curr = curr + s + + if sys.modules.has_key(curr): + mod = sys.modules[curr] + elif _pendingModules.has_key(curr): + mod = _pendingModules[curr] + else: + nmod = imp.new_module(curr) + _pendingModules[curr] = nmod + mod = nmod + + curr = curr + "." + + return mod + +def updateModule(name): + if _pendingModules.has_key(name): + pendingModule = _pendingModules[name] + mod = sys.modules[name] + mod.__dict__.update(pendingModule.__dict__) + del _pendingModules[name] + +def updateModules(): + for name in _pendingModules.keys(): + if sys.modules.has_key(name): + sys.modules[name].__dict__.update(_pendingModules[name].__dict__) + else: + sys.modules[name] = _pendingModules[name] + del _pendingModules[name] + +def createTempClass(): + class __temp: pass + return __temp + +# +# Forward declarations. +# +IcePy._t_Object = IcePy.declareClass('::Ice::Object') +IcePy._t_ObjectPrx = IcePy.declareProxy('::Ice::Object') +IcePy._t_LocalObject = IcePy.declareClass('::Ice::LocalObject') + +# +# Sequence mappings. +# +IcePy.SEQ_DEFAULT = 0 +IcePy.SEQ_TUPLE = 1 +IcePy.SEQ_LIST = 2 +#IcePy.SEQ_ARRAY = 3 + +# +# Slice checksum dictionary. +# +sliceChecksums = {} + +# +# Import generated Ice modules. +# +import Ice_BuiltinSequences_ice +import Ice_Communicator_ice +import Ice_Current_ice +import Ice_ImplicitContext_ice +import Ice_Endpoint_ice +import Ice_EndpointTypes_ice +import Ice_Identity_ice +import Ice_LocalException_ice +import Ice_Locator_ice +import Ice_Logger_ice +import Ice_ObjectAdapter_ice +import Ice_ObjectFactory_ice +import Ice_Properties_ice +import Ice_Router_ice +import Ice_ServantLocator_ice +import Ice_Connection_ice + +# +# Replace EndpointInfo with our implementation. +# +del EndpointInfo +EndpointInfo = IcePy.EndpointInfo +del IPEndpointInfo +IPEndpointInfo = IcePy.IPEndpointInfo +del TCPEndpointInfo +TCPEndpointInfo = IcePy.TCPEndpointInfo +del UDPEndpointInfo +UDPEndpointInfo = IcePy.UDPEndpointInfo +del OpaqueEndpointInfo +OpaqueEndpointInfo = IcePy.OpaqueEndpointInfo + +# +# Replace ConnectionInfo with our implementation. +# +del ConnectionInfo +ConnectionInfo = IcePy.ConnectionInfo +del IPConnectionInfo +IPConnectionInfo = IcePy.IPConnectionInfo +del TCPConnectionInfo +TCPConnectionInfo = IcePy.TCPConnectionInfo +del UDPConnectionInfo +UDPConnectionInfo = IcePy.UDPConnectionInfo + +class ThreadNotification(object): + '''Base class for thread notification callbacks. A subclass must +define the start and stop methods.''' + + def __init__(self): + pass + + def start(): + '''Invoked in the context of a thread created by the Ice run time.''' + pass + + def stop(): + '''Invoked in the context of an Ice run-time thread that is about +to terminate.''' + pass + +# +# Initialization data. +# +class InitializationData(object): + '''The attributes of this class are used to initialize a new +communicator instance. The supported attributes are as follows: + +properties: An instance of Ice.Properties. You can use the + Ice.createProperties function to create a new property set. + +logger: An instance of Ice.Logger. + +threadHook: An object that implements ThreadNotification. +''' + def __init__(self): + self.properties = None + self.logger = None + #self.stats = None # Stats not currently supported in Python. + self.threadHook = None + +# +# Communicator wrapper. +# +class CommunicatorI(Communicator): + def __init__(self, impl): + self._impl = impl + impl._setWrapper(self) + + def destroy(self): + self._impl.destroy() + + def shutdown(self): + self._impl.shutdown() + + def waitForShutdown(self): + # + # If invoked by the main thread, waitForShutdown only blocks for + # the specified timeout in order to give us a chance to handle + # signals. + # + while not self._impl.waitForShutdown(500): + pass + + def isShutdown(self): + return self._impl.isShutdown() + + def stringToProxy(self, str): + return self._impl.stringToProxy(str) + + def proxyToString(self, obj): + return self._impl.proxyToString(obj) + + def propertyToProxy(self, str): + return self._impl.propertyToProxy(str) + + def proxyToProperty(self, obj, str): + return self._impl.proxyToProperty(obj, str) + + def stringToIdentity(self, str): + return self._impl.stringToIdentity(str) + + def identityToString(self, ident): + return self._impl.identityToString(ident) + + def createObjectAdapter(self, name): + adapter = self._impl.createObjectAdapter(name) + return ObjectAdapterI(adapter) + + def createObjectAdapterWithEndpoints(self, name, endpoints): + adapter = self._impl.createObjectAdapterWithEndpoints(name, endpoints) + return ObjectAdapterI(adapter) + + def createObjectAdapterWithRouter(self, name, router): + adapter = self._impl.createObjectAdapterWithRouter(name, router) + return ObjectAdapterI(adapter) + + def addObjectFactory(self, factory, id): + self._impl.addObjectFactory(factory, id) + + def findObjectFactory(self, id): + return self._impl.findObjectFactory(id) + + def getImplicitContext(self): + context = self._impl.getImplicitContext() + if context == None: + return None; + else: + return ImplicitContextI(context) + + def getProperties(self): + properties = self._impl.getProperties() + return PropertiesI(properties) + + def getLogger(self): + logger = self._impl.getLogger() + if isinstance(logger, Logger): + return logger + else: + return LoggerI(logger) + + def getStats(self): + raise RuntimeError("operation `getStats' not implemented") + + def getDefaultRouter(self): + return self._impl.getDefaultRouter() + + def setDefaultRouter(self, rtr): + self._impl.setDefaultRouter(rtr) + + def getDefaultLocator(self): + return self._impl.getDefaultLocator() + + def setDefaultLocator(self, loc): + self._impl.setDefaultLocator(loc) + + def getPluginManager(self): + raise RuntimeError("operation `getPluginManager' not implemented") + + def flushBatchRequests(self): + self._impl.flushBatchRequests() + + def begin_flushBatchRequests(self, _ex=None, _sent=None): + return self._impl.begin_flushBatchRequests(_ex, _sent) + + def end_flushBatchRequests(self, r): + return self._impl.end_flushBatchRequests(r) + + def getAdmin(self): + return self._impl.getAdmin() + + def addAdminFacet(self, servant, facet): + self._impl.addAdminFacet(servant, facet) + + def removeAdminFacet(self, facet): + return self._impl.removeAdminFacet(facet) + +# +# Ice.initialize() +# +def initialize(args=None, data=None): + '''Initializes a new communicator. The optional arguments represent +an argument list (such as sys.argv) and an instance of InitializationData. +You can invoke this function as follows: + +Ice.initialize() +Ice.initialize(args) +Ice.initialize(data) +Ice.initialize(args, data) + +If you supply an argument list, the function removes those arguments from +the list that were recognized by the Ice run time. +''' + communicator = IcePy.Communicator(args, data) + return CommunicatorI(communicator) + +# +# ObjectAdapter wrapper. +# +class ObjectAdapterI(ObjectAdapter): + def __init__(self, impl): + self._impl = impl + + def getName(self): + return self._impl.getName() + + def getCommunicator(self): + communicator = self._impl.getCommunicator() + return communicator._getWrapper() + + def activate(self): + self._impl.activate() + + def hold(self): + self._impl.hold() + + def waitForHold(self): + # + # If invoked by the main thread, waitForHold only blocks for + # the specified timeout in order to give us a chance to handle + # signals. + # + while not self._impl.waitForHold(1000): + pass + + def deactivate(self): + self._impl.deactivate() + + def waitForDeactivate(self): + # + # If invoked by the main thread, waitForDeactivate only blocks for + # the specified timeout in order to give us a chance to handle + # signals. + # + while not self._impl.waitForDeactivate(1000): + pass + + def isDeactivated(self): + self._impl.isDeactivated() + + def destroy(self): + self._impl.destroy() + + def add(self, servant, id): + return self._impl.add(servant, id) + + def addFacet(self, servant, id, facet): + return self._impl.addFacet(servant, id, facet) + + def addWithUUID(self, servant): + return self._impl.addWithUUID(servant) + + def addFacetWithUUID(self, servant, facet): + return self._impl.addFacetWIthUUID(servant, facet) + + def addDefaultServant(self, servant, category): + self._impl.addDefaultServant(servant, category) + + def remove(self, id): + return self._impl.remove(id) + + def removeFacet(self, id, facet): + return self._impl.removeFacet(id, facet) + + def removeAllFacets(self, id): + return self._impl.removeAllFacets(id) + + def removeDefaultServant(self, category): + return self._impl.removeDefaultServant(category) + + def find(self, id): + return self._impl.find(id) + + def findFacet(self, id, facet): + return self._impl.findFacet(id, facet) + + def findAllFacets(self, id): + return self._impl.findAllFacets(id) + + def findByProxy(self, proxy): + return self._impl.findByProxy(proxy) + + def findDefaultServant(self, category): + return self._impl.findDefaultServant(category) + + def addServantLocator(self, locator, category): + self._impl.addServantLocator(locator, category) + + def removeServantLocator(self, category): + return self._impl.removeServantLocator(category) + + def findServantLocator(self, category): + return self._impl.findServantLocator(category) + + def createProxy(self, id): + return self._impl.createProxy(id) + + def createDirectProxy(self, id): + return self._impl.createDirectProxy(id) + + def createIndirectProxy(self, id): + return self._impl.createIndirectProxy(id) + + def createReverseProxy(self, id): + return self._impl.createReverseProxy(id) + + def setLocator(self, loc): + self._impl.setLocator(loc) + + def refreshPublishedEndpoints(self): + self._impl.refreshPublishedEndpoints() + + def getEndpoints(self): + return self._impl.getEndpoints() + + def getPublishedEndpoints(self): + return self._impl.getPublishedEndpoints() + +# +# Logger wrapper. +# +class LoggerI(Logger): + def __init__(self, impl): + self._impl = impl + + def _print(self, message): + return self._impl._print(message) + + def trace(self, category, message): + return self._impl.trace(category, message) + + def warning(self, message): + return self._impl.warning(message) + + def error(self, message): + return self._impl.error(message) + + def cloneWithPrefix(self, prefix): + logger = self._impl.cloneWithPrefix(prefix) + return LoggerI(logger) + +# +# Properties wrapper. +# +class PropertiesI(Properties): + def __init__(self, impl): + self._impl = impl + + def getProperty(self, key): + return self._impl.getProperty(key) + + def getPropertyWithDefault(self, key, value): + return self._impl.getPropertyWithDefault(key, value) + + def getPropertyAsInt(self, key): + return self._impl.getPropertyAsInt(key) + + def getPropertyAsIntWithDefault(self, key, value): + return self._impl.getPropertyAsIntWithDefault(key, value) + + def getPropertyAsList(self, key): + return self._impl.getPropertyAsList(key) + + def getPropertyAsListWithDefault(self, key, value): + return self._impl.getPropertyAsListWithDefault(key, value) + + def getPropertiesForPrefix(self, prefix): + return self._impl.getPropertiesForPrefix(prefix) + + def setProperty(self, key, value): + self._impl.setProperty(key, value) + + def getCommandLineOptions(self): + return self._impl.getCommandLineOptions() + + def parseCommandLineOptions(self, prefix, options): + return self._impl.parseCommandLineOptions(prefix, options) + + def parseIceCommandLineOptions(self, options): + return self._impl.parseIceCommandLineOptions(options) + + def load(self, file): + self._impl.load(file) + + def clone(self): + properties = self._impl.clone() + return PropertiesI(properties) + + def __iter__(self): + dict = self._impl.getPropertiesForPrefix('') + return iter(dict) + + def __str__(self): + return str(self._impl) + +# +# Ice.createProperties() +# +def createProperties(args=None, defaults=None): + '''Creates a new property set. The optional arguments represent +an argument list (such as sys.argv) and a property set that supplies +default values. You can invoke this function as follows: + +Ice.createProperties() +Ice.createProperties(args) +Ice.createProperties(defaults) +Ice.createProperties(args, defaults) + +If you supply an argument list, the function removes those arguments +from the list that were recognized by the Ice run time. +''' + + properties = IcePy.createProperties(args, defaults) + return PropertiesI(properties) + +# +# Ice.getProcessLogger() +# Ice.setProcessLogger() +# +def getProcessLogger(): + '''Returns the default logger object.''' + logger = IcePy.getProcessLogger() + if isinstance(logger, Logger): + return logger + else: + return LoggerI(logger) + +def setProcessLogger(logger): + '''Sets the default logger object.''' + IcePy.setProcessLogger(logger) + +# +# ImplicitContext wrapper +# +class ImplicitContextI(ImplicitContext): + def __init__(self, impl): + self._impl = impl + + def setContext(self, ctx): + self._impl.setContext(ctx) + + def getContext(self): + return self._impl.getContext() + + def containsKey(self, key): + return self._impl.containsKey(key) + + def get(self, key): + return self._impl.get(key) + + def put(self, key, value): + return self._impl.put(key, value) + + def remove(self, key): + return self._impl.remove(key) + + +# +# Its not possible to block in a python signal handler since this +# blocks the main thread from doing further work. As such we queue the +# signal with a worker thread which then "dispatches" the signal to +# the registered callback object. +# +# Note the interface is the same as the C++ CtrlCHandler +# implementation, however, the implementation is different. +# +class CtrlCHandler(threading.Thread): + # Class variable referring to the one and only handler for use + # from the signal handling callback. + _self = None + + def __init__(self): + threading.Thread.__init__(self) + + if CtrlCHandler._self != None: + raise RuntimeError("Only a single instance of a CtrlCHandler can be instantiated.") + CtrlCHandler._self = self + + # State variables. These are not class static variables. + self._condVar = threading.Condition() + self._queue = [] + self._done = False + self._callback = None + + # + # Setup and install signal handlers + # + if signal.__dict__.has_key('SIGHUP'): + signal.signal(signal.SIGHUP, CtrlCHandler.signalHandler) + if signal.__dict__.has_key('SIGBREAK'): + signal.signal(signal.SIGBREAK, CtrlCHandler.signalHandler) + signal.signal(signal.SIGINT, CtrlCHandler.signalHandler) + signal.signal(signal.SIGTERM, CtrlCHandler.signalHandler) + + # Start the thread once everything else is done. + self.start() + + # Dequeue and dispatch signals. + def run(self): + while True: + self._condVar.acquire() + while len(self._queue) == 0 and not self._done: + self._condVar.wait() + if self._done: + self._condVar.release() + break + sig, callback = self._queue.pop() + self._condVar.release() + if callback: + callback(sig) + + # Destroy the object. Wait for the thread to terminate and cleanup + # the internal state. + def destroy(self): + self._condVar.acquire() + self._done = True + self._condVar.notify() + self._condVar.release() + + # Wait for the thread to terminate + self.join() + # + # Cleanup any state set by the CtrlCHandler. + # + if signal.__dict__.has_key('SIGHUP'): + signal.signal(signal.SIGHUP, signal.SIG_DFL) + if signal.__dict__.has_key('SIGBREAK'): + signal.signal(signal.SIGBREAK, signal.SIG_DFL) + signal.signal(signal.SIGINT, signal.SIG_DFL) + signal.signal(signal.SIGTERM, signal.SIG_DFL) + CtrlCHandler._self = None + + def setCallback(self, callback): + self._condVar.acquire() + self._callback = callback + self._condVar.release() + + def getCallback(self): + self._condVar.acquire() + callback = self._callback + self._condVar.release() + return callback + + # Private. Only called by the signal handling mechanism. + def signalHandler(self, sig, frame): + self._self._condVar.acquire() + # + # The signal AND the current callback are queued together. + # + self._self._queue.append([sig, self._self._callback]) + self._self._condVar.notify() + self._self._condVar.release() + signalHandler = classmethod(signalHandler) + +# +# Application logger. +# +class _ApplicationLoggerI(Logger): + def __init__(self, prefix): + if len(prefix) > 0: + self._prefix = prefix + ": " + else: + self._prefix = "" + self._outputMutex = threading.Lock() + + def _print(self, message): + s = "[ " + str(datetime.datetime.now()) + " " + self._prefix + self._outputMutex.acquire() + sys.stderr.write(message + "\n") + self._outputMutex.release() + + def trace(self, category, message): + s = "[ " + str(datetime.datetime.now()) + " " + self._prefix + if len(category) > 0: + s += category + ": " + s += message + " ]" + + s = s.replace("\n", "\n ") + + self._outputMutex.acquire() + sys.stderr.write(s + "\n") + self._outputMutex.release() + + def warning(self, message): + self._outputMutex.acquire() + sys.stderr.write(str(datetime.datetime.now()) + " " + self._prefix + "warning: " + message + "\n") + self._outputMutex.release() + + def error(self, message): + self._outputMutex.acquire() + sys.stderr.write(str(datetime.datetime.now()) + " " + self._prefix + "error: " + message + "\n") + self._outputMutex.release() + +# +# Application class. +# +import signal, traceback +class Application(object): + '''Convenience class that initializes a communicator and reacts +gracefully to signals. An application must define a subclass +of this class and supply an implementation of the run method. +''' + + def __init__(self, signalPolicy=0): # HandleSignals=0 + '''The constructor accepts an optional argument indicating +whether to handle signals. The value should be either +Application.HandleSignals (the default) or +Application.NoSignalHandling. +''' + if type(self) == Application: + raise RuntimeError("Ice.Application is an abstract class") + Application._signalPolicy = signalPolicy + + def main(self, args, configFile=None, initData=None): + '''The main entry point for the Application class. The arguments +are an argument list (such as sys.argv), the name of an Ice +configuration file (optional), and an instance of +InitializationData (optional). This method does not return +until after the completion of the run method. The return +value is an integer representing the exit status. +''' + + if Application._communicator: + getProcessLogger().error(args[0] + ": only one instance of the Application class can be used") + return 1 + + # + # We parse the properties here to extract Ice.ProgramName. + # + if not initData: + initData = InitializationData() + if configFile: + try: + initData.properties = createProperties(None, initData.properties) + initData.properties.load(configFile) + except: + getProcessLogger().error(traceback.format_exc()) + return 1 + initData.properties = createProperties(args, initData.properties) + + # + # 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 isinstance(getProcessLogger(), LoggerI): + setProcessLogger(_ApplicationLoggerI(initData.properties.getProperty("Ice.ProgramName"))) + + # + # Install our handler for the signals we are interested in. We assume main() + # is called from the main thread. + # + Application._ctrlCHandler = CtrlCHandler() + + try: + status = 0 + + Application._interrupted = False + Application._appName = initData.properties.getPropertyWithDefault("Ice.ProgramName", args[0]) + Application._application = self + Application._communicator = initialize(args, initData) + Application._destroyed = False + + # + # Used by _destroyOnInterruptCallback and _shutdownOnInterruptCallback. + # + Application._nohup = Application._communicator.getProperties().getPropertyAsInt("Ice.Nohup") > 0 + + # + # The default is to destroy when a signal is received. + # + if Application._signalPolicy == Application.HandleSignals: + Application.destroyOnInterrupt() + + status = self.doMain(args, initData) + except: + getProcessLogger().error(traceback.format_exc()) + 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 Application._signalPolicy == Application.HandleSignals: + Application.ignoreInterrupt() + + Application._condVar.acquire() + while Application._callbackInProgress: + Application._condVar.wait() + if Application._destroyed: + Application._communicator = None + else: + Application._destroyed = True + # + # And _communicator != 0, meaning will be destroyed + # next, _destroyed = true also ensures that any + # remaining callback won't do anything + # + Application._application = None + Application._condVar.release() + + if Application._communicator: + try: + Application._communicator.destroy() + except: + getProcessLogger().error(traceback.format_exc()) + status = 1 + + Application._communicator = None + + # + # Set _ctrlCHandler to 0 only once communicator.destroy() has + # completed. + # + Application._ctrlCHandler.destroy() + Application._ctrlCHandler = None + + return status + + def doMain(self, args, initData): + return self.run(args) + + def run(self, args): + '''This method must be overridden in a subclass. The base +class supplies an argument list from which all Ice arguments +have already been removed. The method returns an integer +exit status (0 is success, non-zero is failure). +''' + raise RuntimeError('run() not implemented') + + def interruptCallback(self, sig): + '''Subclass hook to intercept an interrupt.''' + pass + + def appName(self): + '''Returns the application name (the first element of +the argument list).''' + return self._appName + appName = classmethod(appName) + + def communicator(self): + '''Returns the communicator that was initialized for +the application.''' + return self._communicator + communicator = classmethod(communicator) + + def destroyOnInterrupt(self): + '''Configures the application to destroy its communicator +when interrupted by a signal.''' + if Application._signalPolicy == Application.HandleSignals: + self._condVar.acquire() + if self._ctrlCHandler.getCallback() == self._holdInterruptCallback: + self._released = True + self._condVar.notify() + self._ctrlCHandler.setCallback(self._destroyOnInterruptCallback) + self._condVar.release() + else: + getProcessLogger().error(Application._appName + \ + ": warning: interrupt method called on Application configured to not handle interrupts.") + destroyOnInterrupt = classmethod(destroyOnInterrupt) + + def shutdownOnInterrupt(self): + '''Configures the application to shutdown its communicator +when interrupted by a signal.''' + if Application._signalPolicy == Application.HandleSignals: + self._condVar.acquire() + if self._ctrlCHandler.getCallback() == self._holdInterruptCallback: + self._released = True + self._condVar.notify() + self._ctrlCHandler.setCallback(self._shutdownOnInterruptCallback) + self._condVar.release() + else: + getProcessLogger().error(Application._appName + \ + ": warning: interrupt method called on Application configured to not handle interrupts.") + shutdownOnInterrupt = classmethod(shutdownOnInterrupt) + + def ignoreInterrupt(self): + '''Configures the application to ignore signals.''' + if Application._signalPolicy == Application.HandleSignals: + self._condVar.acquire() + if self._ctrlCHandler.getCallback() == self._holdInterruptCallback: + self._released = True + self._condVar.notify() + self._ctrlCHandler.setCallback(None) + self._condVar.release() + else: + getProcessLogger().error(Application._appName + \ + ": warning: interrupt method called on Application configured to not handle interrupts.") + ignoreInterrupt = classmethod(ignoreInterrupt) + + def callbackOnInterrupt(self): + '''Configures the application to invoke interruptCallback +when interrupted by a signal.''' + if Application._signalPolicy == Application.HandleSignals: + self._condVar.acquire() + if self._ctrlCHandler.getCallback() == self._holdInterruptCallback: + self._released = True + self._condVar.notify() + self._ctrlCHandler.setCallback(self._callbackOnInterruptCallback) + self._condVar.release() + else: + getProcessLogger().error(Application._appName + \ + ": warning: interrupt method called on Application configured to not handle interrupts.") + callbackOnInterrupt = classmethod(callbackOnInterrupt) + + def holdInterrupt(self): + '''Configures the application to queue an interrupt for +later processing.''' + if Application._signalPolicy == Application.HandleSignals: + self._condVar.acquire() + if self._ctrlCHandler.getCallback() != self._holdInterruptCallback: + self._previousCallback = self._ctrlCHandler.getCallback() + self._released = False + self._ctrlCHandler.setCallback(self._holdInterruptCallback) + # else, we were already holding signals + self._condVar.release() + else: + getProcessLogger().error(Application._appName + \ + ": warning: interrupt method called on Application configured to not handle interrupts.") + holdInterrupt = classmethod(holdInterrupt) + + def releaseInterrupt(self): + '''Instructs the application to process any queued interrupt.''' + if Application._signalPolicy == Application.HandleSignals: + self._condVar.acquire() + if self._ctrlCHandler.getCallback() == self._holdInterruptCallback: + # + # Note that it's very possible no signal is held; + # in this case the callback is just replaced and + # setting _released to true and signalling _condVar + # do no harm. + # + self._released = True + self._ctrlCHandler.setCallback(self._previousCallback) + self._condVar.notify() + # Else nothing to release. + self._condVar.release() + else: + getProcessLogger().error(Application._appName + \ + ": warning: interrupt method called on Application configured to not handle interrupts.") + releaseInterrupt = classmethod(releaseInterrupt) + + def interrupted(self): + '''Returns True if the application was interrupted by a +signal, or False otherwise.''' + self._condVar.acquire() + result = self._interrupted + self._condVar.release() + return result + interrupted = classmethod(interrupted) + + def _holdInterruptCallback(self, sig): + self._condVar.acquire() + while not self._released: + self._condVar.wait() + if self._destroyed: + # + # Being destroyed by main thread + # + self._condVar.release() + return + callback = self._ctrlCHandler.getCallback() + self._condVar.release() + if callback: + callback(sig) + _holdInterruptCallback = classmethod(_holdInterruptCallback) + + def _destroyOnInterruptCallback(self, sig): + self._condVar.acquire() + if self._destroyed or self._nohup and sig == signal.SIGHUP: + # + # Being destroyed by main thread, or nohup. + # + self._condVar.release() + return + + self._callbackInProcess = True + self._interrupted = True + self._destroyed = True + self._condVar.release() + + try: + self._communicator.destroy() + except: + getProcessLogger().error(self._appName + " (while destroying in response to signal " + str(sig) + "):" + \ + traceback.format_exc()) + + self._condVar.acquire() + self._callbackInProcess = False + self._condVar.notify() + self._condVar.release() + _destroyOnInterruptCallback = classmethod(_destroyOnInterruptCallback) + + def _shutdownOnInterruptCallback(self, sig): + self._condVar.acquire() + if self._destroyed or self._nohup and sig == signal.SIGHUP: + # + # Being destroyed by main thread, or nohup. + # + self._condVar.release() + return + + self._callbackInProcess = True + self._interrupted = True + self._condVar.release() + + try: + self._communicator.shutdown() + except: + getProcessLogger().error(self._appName + " (while shutting down in response to signal " + str(sig) + \ + "):" + traceback.format_exc()) + + self._condVar.acquire() + self._callbackInProcess = False + self._condVar.notify() + self._condVar.release() + _shutdownOnInterruptCallback = classmethod(_shutdownOnInterruptCallback) + + def _callbackOnInterruptCallback(self, sig): + self._condVar.acquire() + if self._destroyed: + # + # Being destroyed by main thread. + # + self._condVar.release() + return + # For SIGHUP the user callback is always called. It can decide + # what to do. + + self._callbackInProcess = True + self._interrupted = True + self._condVar.release() + + try: + self._application.interruptCallback(sig) + except: + getProcessLogger().error(self._appName + " (while interrupting in response to signal " + str(sig) + \ + "):" + traceback.format_exc()) + + self._condVar.acquire() + self._callbackInProcess = False + self._condVar.notify() + self._condVar.release() + + _callbackOnInterruptCallback = classmethod(_callbackOnInterruptCallback) + + HandleSignals = 0 + NoSignalHandling = 1 + + _appName = None + _communicator = None + _application = None + _ctrlCHandler = None + _previousCallback = None + _interrupted = False + _released = False + _destroyed = False + _callbackInProgress = False + _condVar = threading.Condition() + _signalPolicy = HandleSignals + +# +# Define Ice::Object and Ice::ObjectPrx. +# +IcePy._t_Object = IcePy.defineClass('::Ice::Object', Object, (), False, None, (), ()) +IcePy._t_ObjectPrx = IcePy.defineProxy('::Ice::Object', ObjectPrx) +Object._ice_type = IcePy._t_Object + +Object._op_ice_isA = IcePy.Operation('ice_isA', OperationMode.Idempotent, OperationMode.Nonmutating, False, (), (((), IcePy._t_string),), (), IcePy._t_bool, ()) +Object._op_ice_ping = IcePy.Operation('ice_ping', OperationMode.Idempotent, OperationMode.Nonmutating, False, (), (), (), None, ()) +Object._op_ice_ids = IcePy.Operation('ice_ids', OperationMode.Idempotent, OperationMode.Nonmutating, False, (), (), (), _t_StringSeq, ()) +Object._op_ice_id = IcePy.Operation('ice_id', OperationMode.Idempotent, OperationMode.Nonmutating, False, (), (), (), IcePy._t_string, ()) + +IcePy._t_LocalObject = IcePy.defineClass('::Ice::LocalObject', object, (), False, None, (), ()) + +# +# Annotate some exceptions. +# +def SyscallException__str__(self): + return "Ice.SyscallException:\n" + os.strerror(self.error) +SyscallException.__str__ = SyscallException__str__ +del SyscallException__str__ + +def SocketException__str__(self): + return "Ice.SocketException:\n" + os.strerror(self.error) +SocketException.__str__ = SocketException__str__ +del SocketException__str__ + +def ConnectFailedException__str__(self): + return "Ice.ConnectFailedException:\n" + os.strerror(self.error) +ConnectFailedException.__str__ = ConnectFailedException__str__ +del ConnectFailedException__str__ + +def ConnectionRefusedException__str__(self): + return "Ice.ConnectionRefusedException:\n" + os.strerror(self.error) +ConnectionRefusedException.__str__ = ConnectionRefusedException__str__ +del ConnectionRefusedException__str__ + +def ConnectionLostException__str__(self): + if self.error == 0: + return "Ice.ConnectionLostException:\nrecv() returned zero" + else: + return "Ice.ConnectionLostException:\n" + os.strerror(self.error) +ConnectionLostException.__str__ = ConnectionLostException__str__ +del ConnectionLostException__str__ + +# +# Proxy comparison functions. +# +def proxyIdentityEqual(lhs, rhs): + '''Determines whether the identities of two proxies are equal.''' + return proxyIdentityCompare(lhs, rhs) == 0 + +def proxyIdentityCompare(lhs, rhs): + '''Compares the identities of two proxies.''' + if (lhs and not isinstance(lhs, ObjectPrx)) or (rhs and not isinstance(rhs, ObjectPrx)): + raise ValueError('argument is not a proxy') + if not lhs and not rhs: + return True + elif not lhs and rhs: + return -1 + elif lhs and not rhs: + return 1 + else: + return cmp(lhs.ice_getIdentity(), rhs.ice_getIdentity()) + +def proxyIdentityAndFacetEqual(lhs, rhs): + '''Determines whether the identities and facets of two +proxies are equal.''' + return proxyIdentityAndFacetCompare(lhs, rhs) == 0 + +def proxyIdentityAndFacetCompare(lhs, rhs): + '''Compares the identities and facets of two proxies.''' + if (lhs and not isinstance(lhs, ObjectPrx)) or (rhs and not isinstance(rhs, ObjectPrx)): + raise ValueError('argument is not a proxy') + if not lhs and not rhs: + return True + elif not lhs and rhs: + return -1 + elif lhs and not rhs: + return 1 + elif lhs.ice_getIdentity() != rhs.ice_getIdentity(): + return cmp(lhs.ice_getIdentity(), rhs.ice_getIdentity()) + else: + return cmp(lhs.ice_getFacet(), rhs.ice_getFacet()) |