Ejemplo n.º 1
0
class ListenerClass(object):
    
    """
    Main class of the SPEL Listener entity. Listens at a given port waiting for
    GUI connections. Manages the SPEL Context processes on GUI requests.
    """
    
    # Client key list
    clientKeys = []
    # Context list
    contexts = []
    # Holds contextID / key pairs
    contextKeys = {}
    # Holds contextID / port pairs
    contextPorts = {}
    # Context status (available/running)
    contextStatus = {}
    # GUI operations delegate
    GUI_Operations = None
    # Synchronization issues
    buffer = None
    # IPC context interface
    __ctxIFC = None 
    # IPC gui interface
    __guiIFC = None 
    # Client lock
    __clientLock = None
    # Context lock
    __ctxLock = None
    
    #===========================================================================
    def __init__(self, warmStart=False):
        self.openContexts = {}
        self.buffer = Queue(1)
        self.GUI_Operations = GUIOperations(self)
        # Obtain the list of available contexts from cfg file
        self.contexts = Config.instance().getAvailableContexts()
        # Update the context status map (all available)
        for ctx in self.contexts:
            self.contextPorts[ctx] = 0
            self.contextStatus[ctx] = LstMessages.DATA_CTX_AVAILABLE
        self.__ctxIFC = IPCinterfaceServer("LST-CTX")
        self.__guiIFC = IPCinterfaceServer("LST-GUI")
        self.__clientLock = thread.allocate_lock()
        self.__ctxLock = thread.allocate_lock()
        self.__contextRegistry = ProcessRegistry("LST", warmStart)
    
    #===========================================================================
    def start(self):
        
        # Get the port number from configuration
        port = Config.instance().getProperty(LISTENER, "ListenerPort")
        LOG("Listening at port: " + str(port), level = LOG_INIT )
        
        # Setup the GUI io channel
        LOG("Setting up GUI channel", level = LOG_INIT)
        self.__guiIFC.connect(999, int(port), self.processGuiMessage, self.processGuiRequest, self.clientConnectionLost)
        self.__guiIFC.start()

        # Setup the Context io channel
        LOG("Setting up CTX channel", level = LOG_INIT)
        self.__ctxIFC.connect(998,0,self.processCtxMessage, self.processCtxRequest, self.contextConnectionLost)
        self.__ctxIFC.start()
        LOG("Ready", level = LOG_INIT )
        
        #Restoring warm context
        for contextInfo in self.__contextRegistry.getProcesses():
            #Only during startup we open contexts in warm mode
            self.openContext(contextInfo.getName(), None, True) 
   
    #===========================================================================
    def stop(self):
        LOG("Stopping, closing contexts", level = LOG_PROC )
        for contextName in self.contexts:
            if self.isContextRunning(contextName):
                self.closeContext(contextName)
        LOG("Killing remaining processes", level = LOG_PROC )
        ProcessManager.instance().killAll()
        self.__guiIFC.disconnect()
        self.__ctxIFC.disconnect()
        LOG("Disconnected", level = LOG_INIT )
        
    #===========================================================================
    def clientConnectionLost(self, clientKey):
        if clientKey in self.clientKeys:
            LOG("Listener lost connection with client: " + repr(clientKey), LOG_ERROR )
            self.clientKeys.remove(clientKey)

    #===========================================================================
    def contextConnectionLost(self, contextKey):
        # Obtain the corresponding name from the key
        for key in self.contextKeys:
            if self.contextKeys[key] == contextKey:
                contextName = key
        originalStatus = self.contextStatus[contextName]
        self.contextStatus[contextName] = LstMessages.DATA_CTX_ERROR
        if not originalStatus == LstMessages.DATA_CTX_STARTING:
            LOG("Listener lost connection with starting context " + repr(contextName), LOG_ERROR)
        else:
            LOG("Listener lost connection with context " + repr(contextName), LOG_ERROR)
        self.notifyContextCrash(contextName)
        self.clearContext(contextName)
        self.__ctxIFC.disconnect(contextKey, eoc = False)
        LOG("Context-lost done")
        
    #===========================================================================
    def contextProcessLost(self, contextName, retCode):
        if (retCode == 0) and not contextName in self.contextKeys:
            LOG("Context finished with code 0")
            return
        self.contextStatus[contextName] = LstMessages.DATA_CTX_ERROR
        if contextName in self.contexts:
            LOG("Listener lost track of context " + repr(contextName), LOG_ERROR)
            self.notifyContextCrash(contextName)
            self.clearContext(contextName)
            if contextName in self.contextKeys:
                contextKey = self.contextKeys[contextName]
                self.__ctxIFC.disconnect(contextKey, eoc = False)
            LOG("Done")
        
    #===========================================================================
    def processGuiMessage(self, msg):
        self.__clientLock.acquire()
        LOG("Received GUI message: " + msg.getId(), level = LOG_COMM)
        guiKey = int(msg.getKey())
        if msg.getId() == LstMessages.MSG_GUI_LOGIN:
            LOG("Client logged in: " + str(guiKey), level = LOG_PROC)
            self.clientKeys.append(guiKey)
        elif msg.getId() == LstMessages.MSG_GUI_LOGOUT:
            self.clientKeys.remove(guiKey)
            LOG("Client logged out: " + str(guiKey), level = LOG_PROC)
        else:
            LOG("ERROR: unknown message from client: " + str(msg.getId()), LOG_ERROR)
        self.__clientLock.release()

    #===========================================================================
    def processCtxMessage(self, msg):
        self.__ctxLock.acquire()
        LOG("Received Context message: " + msg.getId(), level = LOG_COMM)
        if msg.getId() == LstMessages.MSG_CONTEXT_OPEN:
            contextName = msg[LstMessages.FIELD_CTX_NAME]
            LOG("Context logged in: " + contextName, level = LOG_PROC)
            contextKey = int(msg.getKey())
            ctxPort = msg[LstMessages.FIELD_CTX_PORT]
            self.contextPorts[contextName] = ctxPort
            self.contextStatus[contextName] = LstMessages.DATA_CTX_RUNNING
            self.contextKeys[contextName] = contextKey
            self.buffer.put(ctxPort, True)
        elif msg.getId() == LstMessages.MSG_CONTEXT_CLOSED:
            contextName = msg[LstMessages.FIELD_CTX_NAME]
            contextKey = int(msg.getKey())
            LOG("Context logged out: " + contextName + ":" + repr(contextKey), level = LOG_PROC)
            self.contextStatus[contextName] = LstMessages.DATA_CTX_AVAILABLE
            self.buffer.put(contextName, True)
        else:
            LOG("ERROR: unknown message from context:" + str(msg.getId()), LOG_ERROR)
        self.__ctxLock.release()
            
    #===========================================================================
    def processGuiRequest(self, msg):
        self.__clientLock.acquire()
        resp = self.GUI_Operations.processRequest(msg)
        self.__clientLock.release()
        return resp

    #===========================================================================
    def processCtxRequest(self, msg):
        self.__ctxLock.acquire()
        resp = self.createResponse("NONE", msg)
        self.__ctxLock.release()
        return resp
            
    #===========================================================================
    def openContext(self, contextName, clientKey = None, warm = False ):
        
        ctxScript = Config.instance().getProperty(LISTENER, "ContextScript")
        ctxScript = Config.instance().getHome() + os.sep + ctxScript
        arguments = "-c \"" + Config.instance().filename + "\" -n \"" + contextName + "\""
        arguments += " -s " + str(self.__ctxIFC.port)
        if warm == True:
            arguments += " -w"
        pythonBin = os.getenv("PYTHON", "python")
        ctxScript = pythonBin + " \"" + ctxScript + "\" " + arguments
        
        # Set the status as starting (internal)
        self.contextStatus[contextName] = LstMessages.DATA_CTX_STARTING
        
        LOG("Starting context: '" + contextName + "'", level = LOG_PROC )
        pid = ProcessManager.instance().startProcess( contextName, ctxScript, self.contextProcessLost )
        LOG("Context started with pid " + str(pid), level = LOG_PROC )

        LOG("Waiting context port", level = LOG_PROC )
        try:
            ctxPort = self.buffer.get(True,CONTEXT_START_TIMEO)
            LOG("Context port is " + str(ctxPort), level = LOG_PROC )
            # Set the status as started
            self.contextStatus[contextName] = LstMessages.DATA_CTX_RUNNING
            self.notifyContextUpdate(contextName,clientKey)
            self.__contextRegistry.addProcess(pid,contextName)
            return ctxPort
        except BaseException,ex:
            txt = "Failed to open context"
            reason = "Unable to get context listening port"
            LOG(txt + ": " + reason, LOG_ERROR)
            self.killContext(contextName)
            # Set the status as error
            self.contextStatus[contextName] = LstMessages.DATA_CTX_ERROR
            self.notifyContextUpdate(contextName,clientKey,txt,reason)
            return None
Ejemplo n.º 2
0
class ContextClass(object):

    # Holds the context name
    ctxName = None

    # Holds the listening port for GUIs
    port = None
    # Holds the listener port
    listenerPort = None

    # Holds the set of instance numbers for each executor id
    __executorInstanceNumbers = {}
    # Holds the set of executor managers
    __executorManagers = {}
    # Holds the set of clients
    __clientInfo = {}

    # GUI operations delegate
    GUI_Operations = None

    # Listener operations delegate
    LST_Operations = None

    # Executor operations delegate
    EXC_Operations = None

    # IPC interface for clients
    __guiIFC = None

    # IPC interface for listener
    __lstIFC = None

    # Lock for clients
    __clientLock = None

    # Lock for executors
    __execLock = None
    # Lock for context process closure
    __closeLock = None

    #Process registry for registering opened executors in a file
    __executorsRegistry = None

    #===========================================================================
    def __init__(self, ctxName, listenerPort, warmStart=False):
        LOG("Created context", level=LOG_INIT)
        self.ctxName = ctxName
        self.port = None
        self.__executorInstanceNumbers = {}
        self.__executorManagers = {}
        self.__clientInfo = {}
        self.GUI_Operations = GUIOperations(self)
        self.LST_Operations = LSTOperations(self)
        self.EXC_Operations = EXCOperations(self)
        self.__guiIFC = IPCinterfaceServer("CTX-GUI")
        self.__lstIFC = IPCinterfaceClient("CTX-LST")
        self.listenerPort = listenerPort
        self.__clientLock = thread.allocate_lock()
        self.__execLock = thread.allocate_lock()
        self.__closeLock = Queue.Queue(1)
        self.__executorsRegistry = ProcessRegistry("CTX_" + ctxName, warmStart)

    #===========================================================================
    def start(self):
        LOG("Loading procedures for this context", level=LOG_INIT)
        ProcedureManager.instance().setup(self.ctxName)

        LOG("Setting up GUI channel", level=LOG_INIT)
        # Setup the Gui io channel
        self.__guiIFC.connect(888, 0, self.processGuiMessage,
                              self.processGuiRequest, self.guiConnectionLost)
        self.__guiIFC.start()

        LOG("Connecting to listener", level=LOG_INIT)
        # Setup the Listener io channel
        self.__lstIFC.connect("localhost", self.listenerPort,
                              self.processListenerMessage,
                              self.processListenerRequest,
                              self.lstConnectionLost)

        LOG("Using listening port: " + str(self.__guiIFC.port), level=LOG_INIT)
        portMsg = MsgHelper.createMessage(LstMessages.MSG_CONTEXT_OPEN)
        portMsg[LstMessages.FIELD_CTX_NAME] = self.ctxName
        portMsg[LstMessages.FIELD_CTX_PORT] = str(self.__guiIFC.port)
        portMsg.setSender("CTX")
        portMsg.setReceiver("LST")
        self.__lstIFC.sendMessage(portMsg)
        LOG("Ready", level=LOG_INIT)
        sys.stderr.write("*********************************************\n")
        sys.stderr.write("      SPEL Context (" + ctxName + ") Ready \n")
        sys.stderr.write("*********************************************\n")

        #for executorInfo in self.__executorRegistry.getProcesses():
        #    #Launch executors stored in warm file
        #    self.openExecutor(executorInfo.getName(), clientKey, clientMode)

    #===========================================================================
    def stop(self):
        # Force closing all executors
        LOG("Closing executors", level=LOG_PROC)
        self.closeAll()
        # Disconnect comm interfaces
        LOG("Disconnecting from GUI", level=LOG_PROC)
        self.__guiIFC.disconnect()

        LOG("Logout from listener", level=LOG_PROC)
        logoutMsg = MsgHelper.createMessage(LstMessages.MSG_CONTEXT_CLOSED)
        logoutMsg[LstMessages.FIELD_CTX_NAME] = self.ctxName
        logoutMsg.setSender("CTX")
        logoutMsg.setReceiver("LST")
        self.__lstIFC.sendMessage(logoutMsg)
        self.__lstIFC.disconnect()
        LOG("Disconnected", level=LOG_PROC)
        self.__closeLock.put(1)

    #===========================================================================
    def closeAll(self):
        if len(self.getExecutors()) == 0:
            LOG("No executors to be closed")
            return
        for procId in self.getExecutors():
            LOG("Close all: " + procId)
            executor = self.getExecutor(procId)
            if executor:
                executor.stop()
                executor.waitForClose()

    #===========================================================================
    def waitForClose(self):
        self.__closeLock.get(True)

    #===========================================================================
    def guiConnectionLost(self, clientKey):
        if clientKey in self.getClients():
            LOG("Context lost connection with client: " + repr(clientKey),
                LOG_ERROR)
            self.removeClient(clientKey)

    #===========================================================================
    def lstConnectionLost(self, lstKey):
        LOG("Lost connection with listener", LOG_ERROR)
        self.__lstIFC.disconnect(eoc=False)

        LOG("Notifying listener connection lost")

        # Build the notification message
        msg = MsgHelper.createMessage(CtxMessages.MSG_LISTENER_LOST)
        msg.setType(MSG_TYPE_ERROR)
        msg[FIELD_ERROR] = "Lost connection with listener"
        msg[FIELD_REASON] = " "

        clientKeys = []
        for client in self.getClients():
            clientKeys.append(int(client))
        if len(clientKeys) > 0:
            self.messageToClients("CTX", clientKeys, msg)
        LOG("Done")
        return

    #===========================================================================
    def processGuiMessage(self, msg):
        if not self.GUI_Operations.processMessage(msg):
            # If the message is not processed locally, forward it to executors
            procId = msg[ExcMessages.FIELD_PROC_ID]
            # Forward message to the corresponding executor
            clientKey = int(msg.getKey())
            self.messageToExecutor(clientKey, procId, msg)
        return

    #===========================================================================
    def processGuiRequest(self, msg):
        LOG("Process GUI request: " + msg.getId(), level=LOG_COMM)
        resp = None
        resp = self.GUI_Operations.processRequest(msg)
        if resp is None:
            # if the request is not processed locally, forward it to executors
            procId = msg[ExcMessages.FIELD_PROC_ID]
            if not procId in self.getExecutors():
                resp = MsgHelper.createError(
                    msg.getId(), msg,
                    "Unable to forward request " + msg.getId(),
                    "No such executor: " + repr(procId))
            else:
                clientKey = int(msg.getKey())
                LOG("Send GUI request to executor: " + msg.getId(),
                    level=LOG_COMM)
                resp = self.requestToExecutor(clientKey, procId, msg)
        LOG("Process GUI request: " + msg.getId() + " finished",
            level=LOG_COMM)
        return resp

    #===========================================================================
    def processListenerMessage(self, msg):
        self.LST_Operations.processMessage(msg)

    #===========================================================================
    def processListenerRequest(self, msg):
        resp = self.LST_Operations.processRequest(msg)
        return resp

    #===========================================================================
    def processExecutorMessage(self, msg):
        self.EXC_Operations.processMessage(msg)

    #===========================================================================
    def processExecutorRequest(self, msg):
        resp = self.EXC_Operations.processRequest(msg)
        return resp

    #===========================================================================
    def messageToClients(self, procId, clientKeys, msg):
        for clientKey in clientKeys:
            LOG("Forwarding message " + msg.getId() + " from executor " +
                procId + " to client " + repr(clientKey),
                level=LOG_COMM)
            # If the client key is a procedure id, it means the request is to
            # be sent to another procedure
            msg.setSender(procId)
            if type(clientKey) == int:
                msg.setReceiver("GUI-" + str(clientKey))
                resp = self.__guiIFC.sendMessage(msg, clientKey)
            else:
                parentProc = clientKey
                msg.setReceiver(parentProc)
                resp = self.messageToExecutor(procId, parentProc, msg)

    #===========================================================================
    def requestToClients(self, procId, clientKeys, msg):
        LOG("Forward executor " + procId + " request " + msg.getId() +
            " to clients",
            level=LOG_COMM)
        firstResp = None
        if len(clientKeys) == 0:
            LOG("No clients to attend request. Discarding it",
                LOG_WARN,
                level=LOG_COMM)
            firstResp = MsgHelper.createError(msg.getId(), msg,
                                              "Cannot dispatch request",
                                              "No clients connected")
        for clientKey in clientKeys:
            msg.setSender(procId)
            # If the client key is a procedure id, it means the request is to
            # be sent to another procedure
            if type(clientKey) == int:
                msg.setReceiver("GUI-" + str(clientKey))
                resp = self.__guiIFC.forwardRequest(msg, clientKey)
            else:
                parentProc = clientKey
                msg.setReceiver(parentProc)
                resp = self.requestToExecutor(procId, parentProc, msg)
            if firstResp is None: firstResp = resp
        LOG("Request to clients '" + msg.getId() + "' finished",
            level=LOG_COMM)
        return firstResp

    #===========================================================================
    def messageToExecutor(self, clientKey, procId, msg):
        if procId in self.getExecutors():
            exc = self.getExecutor(procId)
            if exc: exc.messageToExecutor(clientKey, msg)

    #===========================================================================
    def requestToExecutor(self, clientKey, procId, msg):
        LOG("Forward client " + repr(clientKey) + " request " + msg.getId() +
            " to executor " + procId,
            level=LOG_COMM)
        resp = None
        if procId in self.getExecutors():
            exc = self.getExecutor(procId)
            if exc: resp = exc.requestToExecutor(clientKey, msg)
        if resp is None:
            resp = MsgHelper.createError(msg.getId(), msg,
                                         "Unable to forward request",
                                         "No such executor")
        LOG("Request to executor " + msg.getId() + " finished", level=LOG_COMM)
        return resp

    #===========================================================================
    def createExecutor(self, procId):
        self.__execLock.acquire()
        try:
            LOG("Creating executor manager for " + repr(procId))
            manager = ExecutorManagerClass(self, procId)
            self.__executorManagers[procId] = manager
            LOG("Manager created")
        finally:
            self.__execLock.release()
        return manager

    #===========================================================================
    def getInstanceId(self, procId):
        self.__execLock.acquire()
        try:
            instance = 0
            LOG("Obtain available instance for " + repr(procId))
            if procId in self.__executorInstanceNumbers:
                for i in range(0, 50):
                    if not i in self.__executorInstanceNumbers[procId]:
                        instance = i
                        break
                self.__executorInstanceNumbers[procId].append(instance)
            else:
                self.__executorInstanceNumbers[procId] = [instance]
            procId = procId + "#" + str(instance)
            LOG("Instance is " + repr(procId))
        finally:
            self.__execLock.release()
        return procId

    #===========================================================================
    def openExecutor(self,
                     procId,
                     clientKey,
                     clientMode,
                     openMode={},
                     arguments=None,
                     condition=None,
                     parentId=None):
        ctxInfo = Config.instance().getContextConfig(self.ctxName)
        driverName = ctxInfo.getDriver()
        maxProcs = int(
            Config.instance().getDriverConfig(driverName).getMaxProcs())
        if maxProcs > 0:
            activeProcs = self.getNumActiveProcs()
            if (activeProcs >= maxProcs):
                raise ContextError(
                    "Could not launch executor. Maximum number of processes reached ("
                    + str(maxProcs) + ")")

        success = False
        LOG("Requested opening executor " + repr(procId), level=LOG_PROC)
        executor = self.createExecutor(procId)

        # Update the proc id with the instance number
        procId = executor.getProcId()

        # Get configuration defaults for open mode and update it
        #TODO: get open mode defaults from config
        useOpenMode = {Automatic: False, Visible: True, Blocking: True}
        useOpenMode.update(openMode)

        # Set the controlling client key if the procedure is visible
        if useOpenMode[Visible] == True:
            clientInfo = self.getClient(clientKey)
            executor.addClient(clientInfo, True)
            clientInfo.addExecutor(procId)
            clientInfo.setMode(clientMode)

        LOG("Launching executor " + repr(procId) + " in mode " +
            repr(useOpenMode),
            level=LOG_PROC)

        if arguments:
            executor.setArguments(arguments)
        if condition:
            executor.setCondition(condition)
        if parentId:
            executor.setParent(parentId)

        # Set the open mode and open the executor
        executor.setOpenMode(useOpenMode)

        executor.start()
        success = executor.waitForOpen()
        LOG("Executor launched (" + repr(success) + ")", level=LOG_PROC)

        if not success:
            error, reason = executor.getError()
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_CLOSE)
            self.killExecutor(procId)
            self.clearExecutor(procId)
            raise ContextError("Could not launch executor",
                               error + ":" + reason)
        else:
            initialStatus = executor.getStatus()
            if initialStatus == server.executor.status.ERROR:
                error, reason = executor.getError()
                self.notifyExecutorOperation(procId,
                                             CtxMessages.DATA_EXOP_CLOSE)
                self.killExecutor(procId)
                self.clearExecutor(procId)
                raise ContextError("Executor failed startup",
                                   error + ":" + reason)
            else:
                pid = executor.getExecutorPid()
                self.__executorsRegistry.addProcess(pid, procId)

                LOG("Notifying executor open", level=LOG_PROC)
                self.notifyExecutorOperation(procId,
                                             CtxMessages.DATA_EXOP_OPEN,
                                             clientKey, clientMode)
                LOG("Open finished")
        return

    #===========================================================================
    def closeExecutor(self, procId):
        LOG("Requested closing executor " + repr(procId))

        executor = self.getExecutor(procId)

        if executor is None:
            raise ContextError("No such executor: " + repr(procId))

        LOG("Closing executor")
        executor.stop()
        success = executor.waitForClose()
        LOG("Executor closed (" + repr(success) + ")")

        if not success:
            error, reason = executor.getError()
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_CLOSE)
            self.killExecutor(procId)
            self.clearExecutor(procId)
            raise ContextError("Could not close executor",
                               error + ":" + reason)
        else:
            LOG("Notifying executor close", level=LOG_PROC)
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_CLOSE)
            self.clearExecutor(procId)
            LOG("Close finished")

        return

    #===========================================================================
    def clearExecutor(self, procId):
        LOG("Clearing executor for " + repr(procId))

        executor = self.getExecutor(procId)

        if executor is None:
            raise ContextError("No such executor: " + repr(procId))
        # Remove executor from client infos
        clients = executor.getClients()
        for client in clients:
            self.getClient(client).delExecutor(procId)

        self.__execLock.acquire()
        try:
            # Remove executor manager
            del self.__executorManagers[procId]
            # Remove the corresponding instance from the list
            idx = procId.find("#")
            id = procId[0:idx]
            instance = int(procId[idx + 1:])
            # Remove from the registry
            self.__executorsRegistry.removeProcess(procId)
            if id in self.__executorInstanceNumbers:
                self.__executorInstanceNumbers[id].remove(instance)
                if len(self.__executorInstanceNumbers[id]) == 0:
                    self.__executorInstanceNumbers.pop(id)
        finally:
            self.__execLock.release()
        return

    #===========================================================================
    def killExecutor(self, procId):
        LOG("Killing executor for " + repr(procId))

        executor = self.getExecutor(procId)

        if executor is None:
            raise ContextError("No such executor: " + repr(procId))

        executor.stop(True)

        self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_KILL)
        return True

    #===========================================================================
    def clientAttachExecutor(self, procId, clientKey, clientMode, firstClient):

        executor = self.getExecutor(procId)

        if executor is None:
            raise ContextError("No such executor: " + repr(procId))

        LOG("Client " + str(clientKey) + " requested attaching executor " +
            procId)
        clientKey = int(clientKey)
        clientInfo = self.getClient(clientKey)
        if not procId in clientInfo.getExecutors():
            clientInfo.addExecutor(procId)
            clientInfo.setMode(clientMode)

            if not executor.addClient(clientInfo, firstClient):
                raise ContextError("Cannot add client in mode " +
                                   repr(clientMode))

            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_ATTACH,
                                         clientKey, clientMode)

    #===========================================================================
    def clientDetachExecutor(self, procId, clientKey):

        executor = self.getExecutor(procId)

        if executor is None:
            raise ContextError("No such executor: " + repr(procId))

        LOG("Client " + str(clientKey) + " requested detaching executor " +
            procId)
        clientKey = int(clientKey)
        clientInfo = self.getClient(clientKey)
        if procId in clientInfo.getExecutors():
            self.getClient(clientKey).delExecutor(procId)
            executor.removeClient(clientKey)
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_DETACH,
                                         clientKey)

    #===========================================================================
    def createClient(self, clientKey, host, clientMode):
        self.__clientLock.acquire()
        try:
            self.__clientInfo[clientKey] = ClientInfo(clientKey, host,
                                                      clientMode)
        finally:
            self.__clientLock.release()
        return

    #===========================================================================
    def removeClient(self, clientKey):
        # Process orphan executors
        clientInfo = self.getClient(clientKey)

        self.__clientLock.acquire()
        try:
            if clientInfo:
                execs = clientInfo.getExecutors()
                # Disconnect the interface
                self.__guiIFC.disconnect(clientKey, eoc=False)
                if len(execs) == 0:
                    LOG("No executors linked to client " + repr(clientKey),
                        level=LOG_PROC)
                else:
                    for procId in execs:
                        executor = self.getExecutor(procId)
                        if executor is not None:
                            executor.clientLost(clientKey)
                        else:
                            LOG("Unknown executor:" + procId)
                LOG("Unregistering client: " + repr(clientKey))
                host = self.__clientInfo[clientKey].getHost()
                del self.__clientInfo[clientKey]
        finally:
            self.__clientLock.release()
        return

    #===========================================================================
    def getExecutors(self):
        self.__execLock.acquire()
        keys = []
        try:
            keys = self.__executorManagers.keys()
        finally:
            self.__execLock.release()
        return keys

    #===========================================================================
    def getExecutor(self, procId):
        self.__execLock.acquire()
        exc = None
        try:
            exc = self.__executorManagers.get(procId)
        finally:
            self.__execLock.release()
        return exc

    #===========================================================================
    def getClients(self):
        self.__clientLock.acquire()
        keys = []
        try:
            keys = self.__clientInfo.keys()
        finally:
            self.__clientLock.release()
        return keys

    #===========================================================================
    def getClient(self, clientKey):
        self.__clientLock.acquire()
        clt = None
        try:
            if clientKey == NO_CLIENT:
                clt = None
            else:
                clt = self.__clientInfo[clientKey]
        finally:
            self.__clientLock.release()
        return clt

    #===========================================================================
    def notifyClientOperation(self, clientKey, clientMode, host, operation):

        LOG("Notifying client operation " + repr(operation) + ", " +
            repr(clientKey))

        # Build the notification message
        msg = MsgHelper.createMessage(CtxMessages.MSG_CLIENT_OP)
        msg[CtxMessages.FIELD_GUI_KEY] = clientKey
        msg[CtxMessages.FIELD_GUI_MODE] = clientMode
        msg[FIELD_HOST] = host
        msg[CtxMessages.FIELD_CLOP] = operation

        # Get all connected clients but the logged in one
        clientKey = int(clientKey)

        clientKeys = []
        for client in self.getClients():
            client = int(client)
            if client != clientKey:
                clientKeys.append(client)

        if len(clientKeys) > 0:
            self.messageToClients("CTX", clientKeys, msg)
        LOG("Notify done")

    #===========================================================================
    def notifyExecutorOperation(self,
                                procId,
                                operation,
                                clientKey="",
                                clientMode=""):

        LOG("Notifying executor operation " + repr(operation) + ", " +
            repr(procId))

        # Build the notification message
        msg = MsgHelper.createMessage(CtxMessages.MSG_EXEC_OP)
        msg[CtxMessages.FIELD_PROC_ID] = procId
        msg[CtxMessages.FIELD_EXOP] = operation
        msg[CtxMessages.FIELD_GUI_KEY] = clientKey
        msg[CtxMessages.FIELD_GUI_MODE] = clientMode
        msg[ExcMessages.FIELD_EXEC_STATUS] = self.getExecutor(
            procId).getStatus()
        msg[ExcMessages.FIELD_CONDITION] = self.getExecutor(
            procId).getCondition()
        msg[CtxMessages.FIELD_OPEN_MODE] = self.getExecutor(
            procId).getOpenMode()

        clientKeys = self.getClients()
        LOG("All clients: " + repr(clientKeys))

        # Notify parent procedure also, if it is present
        executor = self.getExecutor(procId)
        if executor:
            parent = executor.getParent()
            if parent is not None:
                clientKeys += [parent]

        LOG("Notifying clients: " + repr(clientKeys))
        if len(clientKeys) > 0:
            self.messageToClients(procId, clientKeys, msg)

        LOG("Notifying executor operation done")

    #===========================================================================
    def executorError(self, procId, msg):
        errorText = "Executor fatal error"
        errorMsg = MsgHelper.createError2(CtxMessages.MSG_EXEC_ERROR, procId,
                                          "CLT", errorText, msg)
        errorMsg[CtxMessages.FIELD_PROC_ID] = procId

        executor = self.getExecutor(procId)
        if executor:
            procClients = executor.getClients()
            self.messageToClients(procId, procClients, errorMsg)
        else:
            LOG("No executor found to notify error", LOG_ERROR)

    #===========================================================================
    def buildExecutorInfo(self, procId, resp):

        executor = self.getExecutor(procId)

        pname = ProcedureManager.instance().getProcedure(procId).name()

        if executor is None:
            txt = "No such executor: " + repr(procId)
            reason = " "
            resp[ExcMessages.FIELD_PROC_ID] = procId
            resp[ExcMessages.FIELD_PARENT_PROC] = " "
            resp[ExcMessages.FIELD_PROC_NAME] = pname
            resp[ExcMessages.FIELD_ASRUN_NAME] = " "
            resp[ExcMessages.FIELD_LOG_NAME] = " "
            resp[ExcMessages.FIELD_EXEC_PORT] = "0"
            resp[ExcMessages.FIELD_EXEC_STATUS] = server.executor.status.UNINIT
            resp[ExcMessages.FIELD_CONDITION] = ""
            resp[ExcMessages.FIELD_GUI_LIST] = " "
            resp[ExcMessages.FIELD_GUI_CONTROL] = " "
            resp[CtxMessages.FIELD_OPEN_MODE] = " "
            resp[ExcMessages.FIELD_LINE] = "0"
            resp[ExcMessages.FIELD_CSP] = procId
        else:
            control = executor.getControllingClient()
            if control is None:
                control = " "

            guiList = ""
            for gui in executor.getMonitoringClients():
                if len(guiList) > 0: guiList = guiList + ","
                guiList = guiList + str(gui)

            resp[ExcMessages.FIELD_PROC_ID] = procId
            resp[ExcMessages.FIELD_PARENT_PROC] = executor.getParent()
            resp[ExcMessages.FIELD_PROC_NAME] = pname
            resp[ExcMessages.FIELD_ASRUN_NAME] = executor.getAsRunFile()
            resp[ExcMessages.FIELD_LOG_NAME] = executor.getLogFile()
            resp[ExcMessages.FIELD_EXEC_PORT] = executor.getPort()
            resp[ExcMessages.FIELD_EXEC_STATUS] = executor.getStatus()
            resp[ExcMessages.FIELD_CONDITION] = executor.getCondition()
            resp[ExcMessages.FIELD_GUI_LIST] = guiList
            resp[ExcMessages.FIELD_GUI_CONTROL] = control
            resp[CtxMessages.FIELD_OPEN_MODE] = executor.getOpenMode()
            resp[ExcMessages.FIELD_CSP] = executor.getStackPosition()

        return resp

    #===========================================================================
    def getFileData(self, procId, logId):
        executor = self.getExecutor(procId)
        if executor is None:
            return resp
        code = " "
        if (logId == ExcMessages.DATA_FILE_ASRUN):
            filename = executor.getAsRunFile()
        else:
            filename = executor.getLogFile()

        lines = []
        if filename and os.path.exists(filename):
            f = file(filename)
            lines = f.readlines()
        return lines

    #===========================================================================
    def getNumActiveProcs(self):
        activeProcs = 0
        for key in self.getExecutors():
            executor = self.getExecutor(key)
            if executor and executor.isActive(): activeProcs += 1
        return activeProcs
Ejemplo n.º 3
0
class ListenerClass(object):
    """
    Main class of the SPEL Listener entity. Listens at a given port waiting for
    GUI connections. Manages the SPEL Context processes on GUI requests.
    """

    # Client key list
    clientKeys = []
    # Context list
    contexts = []
    # Holds contextID / key pairs
    contextKeys = {}
    # Holds contextID / port pairs
    contextPorts = {}
    # Context status (available/running)
    contextStatus = {}
    # GUI operations delegate
    GUI_Operations = None
    # Synchronization issues
    buffer = None
    # IPC context interface
    __ctxIFC = None
    # IPC gui interface
    __guiIFC = None
    # Client lock
    __clientLock = None
    # Context lock
    __ctxLock = None

    #===========================================================================
    def __init__(self, warmStart=False):
        self.openContexts = {}
        self.buffer = Queue(1)
        self.GUI_Operations = GUIOperations(self)
        # Obtain the list of available contexts from cfg file
        self.contexts = Config.instance().getAvailableContexts()
        # Update the context status map (all available)
        for ctx in self.contexts:
            self.contextPorts[ctx] = 0
            self.contextStatus[ctx] = LstMessages.DATA_CTX_AVAILABLE
        self.__ctxIFC = IPCinterfaceServer("LST-CTX")
        self.__guiIFC = IPCinterfaceServer("LST-GUI")
        self.__clientLock = thread.allocate_lock()
        self.__ctxLock = thread.allocate_lock()
        self.__contextRegistry = ProcessRegistry("LST", warmStart)

    #===========================================================================
    def start(self):

        # Get the port number from configuration
        port = Config.instance().getProperty(LISTENER, "ListenerPort")
        LOG("Listening at port: " + str(port), level=LOG_INIT)

        # Setup the GUI io channel
        LOG("Setting up GUI channel", level=LOG_INIT)
        self.__guiIFC.connect(999, int(port), self.processGuiMessage,
                              self.processGuiRequest,
                              self.clientConnectionLost)
        self.__guiIFC.start()

        # Setup the Context io channel
        LOG("Setting up CTX channel", level=LOG_INIT)
        self.__ctxIFC.connect(998, 0, self.processCtxMessage,
                              self.processCtxRequest,
                              self.contextConnectionLost)
        self.__ctxIFC.start()
        LOG("Ready", level=LOG_INIT)

        #Restoring warm context
        for contextInfo in self.__contextRegistry.getProcesses():
            #Only during startup we open contexts in warm mode
            self.openContext(contextInfo.getName(), None, True)

    #===========================================================================
    def stop(self):
        LOG("Stopping, closing contexts", level=LOG_PROC)
        for contextName in self.contexts:
            if self.isContextRunning(contextName):
                self.closeContext(contextName)
        LOG("Killing remaining processes", level=LOG_PROC)
        ProcessManager.instance().killAll()
        self.__guiIFC.disconnect()
        self.__ctxIFC.disconnect()
        LOG("Disconnected", level=LOG_INIT)

    #===========================================================================
    def clientConnectionLost(self, clientKey):
        if clientKey in self.clientKeys:
            LOG("Listener lost connection with client: " + repr(clientKey),
                LOG_ERROR)
            self.clientKeys.remove(clientKey)

    #===========================================================================
    def contextConnectionLost(self, contextKey):
        # Obtain the corresponding name from the key
        for key in self.contextKeys:
            if self.contextKeys[key] == contextKey:
                contextName = key
        originalStatus = self.contextStatus[contextName]
        self.contextStatus[contextName] = LstMessages.DATA_CTX_ERROR
        if not originalStatus == LstMessages.DATA_CTX_STARTING:
            LOG(
                "Listener lost connection with starting context " +
                repr(contextName), LOG_ERROR)
        else:
            LOG("Listener lost connection with context " + repr(contextName),
                LOG_ERROR)
        self.notifyContextCrash(contextName)
        self.clearContext(contextName)
        self.__ctxIFC.disconnect(contextKey, eoc=False)
        LOG("Context-lost done")

    #===========================================================================
    def contextProcessLost(self, contextName, retCode):
        if (retCode == 0) and not contextName in self.contextKeys:
            LOG("Context finished with code 0")
            return
        self.contextStatus[contextName] = LstMessages.DATA_CTX_ERROR
        if contextName in self.contexts:
            LOG("Listener lost track of context " + repr(contextName),
                LOG_ERROR)
            self.notifyContextCrash(contextName)
            self.clearContext(contextName)
            if contextName in self.contextKeys:
                contextKey = self.contextKeys[contextName]
                self.__ctxIFC.disconnect(contextKey, eoc=False)
            LOG("Done")

    #===========================================================================
    def processGuiMessage(self, msg):
        self.__clientLock.acquire()
        LOG("Received GUI message: " + msg.getId(), level=LOG_COMM)
        guiKey = int(msg.getKey())
        if msg.getId() == LstMessages.MSG_GUI_LOGIN:
            LOG("Client logged in: " + str(guiKey), level=LOG_PROC)
            self.clientKeys.append(guiKey)
        elif msg.getId() == LstMessages.MSG_GUI_LOGOUT:
            self.clientKeys.remove(guiKey)
            LOG("Client logged out: " + str(guiKey), level=LOG_PROC)
        else:
            LOG("ERROR: unknown message from client: " + str(msg.getId()),
                LOG_ERROR)
        self.__clientLock.release()

    #===========================================================================
    def processCtxMessage(self, msg):
        self.__ctxLock.acquire()
        LOG("Received Context message: " + msg.getId(), level=LOG_COMM)
        if msg.getId() == LstMessages.MSG_CONTEXT_OPEN:
            contextName = msg[LstMessages.FIELD_CTX_NAME]
            LOG("Context logged in: " + contextName, level=LOG_PROC)
            contextKey = int(msg.getKey())
            ctxPort = msg[LstMessages.FIELD_CTX_PORT]
            self.contextPorts[contextName] = ctxPort
            self.contextStatus[contextName] = LstMessages.DATA_CTX_RUNNING
            self.contextKeys[contextName] = contextKey
            self.buffer.put(ctxPort, True)
        elif msg.getId() == LstMessages.MSG_CONTEXT_CLOSED:
            contextName = msg[LstMessages.FIELD_CTX_NAME]
            contextKey = int(msg.getKey())
            LOG("Context logged out: " + contextName + ":" + repr(contextKey),
                level=LOG_PROC)
            self.contextStatus[contextName] = LstMessages.DATA_CTX_AVAILABLE
            self.buffer.put(contextName, True)
        else:
            LOG("ERROR: unknown message from context:" + str(msg.getId()),
                LOG_ERROR)
        self.__ctxLock.release()

    #===========================================================================
    def processGuiRequest(self, msg):
        self.__clientLock.acquire()
        resp = self.GUI_Operations.processRequest(msg)
        self.__clientLock.release()
        return resp

    #===========================================================================
    def processCtxRequest(self, msg):
        self.__ctxLock.acquire()
        resp = self.createResponse("NONE", msg)
        self.__ctxLock.release()
        return resp

    #===========================================================================
    def openContext(self, contextName, clientKey=None, warm=False):

        ctxScript = Config.instance().getProperty(LISTENER, "ContextScript")
        ctxScript = Config.instance().getHome() + os.sep + ctxScript
        arguments = "-c \"" + Config.instance(
        ).filename + "\" -n \"" + contextName + "\""
        arguments += " -s " + str(self.__ctxIFC.port)
        if warm == True:
            arguments += " -w"
        pythonBin = os.getenv("PYTHON", "python")
        ctxScript = pythonBin + " \"" + ctxScript + "\" " + arguments

        # Set the status as starting (internal)
        self.contextStatus[contextName] = LstMessages.DATA_CTX_STARTING

        LOG("Starting context: '" + contextName + "'", level=LOG_PROC)
        pid = ProcessManager.instance().startProcess(contextName, ctxScript,
                                                     self.contextProcessLost)
        LOG("Context started with pid " + str(pid), level=LOG_PROC)

        LOG("Waiting context port", level=LOG_PROC)
        try:
            ctxPort = self.buffer.get(True, CONTEXT_START_TIMEO)
            LOG("Context port is " + str(ctxPort), level=LOG_PROC)
            # Set the status as started
            self.contextStatus[contextName] = LstMessages.DATA_CTX_RUNNING
            self.notifyContextUpdate(contextName, clientKey)
            self.__contextRegistry.addProcess(pid, contextName)
            return ctxPort
        except BaseException, ex:
            txt = "Failed to open context"
            reason = "Unable to get context listening port"
            LOG(txt + ": " + reason, LOG_ERROR)
            self.killContext(contextName)
            # Set the status as error
            self.contextStatus[contextName] = LstMessages.DATA_CTX_ERROR
            self.notifyContextUpdate(contextName, clientKey, txt, reason)
            return None
Ejemplo n.º 4
0
class ContextClass(object):
    
    # Holds the context name
    ctxName = None
    
    # Holds the listening port for GUIs
    port = None
    # Holds the listener port
    listenerPort = None
    
    # Holds the set of instance numbers for each executor id
    __executorInstanceNumbers = {}
    # Holds the set of executor managers
    __executorManagers = {}
    # Holds the set of clients
    __clientInfo = {}
    
    # GUI operations delegate
    GUI_Operations = None
    
    # Listener operations delegate
    LST_Operations = None

    # Executor operations delegate
    EXC_Operations = None
    
    # IPC interface for clients
    __guiIFC = None
    
    # IPC interface for listener
    __lstIFC = None
    
    # Lock for clients
    __clientLock = None
    
    # Lock for executors
    __execLock = None
    # Lock for context process closure
    __closeLock = None
    
    #Process registry for registering opened executors in a file
    __executorsRegistry = None
    
    #===========================================================================
    def __init__(self, ctxName, listenerPort, warmStart = False):
        LOG("Created context", level = LOG_INIT)
        self.ctxName = ctxName
        self.port = None
        self.__executorInstanceNumbers = {}
        self.__executorManagers = {}
        self.__clientInfo = {}
        self.GUI_Operations = GUIOperations(self)
        self.LST_Operations = LSTOperations(self)
        self.EXC_Operations = EXCOperations(self)
        self.__guiIFC = IPCinterfaceServer("CTX-GUI")
        self.__lstIFC = IPCinterfaceClient("CTX-LST")
        self.listenerPort = listenerPort
        self.__clientLock   = thread.allocate_lock()
        self.__execLock   = thread.allocate_lock()
        self.__closeLock = Queue.Queue(1)
        self.__executorsRegistry = ProcessRegistry("CTX_" + ctxName, warmStart)
        
    #===========================================================================
    def start(self):
        LOG("Loading procedures for this context", level = LOG_INIT)
        ProcedureManager.instance().setup(self.ctxName)

        LOG("Setting up GUI channel", level = LOG_INIT)
        # Setup the Gui io channel
        self.__guiIFC.connect( 888, 0, self.processGuiMessage, self.processGuiRequest, self.guiConnectionLost)
        self.__guiIFC.start()

        LOG("Connecting to listener", level = LOG_INIT)
        # Setup the Listener io channel
        self.__lstIFC.connect("localhost",self.listenerPort, self.processListenerMessage, self.processListenerRequest, self.lstConnectionLost)

        LOG("Using listening port: " + str(self.__guiIFC.port), level = LOG_INIT)
        portMsg = MsgHelper.createMessage(LstMessages.MSG_CONTEXT_OPEN)
        portMsg[LstMessages.FIELD_CTX_NAME] = self.ctxName
        portMsg[LstMessages.FIELD_CTX_PORT] = str(self.__guiIFC.port)
        portMsg.setSender("CTX")
        portMsg.setReceiver("LST")
        self.__lstIFC.sendMessage(portMsg)
        LOG("Ready", level = LOG_INIT)
        sys.stderr.write("*********************************************\n")
        sys.stderr.write("      SPEL Context (" + ctxName + ") Ready \n")
        sys.stderr.write("*********************************************\n")
        
        #for executorInfo in self.__executorRegistry.getProcesses():
        #    #Launch executors stored in warm file
        #    self.openExecutor(executorInfo.getName(), clientKey, clientMode)
        
    #===========================================================================
    def stop(self):
        # Force closing all executors
        LOG("Closing executors", level = LOG_PROC)
        self.closeAll()
        # Disconnect comm interfaces
        LOG("Disconnecting from GUI", level = LOG_PROC)
        self.__guiIFC.disconnect()

        LOG("Logout from listener", level = LOG_PROC)
        logoutMsg = MsgHelper.createMessage(LstMessages.MSG_CONTEXT_CLOSED)
        logoutMsg[LstMessages.FIELD_CTX_NAME] = self.ctxName
        logoutMsg.setSender("CTX")
        logoutMsg.setReceiver("LST")
        self.__lstIFC.sendMessage(logoutMsg)
        self.__lstIFC.disconnect()
        LOG("Disconnected", level = LOG_PROC)
        self.__closeLock.put(1)

    #===========================================================================
    def closeAll(self):
        if len(self.getExecutors())==0:
            LOG("No executors to be closed")
            return
        for procId in self.getExecutors():
            LOG("Close all: " + procId)
            executor = self.getExecutor(procId)
            if executor:
                executor.stop()
                executor.waitForClose()

    #===========================================================================
    def waitForClose(self):
        self.__closeLock.get(True)

    #===========================================================================
    def guiConnectionLost(self, clientKey):
        if clientKey in self.getClients():
            LOG("Context lost connection with client: " + repr(clientKey), LOG_ERROR)
            self.removeClient(clientKey)

    #===========================================================================
    def lstConnectionLost(self, lstKey):
        LOG("Lost connection with listener", LOG_ERROR)
        self.__lstIFC.disconnect( eoc = False )

        LOG("Notifying listener connection lost")
        
        # Build the notification message
        msg = MsgHelper.createMessage(CtxMessages.MSG_LISTENER_LOST)
        msg.setType(MSG_TYPE_ERROR)
        msg[FIELD_ERROR] = "Lost connection with listener"
        msg[FIELD_REASON] = " "
        
        clientKeys = []
        for client in self.getClients():
            clientKeys.append(int(client))
        if len(clientKeys)>0:
            self.messageToClients("CTX", clientKeys, msg)
        LOG("Done")
        return

    #===========================================================================
    def processGuiMessage(self, msg):
        if not self.GUI_Operations.processMessage(msg):
            # If the message is not processed locally, forward it to executors
            procId = msg[ExcMessages.FIELD_PROC_ID]
            # Forward message to the corresponding executor
            clientKey = int(msg.getKey())
            self.messageToExecutor(clientKey, procId, msg)
        return
        
    #===========================================================================
    def processGuiRequest(self, msg):
        LOG("Process GUI request: " + msg.getId(), level = LOG_COMM)
        resp = None
        resp = self.GUI_Operations.processRequest(msg)
        if resp is None:
            # if the request is not processed locally, forward it to executors
            procId = msg[ExcMessages.FIELD_PROC_ID]
            if not procId in self.getExecutors():
                resp = MsgHelper.createError(msg.getId(), msg, "Unable to forward request " + msg.getId(), "No such executor: " + repr(procId))
            else:
                clientKey = int(msg.getKey())
                LOG("Send GUI request to executor: " + msg.getId(), level = LOG_COMM)
                resp = self.requestToExecutor(clientKey, procId, msg)
        LOG("Process GUI request: " + msg.getId() + " finished", level = LOG_COMM)
        return resp
        
    #===========================================================================
    def processListenerMessage(self, msg):
        self.LST_Operations.processMessage(msg)

    #===========================================================================
    def processListenerRequest(self, msg):
        resp = self.LST_Operations.processRequest(msg)
        return resp

    #===========================================================================
    def processExecutorMessage(self, msg):
        self.EXC_Operations.processMessage(msg)

    #===========================================================================
    def processExecutorRequest(self, msg):
        resp = self.EXC_Operations.processRequest(msg)
        return resp

    #===========================================================================
    def messageToClients(self, procId, clientKeys, msg):
        for clientKey in clientKeys:
            LOG("Forwarding message " + msg.getId() + " from executor " + procId + " to client "+ repr(clientKey), level = LOG_COMM)
            # If the client key is a procedure id, it means the request is to
            # be sent to another procedure
            msg.setSender(procId)
            if type(clientKey)==int:
                msg.setReceiver("GUI-" + str(clientKey))
                resp = self.__guiIFC.sendMessage(msg,clientKey)
            else:
                parentProc = clientKey
                msg.setReceiver(parentProc)
                resp = self.messageToExecutor( procId, parentProc, msg)

    #===========================================================================
    def requestToClients(self, procId, clientKeys, msg):
        LOG("Forward executor " + procId + " request " + msg.getId() + " to clients", level = LOG_COMM)
        firstResp = None
        if len(clientKeys)==0:
            LOG("No clients to attend request. Discarding it", LOG_WARN, level = LOG_COMM)
            firstResp = MsgHelper.createError(msg.getId(), msg, "Cannot dispatch request", "No clients connected")
        for clientKey in clientKeys:
            msg.setSender(procId)
            # If the client key is a procedure id, it means the request is to
            # be sent to another procedure
            if type(clientKey)==int:
                msg.setReceiver("GUI-" + str(clientKey))
                resp = self.__guiIFC.forwardRequest(msg,clientKey)
            else:
                parentProc = clientKey
                msg.setReceiver(parentProc)
                resp = self.requestToExecutor( procId, parentProc, msg)
            if firstResp is None: firstResp = resp
        LOG("Request to clients '" + msg.getId() + "' finished", level = LOG_COMM)
        return firstResp

    #===========================================================================
    def messageToExecutor(self, clientKey, procId, msg):
        if procId in self.getExecutors():
            exc = self.getExecutor(procId)
            if exc: exc.messageToExecutor(clientKey,msg)

    #===========================================================================
    def requestToExecutor(self, clientKey, procId, msg):
        LOG("Forward client " + repr(clientKey) + " request " + msg.getId() + " to executor " + procId, level = LOG_COMM)
        resp = None
        if procId in self.getExecutors():
            exc = self.getExecutor(procId)
            if exc: resp = exc.requestToExecutor(clientKey,msg)
        if resp is None:
            resp = MsgHelper.createError(msg.getId(), msg, "Unable to forward request", "No such executor")
        LOG("Request to executor " + msg.getId() + " finished", level = LOG_COMM)
        return resp

    #===========================================================================
    def createExecutor(self, procId):
        self.__execLock.acquire()
        try:
            LOG("Creating executor manager for " + repr(procId))
            manager = ExecutorManagerClass(self,procId)
            self.__executorManagers[procId] = manager
            LOG("Manager created")
        finally:
            self.__execLock.release()
        return manager

    #===========================================================================
    def getInstanceId(self, procId):
        self.__execLock.acquire()
        try:
            instance = 0
            LOG("Obtain available instance for " + repr(procId))
            if procId in self.__executorInstanceNumbers:
                for i in range(0,50):
                    if not i in self.__executorInstanceNumbers[procId]:
                        instance = i
                        break
                self.__executorInstanceNumbers[procId].append(instance)
            else:
                self.__executorInstanceNumbers[procId] = [instance]
            procId = procId + "#" + str(instance)
            LOG("Instance is " + repr(procId))
        finally:
            self.__execLock.release()
        return procId
        
    #===========================================================================
    def openExecutor(self, procId, clientKey, clientMode, openMode = {},
                     arguments = None, condition = None, parentId = None):
        ctxInfo = Config.instance().getContextConfig(self.ctxName)
        driverName = ctxInfo.getDriver()
        maxProcs = int(Config.instance().getDriverConfig(driverName).getMaxProcs())
        if maxProcs>0:
            activeProcs = self.getNumActiveProcs()
            if (activeProcs >= maxProcs):
                raise ContextError("Could not launch executor. Maximum number of processes reached (" + str(maxProcs) + ")")
        
        success = False
        LOG("Requested opening executor " + repr(procId), level = LOG_PROC)
        executor = self.createExecutor(procId)
        
        # Update the proc id with the instance number
        procId = executor.getProcId()
        
        # Get configuration defaults for open mode and update it
        #TODO: get open mode defaults from config
        useOpenMode = {Automatic:False,Visible:True,Blocking:True}
        useOpenMode.update(openMode)

        # Set the controlling client key if the procedure is visible
        if useOpenMode[Visible] == True:
            clientInfo = self.getClient(clientKey)
            executor.addClient(clientInfo, True)
            clientInfo.addExecutor(procId) 
            clientInfo.setMode(clientMode)
        
        LOG("Launching executor " + repr(procId) + " in mode " + repr(useOpenMode), level = LOG_PROC)

        if arguments:
            executor.setArguments(arguments)
        if condition:
            executor.setCondition(condition)
        if parentId:
            executor.setParent(parentId)
            
        # Set the open mode and open the executor
        executor.setOpenMode(useOpenMode)
        
        executor.start()
        success = executor.waitForOpen()
        LOG("Executor launched (" + repr(success) + ")", level = LOG_PROC)

        if not success:
            error,reason = executor.getError()
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_CLOSE)
            self.killExecutor(procId)
            self.clearExecutor(procId)
            raise ContextError("Could not launch executor",error + ":" + reason)
        else:
            initialStatus = executor.getStatus()
            if initialStatus == server.executor.status.ERROR:
                error,reason = executor.getError()
                self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_CLOSE)
                self.killExecutor(procId)
                self.clearExecutor(procId)
                raise ContextError("Executor failed startup",error + ":" + reason)
            else:
                pid = executor.getExecutorPid()
                self.__executorsRegistry.addProcess(pid,procId)
                
                LOG("Notifying executor open", level = LOG_PROC)
                self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_OPEN, clientKey, clientMode)
                LOG("Open finished")
        return 

    #===========================================================================
    def closeExecutor(self, procId):
        LOG("Requested closing executor " + repr(procId))

        executor = self.getExecutor(procId)
        
        if executor is None:
            raise ContextError("No such executor: " + repr(procId))
        
        LOG("Closing executor")
        executor.stop()
        success = executor.waitForClose()
        LOG("Executor closed (" + repr(success) + ")")
        
        if not success: 
            error,reason = executor.getError()
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_CLOSE)
            self.killExecutor(procId)
            self.clearExecutor(procId)
            raise ContextError("Could not close executor",error + ":" + reason)
        else:
            LOG("Notifying executor close", level = LOG_PROC)
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_CLOSE)
            self.clearExecutor(procId)
            LOG("Close finished")
            
        return 

    #===========================================================================
    def clearExecutor(self, procId):
        LOG("Clearing executor for " + repr(procId))
        
        executor = self.getExecutor(procId)
        
        if executor is None:
            raise ContextError("No such executor: " + repr(procId))
        # Remove executor from client infos
        clients = executor.getClients()
        for client in clients:
            self.getClient(client).delExecutor(procId)
            
        self.__execLock.acquire()
        try:
            # Remove executor manager
            del self.__executorManagers[procId]
            # Remove the corresponding instance from the list
            idx = procId.find("#")
            id = procId[0:idx]
            instance = int(procId[idx+1:])
            # Remove from the registry
            self.__executorsRegistry.removeProcess(procId)
            if id in self.__executorInstanceNumbers:
                self.__executorInstanceNumbers[id].remove(instance)
                if len(self.__executorInstanceNumbers[id])==0:
                    self.__executorInstanceNumbers.pop(id)
        finally:
            self.__execLock.release()
        return
            
    #===========================================================================
    def killExecutor(self, procId):
        LOG("Killing executor for " + repr(procId))
        
        executor = self.getExecutor(procId)
        
        if executor is None:
            raise ContextError("No such executor: " + repr(procId))
        
        executor.stop(True)
        
        self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_KILL)
        return True

    #===========================================================================
    def clientAttachExecutor(self, procId, clientKey, clientMode, firstClient):
        
        executor = self.getExecutor(procId)
        
        if executor is None:
            raise ContextError("No such executor: " + repr(procId))
        
        LOG("Client " + str(clientKey) + " requested attaching executor " + procId)
        clientKey = int(clientKey)
        clientInfo = self.getClient(clientKey)
        if not procId in clientInfo.getExecutors():
            clientInfo.addExecutor(procId) 
            clientInfo.setMode(clientMode)
            
            if not executor.addClient(clientInfo, firstClient):
                raise ContextError("Cannot add client in mode " + repr(clientMode))
                    
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_ATTACH, clientKey, clientMode)

    #===========================================================================
    def clientDetachExecutor(self, procId, clientKey):
        
        executor = self.getExecutor(procId)
        
        if executor is None:
            raise ContextError("No such executor: " + repr(procId))
        
        LOG("Client " + str(clientKey) + " requested detaching executor " + procId)
        clientKey = int(clientKey)
        clientInfo = self.getClient(clientKey)
        if procId in clientInfo.getExecutors():
            self.getClient(clientKey).delExecutor(procId)
            executor.removeClient(clientKey)
            self.notifyExecutorOperation(procId, CtxMessages.DATA_EXOP_DETACH, clientKey)

    #===========================================================================
    def createClient(self, clientKey, host, clientMode):
        self.__clientLock.acquire()
        try:
            self.__clientInfo[clientKey] = ClientInfo(clientKey,host,clientMode)
        finally: 
            self.__clientLock.release()
        return

    #===========================================================================
    def removeClient(self, clientKey):
        # Process orphan executors
        clientInfo = self.getClient(clientKey)
        
        self.__clientLock.acquire()
        try:
            if clientInfo:
                execs = clientInfo.getExecutors()
                # Disconnect the interface
                self.__guiIFC.disconnect(clientKey, eoc = False)
                if len(execs)==0:
                    LOG("No executors linked to client " + repr(clientKey), level = LOG_PROC )
                else:
                    for procId in execs:
                        executor = self.getExecutor(procId)
                        if executor is not None:
                            executor.clientLost(clientKey)
                        else:
                            LOG("Unknown executor:" + procId)
                LOG("Unregistering client: " + repr(clientKey))
                host = self.__clientInfo[clientKey].getHost()
                del self.__clientInfo[clientKey]
        finally:
            self.__clientLock.release()
        return

    #===========================================================================
    def getExecutors(self):
        self.__execLock.acquire()
        keys = []
        try:
            keys = self.__executorManagers.keys()
        finally:
            self.__execLock.release()
        return keys

    #===========================================================================
    def getExecutor(self, procId):
        self.__execLock.acquire()
        exc = None
        try:
            exc = self.__executorManagers.get(procId)
        finally:
            self.__execLock.release()
        return exc

    #===========================================================================
    def getClients(self):
        self.__clientLock.acquire()
        keys = []
        try:
            keys = self.__clientInfo.keys()
        finally:
            self.__clientLock.release()
        return keys

    #===========================================================================
    def getClient(self, clientKey):
        self.__clientLock.acquire()
        clt = None
        try:
            if clientKey == NO_CLIENT: 
                clt = None
            else:
                clt = self.__clientInfo[clientKey]
        finally:
            self.__clientLock.release()
        return clt

    #===========================================================================
    def notifyClientOperation(self, clientKey, clientMode, host, operation):

        LOG("Notifying client operation " + repr(operation) + ", " + repr(clientKey))
        
        # Build the notification message
        msg = MsgHelper.createMessage(CtxMessages.MSG_CLIENT_OP)
        msg[CtxMessages.FIELD_GUI_KEY] = clientKey
        msg[CtxMessages.FIELD_GUI_MODE] = clientMode
        msg[FIELD_HOST] = host
        msg[CtxMessages.FIELD_CLOP] = operation
        
        # Get all connected clients but the logged in one
        clientKey = int(clientKey)

        clientKeys = []
        for client in self.getClients():
            client = int(client)
            if client != clientKey:
                clientKeys.append(client)

        if len(clientKeys)>0:
            self.messageToClients("CTX", clientKeys, msg)
        LOG("Notify done")

    #===========================================================================
    def notifyExecutorOperation(self, procId, operation, clientKey = "", clientMode = "" ):
        
        LOG("Notifying executor operation " + repr(operation) + ", " + repr(procId))
        
        # Build the notification message
        msg = MsgHelper.createMessage(CtxMessages.MSG_EXEC_OP)
        msg[CtxMessages.FIELD_PROC_ID] = procId
        msg[CtxMessages.FIELD_EXOP] = operation
        msg[CtxMessages.FIELD_GUI_KEY] = clientKey
        msg[CtxMessages.FIELD_GUI_MODE] = clientMode
        msg[ExcMessages.FIELD_EXEC_STATUS] = self.getExecutor(procId).getStatus()
        msg[ExcMessages.FIELD_CONDITION] = self.getExecutor(procId).getCondition()
        msg[CtxMessages.FIELD_OPEN_MODE] = self.getExecutor(procId).getOpenMode()

        clientKeys = self.getClients()
        LOG("All clients: " + repr(clientKeys))
        
        # Notify parent procedure also, if it is present
        executor = self.getExecutor(procId)
        if executor:
            parent = executor.getParent()
            if parent is not None:
                clientKeys += [parent]
        
        LOG("Notifying clients: " + repr(clientKeys))
        if len(clientKeys)>0:
            self.messageToClients(procId, clientKeys, msg)
            
        LOG("Notifying executor operation done")

    #===========================================================================
    def executorError(self, procId, msg):
        errorText = "Executor fatal error"  
        errorMsg = MsgHelper.createError2(CtxMessages.MSG_EXEC_ERROR, procId, "CLT", errorText, msg)
        errorMsg[CtxMessages.FIELD_PROC_ID] = procId
        
        executor = self.getExecutor(procId)
        if executor:
            procClients = executor.getClients()
            self.messageToClients(procId, procClients, errorMsg)
        else:
            LOG("No executor found to notify error", LOG_ERROR)
        
    #===========================================================================                
    def buildExecutorInfo(self, procId, resp ):
        
        executor = self.getExecutor(procId)

        pname = ProcedureManager.instance().getProcedure(procId).name()
        
        if executor is None:
            txt = "No such executor: " + repr(procId)
            reason = " "
            resp[ExcMessages.FIELD_PROC_ID] = procId
            resp[ExcMessages.FIELD_PARENT_PROC] = " "
            resp[ExcMessages.FIELD_PROC_NAME] = pname
            resp[ExcMessages.FIELD_ASRUN_NAME] = " "
            resp[ExcMessages.FIELD_LOG_NAME] = " "
            resp[ExcMessages.FIELD_EXEC_PORT] = "0"
            resp[ExcMessages.FIELD_EXEC_STATUS] = server.executor.status.UNINIT
            resp[ExcMessages.FIELD_CONDITION] = ""
            resp[ExcMessages.FIELD_GUI_LIST] = " "
            resp[ExcMessages.FIELD_GUI_CONTROL] = " "
            resp[CtxMessages.FIELD_OPEN_MODE] = " "
            resp[ExcMessages.FIELD_LINE] = "0"
            resp[ExcMessages.FIELD_CSP] = procId
        else:
            control = executor.getControllingClient()
            if control is None:
                control = " "

            guiList = ""
            for gui in executor.getMonitoringClients():
                if len(guiList)>0: guiList = guiList + ","
                guiList = guiList + str(gui)
            
            resp[ExcMessages.FIELD_PROC_ID] = procId
            resp[ExcMessages.FIELD_PARENT_PROC] = executor.getParent()
            resp[ExcMessages.FIELD_PROC_NAME] = pname
            resp[ExcMessages.FIELD_ASRUN_NAME] = executor.getAsRunFile()
            resp[ExcMessages.FIELD_LOG_NAME] = executor.getLogFile()
            resp[ExcMessages.FIELD_EXEC_PORT] = executor.getPort()
            resp[ExcMessages.FIELD_EXEC_STATUS] = executor.getStatus()
            resp[ExcMessages.FIELD_CONDITION] = executor.getCondition()
            resp[ExcMessages.FIELD_GUI_LIST] = guiList
            resp[ExcMessages.FIELD_GUI_CONTROL] = control
            resp[CtxMessages.FIELD_OPEN_MODE] = executor.getOpenMode()
            resp[ExcMessages.FIELD_CSP] = executor.getStackPosition()
            resp[ExcMessages.FIELD_ACTION_LABEL] = executor.getUserActionLabel()
            resp[ExcMessages.FIELD_ACTION_SEV] = executor.getUserActionSeverity()
            aen = executor.getUserActionEnabled()
            if aen:
                resp[ExcMessages.FIELD_ACTION_ENABLED] = "True"
            else:
                resp[ExcMessages.FIELD_ACTION_ENABLED] = "False"
        return resp
    
    #===========================================================================                
    def getFileData(self, procId, logId):
        executor = self.getExecutor(procId)
        if executor is None:
            return resp
        code = " "
        if (logId == ExcMessages.DATA_FILE_ASRUN):
            filename = executor.getAsRunFile()
        else:
            filename = executor.getLogFile()

        lines = []
        if filename and os.path.exists(filename):
            f = file(filename)
            lines = f.readlines()
        return lines

    #===========================================================================                
    def getNumActiveProcs(self):
        activeProcs = 0
        for key in self.getExecutors():
            executor = self.getExecutor(key)
            if executor and executor.isActive(): activeProcs += 1
        return activeProcs