def __init__(self, callback, connectionData, debugMode=False): ''' Initializes the state of the endpoint. Keyword arguments: callback -- the object that will process the incoming SSAP messages. connectionData -- the object that stores the configuration of the websocket connection. debugMode -- a flag that enables additional debug messages. ''' SSAPEndpoint.__init__(self, callback) if (debugMode) : logLevel = logging.DEBUG else: logLevel = logging.INFO self.__logger = LogFactory.configureLogger(self, logLevel, LogFactory.DEFAULT_LOG_FILE) self.__queue = GenericThreadSafeList() self.__websocket = None self.__connectionData = connectionData self.__activeSubscriptions = 0
class WebsocketBasedSSAPEndpoint(SSAPEndpoint): '''A websocket-based SSAP endpoint''' def __init__(self, callback, connectionData, debugMode=False): ''' Initializes the state of the endpoint. Keyword arguments: callback -- the object that will process the incoming SSAP messages. connectionData -- the object that stores the configuration of the websocket connection. debugMode -- a flag that enables additional debug messages. ''' SSAPEndpoint.__init__(self, callback) if (debugMode) : logLevel = logging.DEBUG else: logLevel = logging.INFO self.__logger = LogFactory.configureLogger(self, logLevel, LogFactory.DEFAULT_LOG_FILE) self.__queue = GenericThreadSafeList() self.__websocket = None self.__connectionData = connectionData self.__activeSubscriptions = 0 def __sendSSAPRequest(self, messageType, ssapRequest, checkWebsocket=True): ''' Prepares a SSAP message to be sent to the SIB. Keyworkd arguments: messageType -- the type of the SSAP message to send. ssapRequest -- the serialized SSAP message to send. checkWebsocket -- indicates if we must check wether the connection is ready or not. ''' if checkWebsocket : self.__checkIfWebsocketIsInstantiated() self.__appendRequest(_SSAPRequest(messageType, ssapRequest)) def joinWithToken(self, token, instance): self._token = token self._instance = instance self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.JOIN, _SSAPMessageFactory.buildTokenBasedJoinMessage(token, instance), False) def leave(self): if (self.__activeSubscriptions != 0): self.__logger.warning("There are active subscriptions. You should cancel them before disconnecting from the SIB") self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.LEAVE, _SSAPMessageFactory.buildLeaveMessage(self._sessionKey)) def renovateSessionKey(self): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.JOIN, _SSAPMessageFactory.buildRenewSessionKeyJoinMessage(self._token, self._instance, self._sessionKey)) def insert(self, ontology, data, queryType=SSAP_QUERY_TYPE.NATIVE): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.INSERT, _SSAPMessageFactory.buildInsertMessage(ontology, data, queryType, self._sessionKey)) def query(self, ontology, query, queryType=SSAP_QUERY_TYPE.NATIVE, queryParams = None): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.QUERY, _SSAPMessageFactory.buildQueryMessage(ontology, query, queryType, queryParams, self._sessionKey)) def update(self, ontology, query, data, queryType=SSAP_QUERY_TYPE.NATIVE): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.UPDATE, _SSAPMessageFactory.buildUpdateMessage(ontology, query, queryType, data, self._sessionKey)) def delete(self, ontology, query, queryType=SSAP_QUERY_TYPE.NATIVE): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.DELETE, _SSAPMessageFactory.buildDeleteMessage(ontology, query, queryType, self._sessionKey)) def subscribe(self, ontology, query, queryType=SSAP_QUERY_TYPE.NATIVE, refreshTimeInMillis=1000): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.SUBSCRIBE, _SSAPMessageFactory.buildSubscribeMessage(ontology, query, queryType, refreshTimeInMillis, self._sessionKey)) def unsubscribe(self, subscriptionId): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.UNSUBSCRIBE, _SSAPMessageFactory.buildUnsubscribeMessage(subscriptionId, self._sessionKey)) def config(self, kpName, kpInstance, token, assetService, assetServiceParam): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.CONFIG, _SSAPMessageFactory.buildConfigMessage(kpName, kpInstance, token, assetService, assetServiceParam), False) # def bulk(self, ontology, ssapBulkRequest): # self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.BULK, # _SSAPMessageFactory.buildBulkMessage(ssapBulkRequest, ontology, self._sessionKey)) def waitForever(self): if (self.__websocket is None) : raise InvalidSSAPOperation("The connection with the SIB is not established") self.__websocket.run_forever() def __appendRequest(self, request): ''' Queues a send request in the output message queue. Keyword arguments: request -- the request to queue. ''' self.__queue.append(request) if (self.__queue.getSize() == 1): self.__sendNextRequestToSib() def __checkIfWebsocketIsInstantiated(self): ''' Checks if the websocket has been instantiated. This allows us to detect invalid API invocations. ''' if (self.__websocket is None): raise InvalidSSAPOperation("The connection with the SIB has not been established yet") def __sendNextRequestToSib(self): ''' Pops a SSAP message request from the output queue and sends it to the SIB. ''' if (self.__websocket is None): self.__openConnection() request = self.__queue[0] self.__websocket.send(request.getQuery(), False) def __openConnection(self): ''' Establishes a websocket-based connection with the SIB ''' if (not self.__websocket is None) : raise InvalidSSAPOperation("The connection with the SIB has already been established") try : self.__websocket = _SSAPWebsocketClient(self.__connectionData.getServerUrl(), self.__connectionData.getProtocols(), self.__onConnectionEvent, self.__onDataReceived) self.__websocket.connect() self.__waitUntilConnectionEstablished() except Exception as ws4pyException: self._clearStateData() raise SSAPConnectionError("Couldn't connect to the SIB: " + str(ws4pyException)) def __waitUntilConnectionEstablished(self): ''' Waits for the websocket connection to the SIB to be established. ''' self.__logger.info("Waiting for the websocket connection to be established") while not self.__connection_established : sleep(1) def __onConnectionEvent(self): ''' This method is invoked from the ws4py library when a websocket connection is established or closed. ''' self.__connection_established = True def __onDataReceived(self, data): ''' This method is invokef from the ws4py library when data is received from the websocket. Keyword arguments: data -- the received data. ''' if (len(data.data) == 1): return # We might receive some shit after closing the connection. We won't process it. self.__logger.debug("Data received: " + data.data) parsed_message = _SSAPMessageParser.parse(data.data) # The message content can be modified within the callback. We must copy # everything we need before invoking it. messageType = parsed_message["messageType"] noErrors = parsed_message["messageType"] != SSAP_MESSAGE_TYPE.INDICATION and parsed_message["body"]["ok"] if (noErrors and messageType == SSAP_MESSAGE_TYPE.JOIN): self._sessionKey = parsed_message["sessionKey"] self._callback.onSSAPMessageReceived(parsed_message) if (messageType != SSAP_MESSAGE_TYPE.INDICATION) : self.__queue.pop() if (noErrors) : if (messageType == SSAP_MESSAGE_TYPE.LEAVE): self.__closeConnection() self._clearStateData() self.__websocket = None elif (messageType == SSAP_MESSAGE_TYPE.SUBSCRIBE): self.__activeSubscriptions = self.__activeSubscriptions + 1 elif (messageType == SSAP_MESSAGE_TYPE.UNSUBSCRIBE): self.__activeSubscriptions = self.__activeSubscriptions - 1 if (self.__queue.getSize() != 0) : self.__sendNextRequestToSib() def __closeConnection(self): ''' Closes the connection. ''' if (self.__websocket is None) : raise InvalidSSAPOperation("The connection with the SIB is not established") self._clearStateData() self.__websocket.close() self.__websocket = None def _clearStateData(self): ''' Deletes all the state data stored in the endpoint (i.e. session keys, ...). ''' SSAPEndpoint._clearStateData(self) self.__connection_established = False
class WebsocketBasedSSAPEndpoint(SSAPEndpoint): '''A websocket-based SSAP endpoint''' def __init__(self, callback, connectionData, debugMode=False): ''' Initializes the state of the endpoint. Keyword arguments: callback -- the object that will process the incoming SSAP messages. connectionData -- the object that stores the configuration of the websocket connection. debugMode -- a flag that enables additional debug messages. ''' SSAPEndpoint.__init__(self, callback) if (debugMode) : logLevel = logging.DEBUG else: logLevel = logging.INFO self.__logger = LogFactory.configureLogger(self, logLevel, LogFactory.DEFAULT_LOG_FILE) self.__queue = GenericThreadSafeList() self.__websocket = None self.__connectionData = connectionData self.__activeSubscriptions = 0 def __sendSSAPRequest(self, messageType, ssapRequest, checkWebsocket=True): ''' Prepares a SSAP message to be sent to the SIB. Keyworkd arguments: messageType -- the type of the SSAP message to send. ssapRequest -- the serialized SSAP message to send. checkWebsocket -- indicates if we must check wether the connection is ready or not. ''' if checkWebsocket : self.__checkIfWebsocketIsInstantiated() self.__appendRequest(_SSAPRequest(messageType, ssapRequest)) def joinWithToken(self, token, instance): self._token = token self._instance = instance self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.JOIN, _SSAPMessageFactory.buildTokenBasedJoinMessage(token, instance), False) def leave(self): if (self.__activeSubscriptions != 0): self.__logger.warning("There are active subscriptions. You should cancel them before disconnecting from the SIB") self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.LEAVE, _SSAPMessageFactory.buildLeaveMessage(self._sessionKey)) def renovateSessionKey(self): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.JOIN, _SSAPMessageFactory.buildRenewSessionKeyJoinMessage(self._token, self._instance, self._sessionKey)) def insert(self, ontology, data, queryType=SSAP_QUERY_TYPE.NATIVE): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.INSERT, _SSAPMessageFactory.buildInsertMessage(ontology, data, queryType, self._sessionKey)) def query(self, ontology, query, queryType=SSAP_QUERY_TYPE.NATIVE, queryParams = None): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.QUERY, _SSAPMessageFactory.buildQueryMessage(ontology, query, queryType, queryParams, self._sessionKey)) def update(self, ontology, query, data, queryType=SSAP_QUERY_TYPE.NATIVE): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.UPDATE, _SSAPMessageFactory.buildUpdateMessage(ontology, query, queryType, data, self._sessionKey)) def delete(self, ontology, query, queryType=SSAP_QUERY_TYPE.NATIVE): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.DELETE, _SSAPMessageFactory.buildDeleteMessage(ontology, query, queryType, self._sessionKey)) def subscribe(self, ontology, query, queryType=SSAP_QUERY_TYPE.NATIVE, refreshTimeInMillis=1000): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.SUBSCRIBE, _SSAPMessageFactory.buildSubscribeMessage(ontology, query, queryType, refreshTimeInMillis, self._sessionKey)) def unsubscribe(self, subscriptionId): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.UNSUBSCRIBE, _SSAPMessageFactory.buildUnsubscribeMessage(subscriptionId, self._sessionKey)) def config(self, kpName, kpInstance, token, assetService, assetServiceParam): self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.CONFIG, _SSAPMessageFactory.buildConfigMessage(kpName, kpInstance, token, assetService, assetServiceParam), False) # def bulk(self, ontology, ssapBulkRequest): # self.__sendSSAPRequest(SSAP_MESSAGE_TYPE.BULK, # _SSAPMessageFactory.buildBulkMessage(ssapBulkRequest, ontology, self._sessionKey)) def waitForever(self): if (self.__websocket is None) : raise InvalidSSAPOperation("The connection with the SIB is not established") self.__websocket.run_forever() def __appendRequest(self, request): ''' Queues a send request in the output message queue. Keyword arguments: request -- the request to queue. ''' self.__queue.append(request) if (self.__queue.getSize() == 1): self.__sendNextRequestToSib() def __checkIfWebsocketIsInstantiated(self): ''' Checks if the websocket has been instantiated. This allows us to detect invalid API invocations. ''' if (self.__websocket is None): raise InvalidSSAPOperation("The connection with the SIB has not been established yet") def __sendNextRequestToSib(self): ''' Pops a SSAP message request from the output queue and sends it to the SIB. ''' if (self.__websocket is None): self.__openConnection() request = self.__queue[0] self.__websocket.send(request.getQuery(), False) def __openConnection(self): ''' Establishes a websocket-based connection with the SIB ''' if (not self.__websocket is None) : raise InvalidSSAPOperation("The connection with the SIB has already been established") try : self.__websocket = _SSAPWebsocketClient(self.__connectionData.getServerUrl(), self.__connectionData.getProtocols(), self.__onConnectionEvent, self.__onDataReceived) self.__websocket.connect() self.__waitUntilConnectionEstablished() except Exception as ws4pyException: self._clearStateData() raise SSAPConnectionError("Couldn't connect to the SIB: " + str(ws4pyException)) def __waitUntilConnectionEstablished(self): ''' Waits for the websocket connection to the SIB to be established. ''' self.__logger.info("Waiting for the websocket connection to be established") while not self.__connection_established : sleep(1) def __onConnectionEvent(self): ''' This method is invoked from the ws4py library when a websocket connection is established or closed. ''' self.__connection_established = True def __onDataReceived(self, data): ''' This method is invokef from the ws4py library when data is received from the websocket. Keyword arguments: data -- the received data. ''' if (len(data.data) == 1): return # We might receive some shit after closing the connection. We won't process it. self.__logger.debug("Data received: ") self.__logger.debug(data.data) parsed_message = _SSAPMessageParser.parse(data.data) # The message content can be modified within the callback. We must copy # everything we need before invoking it. messageType = parsed_message["messageType"] noErrors = parsed_message["messageType"] != SSAP_MESSAGE_TYPE.INDICATION and parsed_message["body"]["ok"] if (noErrors and messageType == SSAP_MESSAGE_TYPE.JOIN): self._sessionKey = parsed_message["sessionKey"] self._callback.onSSAPMessageReceived(parsed_message) if (messageType != SSAP_MESSAGE_TYPE.INDICATION) : self.__queue.pop() if (noErrors) : if (messageType == SSAP_MESSAGE_TYPE.LEAVE): self.__closeConnection() self._clearStateData() self.__websocket = None elif (messageType == SSAP_MESSAGE_TYPE.SUBSCRIBE): self.__activeSubscriptions = self.__activeSubscriptions + 1 elif (messageType == SSAP_MESSAGE_TYPE.UNSUBSCRIBE): self.__activeSubscriptions = self.__activeSubscriptions - 1 if (self.__queue.getSize() != 0) : self.__sendNextRequestToSib() def __closeConnection(self): ''' Closes the connection. ''' if (self.__websocket is None) : raise InvalidSSAPOperation("The connection with the SIB is not established") self._clearStateData() self.__websocket.close() self.__websocket = None def _clearStateData(self): ''' Deletes all the state data stored in the endpoint (i.e. session keys, ...). ''' SSAPEndpoint._clearStateData(self) self.__connection_established = False