##
# This software was developed and / or modified by Raytheon Company,
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
# 
# U.S. EXPORT CONTROLLED TECHNICAL DATA
# This software product contains export-restricted data whose
# export/transfer/disclosure is restricted by U.S. law. Dissemination
# to non-U.S. persons whether in the United States or abroad requires
# an export license or other authorization.
# 
# Contractor Name:        Raytheon Company
# Contractor Address:     6825 Pine Street, Suite 340
#                         Mail Stop B8
#                         Omaha, NE 68106
#                         402.291.0100
# 
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
# further licensing information.
##
#
#    Name:
#       AvnServer.py
#       GFS1-NHD:A7965.0000-SCRIPT;1.6
#
#    Status:
#       DELIVERED
#    
# AvnServer.py
# wrappers for name and event servers
# George Trojan, SAIC/MDL, August 2004
# last update: 06/04/05

import os, threading
import Pyro.core, Pyro.naming, Pyro.util, Pyro.constants
from Pyro.errors import *
import AvnPyro

class NameServer(threading.Thread):
    def __init__(self, **kw):
        threading.Thread.__init__(self)
        self.setDaemon(1)
        self.kw = kw
        self.starter = Pyro.naming.NameServerStarter()

    def run(self):
        print "Launching Pyro Name Server"
        kw = {'hostname': self.kw.get('-n', '')}
        kw.update({'persistent': 1, 'dbdir': 'nsdb', 'verbose': 1,\
            'Guards': (AvnPyro.NSGuard(), AvnPyro.BCGuard())})
        self.starter.start(**kw)

    def waitUntilStarted(self, timeout=None):
        return self.starter.waitUntilStarted(timeout)

##############################################################################
import Pyro.EventService.Server

Log=Pyro.util.Log
        
class EventServer(threading.Thread):
    def __init__(self, **kw):
        threading.Thread.__init__(self)
        self.setDaemon(1)
        self.kw = kw
        self.starter = EventServiceStarter()

    def run(self):
        Log.msg('Launching Pyro Event Server')
        self.starter.start(hostname=self.kw.get('-n', ''))

    def waitUntilStarted(self):
        return self.starter.waitUntilStarted()

###############################################################################
# EventServiceStarter is copied from Pyro.EventService.Server, with minor
# modifications, to avoid the need for interactive call

class EventServiceStarter:
    def __init__(self, identification=None):
        self.running=1
        self.identification=identification
        self.started = Pyro.util.getEventObject()

    def start(self, *args, **kwargs):
        # see _start for allowed arguments
        self._start(startloop=1, *args, **kwargs)

    def initialize(self, *args, **kwargs):      
        # see _start for allowed arguments
        self._start(startloop=0, *args, **kwargs)

    def getServerSockets(self):
        return self.daemon.getServerSockets()

    def waitUntilStarted(self, timeout=None):
        self.started.wait(timeout)
        return self.started.isSet()

    def _start(self, hostname='', port=None, startloop=1, useNameServer=1):
        daemon = Pyro.core.Daemon(host=hostname, port=port)
        if self.identification:
            # Requiring connection authentication
            daemon.setAllowedIdentifications([self.identification])

        if useNameServer:
            locator = Pyro.naming.NameServerLocator( \
                identification=self.identification)
            try:
                port = int(os.environ['PYRO_NS_PORT'])
            except KeyError:
                port = None
            ns = locator.getNS(port=port)
    
            # check if ES already running
            try:
                ns.resolve(Pyro.constants.EVENTSERVER_NAME)
                Log.warn('Event Server appears to be already running.')
#               ns.unregister(Pyro.constants.EVENTSERVER_NAME)
            except NamingError:
                pass
    
            daemon.useNameServer(ns)

        es = Pyro.EventService.Server.EventService()

        esURI=daemon.connectPersistent(es, 
            Pyro.constants.EVENTSERVER_NAME)
        Log.msg('URI=%s' % esURI)

        message = daemon.validateHostnameAndIP()
        if message:
            Log.warn('WARNING: ' + message)

        Log.msg('Event Server started.')

        self.started.set()      # signal that we've started.

        if startloop:
            Log.msg('ES daemon','This is the Pyro Event Server.')
            try:
                daemon.setTimeout(20)  # XXX fixed timeout
                daemon.requestLoop(lambda s=self: s.running)
            except KeyboardInterrupt:
                Log.warn('ES daemon', 'shutdown on user break signal')
                self.shutdown(es)
            except:
                try:
                    import traceback
                    (exc_type, exc_value, exc_trb) = sys.exc_info()
                    out = ''.join(traceback.format_exception(exc_type, 
                        exc_value, exc_trb)[-5:])
                    Log.error('ES daemon', 'Unexpected exception, type',
                        exc_type,
                        '\n--- partial traceback of this exception follows:\n',
                        out,'\n--- end of traceback')
                finally:    
                    del exc_type, exc_value, exc_trb
            Log.msg('ES daemon','Shut down gracefully.')
        else:
            # no loop, store the required objects for 
            # getServerSockets()
            self.daemon=daemon
            self.es=es
            daemon.setTimeout(20)  # XXX fixed timeout

    def mustContinueRunning(self):
        return self.running

    def handleRequests(self, timeout=None):
        # this method must be called from a custom event loop
        self.daemon.handleRequests(timeout=timeout)

    def shutdown(self, es):
        if es:
            # internal shutdown call with specified ES object
            daemon=es.getDaemon()
        else:
            # custom shutdown call w/o specified ES object, 
            # use stored instance
            daemon=self.daemon
            es=self.es
            del self.es, self.daemon
        try:
            daemon.disconnect(es) # clean up nicely
        except NamingError,x:
            Log.warn('ES daemon', 'disconnect error during shutdown:',x)
        except ConnectionClosedError,x:
            Log.warn('ES daemon',
                'lost connection with Name Server, cannot unregister')
        self.running=0
        daemon.shutdown()
