Exemplo n.º 1
0
    def send_unregister_service_event(self):
        """
        Send the unregister event for the service.

        :return: None.
        """
        if not self.client:
            raise DxlException("Client not defined")
        with self.lock:
            # Send the unregister event only if the register event was sent before and TTL has not yet expired.
            current_time = int(time.time())
            last_register_time = self.get_register_time()

            if last_register_time > 0 and (
                    current_time - last_register_time) <= (self.ttl * 60):
                request = Request(destination_topic=_ServiceManager.
                                  DXL_SERVICE_UNREGISTER_REQUEST_CHANNEL)
                request.payload = self.json_unregister_service()
                response = self.client.sync_request(request, timeout=60)
                if response.message_type == Message.MESSAGE_TYPE_ERROR:
                    raise DxlException("Unregister service request timed out")
            else:
                if last_register_time > 0:
                    # pylint: disable=logging-not-lazy
                    logger.info(
                        "TTL expired, unregister service event omitted for " +
                        self.service_type + " (" + self.instance_id + ")")
            info = self.service
            if info:
                info._notify_unregistration_succeeded()
Exemplo n.º 2
0
    def send_unregister_service_event(self):
        """
        Send the unregister event for the service.
        
        :return: None.
        """
        if not self.client:
            raise DxlException("Client not defined")
        # Send the unregister event only if the register event was sent before and TTL has not yet expired.
        current_time = int(time.time())
        last_register_time = self.get_register_time()

        if last_register_time > 0 and (current_time -
                                       last_register_time) <= (self.ttl * 60):
            request = Request(destination_topic=_ServiceManager.
                              DXL_SERVICE_UNREGISTER_REQUEST_CHANNEL)
            request.payload = bytes(self.json_unregister_service())
            response = self.client.sync_request(request, timeout=60)
            if response.message_type != Message.MESSAGE_TYPE_ERROR:
                info = self.get_service()
                if info:
                    info._notify_unregistration_succeeded()
            else:
                raise DxlException("Unregister service request timed out")
        else:
            raise DxlException("Unregister service request timed out")
Exemplo n.º 3
0
    def unregister_service_sync(self, service_req_info, timeout):
        """
        Unregisters (removes) a DXL service from the fabric. The specified
        :class:`dxlclient.service.ServiceRegistrationInfo` instance contains information about the
        service that is to be removed.

        This method will wait for confirmation of the service unregistration for up to the specified timeout
        in seconds. If the timeout is exceeded an exception will be raised.

        See :mod:`dxlclient.service` for more information on DXL services.

        :param service_reg_info: A :class:`dxlclient.service.ServiceRegistrationInfo` instance containing information
            about the service that is to be removed.
        :param timeout: The amount of time (in seconds) to wait for confirmation of the service unregistration.
            If the timeout is exceeded an exception will be raised.
        """
        if self._service_manager:
            if not self.connected:
                raise DxlException("Client is not currently connected")

            if not service_req_info:
                raise ValueError("Undefined service object")

            self._service_manager.remove_service(service_req_info.service_id)
            service_req_info._wait_for_unregistration(timeout=timeout)
Exemplo n.º 4
0
    def add_service(self, service_reg_info):
        """
        Adds the specified service.
        
        :param service: The service to add.
        :return: None.
        """
        if not isinstance(service_reg_info, (type, ServiceRegistrationInfo)):
            raise ValueError("Expected ServiceRegistrationInfo object")

        with self.lock:
            try:
                service_handler = self.services[service_reg_info._service_id]
                raise DxlException("Service already registered")
            except KeyError:
                pass

            # Add service to registry
            service_handler = _ServiceRegistrationHandler(
                self.__client, service_reg_info)
            self.services[service_reg_info._service_id] = service_handler

            # Subscribe channels
            for channel in service_reg_info.topics:
                self.__client.subscribe(channel)
                self.__client.add_request_callback(channel, self)

            if self.__client.connected:
                service_handler.start_timer()
Exemplo n.º 5
0
    def _from_bytes(raw):
        """
        Converts the specified array of bytes to a concrete message instance
        (request, response, error, etc.) and returns it.

        :param raw: {@code list} of bytes.
        :returns: {@link dxlclient.message.Message} object.
        """
        buf = BytesIO(raw)
        buf.seek(0)
        unpacker = msgpack.Unpacker(buf)
        version = unpacker.next()
        message_type = unpacker.next()

        message = None
        if message_type == Message.MESSAGE_TYPE_REQUEST:
            message = Request(destination_topic="")
        elif message_type == Message.MESSAGE_TYPE_ERROR:
            message = ErrorResponse(request=None)
        elif message_type == Message.MESSAGE_TYPE_RESPONSE:
            message = Response(request="")
        elif message_type == Message.MESSAGE_TYPE_EVENT:
            message = Event(destination_topic="")

        if message is not None:
            message._version = version
            message._unpack_message(unpacker)
            return message

        raise DxlException("Unknown message type: " + message_type)
Exemplo n.º 6
0
    def connect(self):
        """
        Attempts to connect the client to the DXL fabric.

        This method does not return until either the client has connected to the fabric or it has exhausted
        the number of retries configured for the client causing an exception to be raised.

        Several attributes are available for controlling the client retry behavior:

        - :attr:`dxlclient.client_config.DxlClientConfig.connect_retries` : The maximum number of connection attempts
          for each :class:`dxlclient.broker.Broker` specified in the :class:`dxlclient.client_config.DxlClientConfig`
        - :attr:`dxlclient.client_config.DxlClientConfig.reconnect_delay` : The initial delay between retry attempts.
          The delay increases ("backs off") as subsequent connection attempts are made.
        - :attr:`dxlclient.client_config.DxlClientConfig.reconnect_back_off_multiplier` : Multiples the current
          reconnect delay by this value on subsequent connect retries. For example, a current delay of 3 seconds
          with a multiplier of 2 would result in the next retry attempt being in 6 seconds.
        - :attr:`dxlclient.client_config.DxlClientConfig.reconnect_delay_random` : A randomness delay percentage
          (between 0.0 and 1.0) that is used to increase the current retry delay by a random amount for the purpose
          of preventing multiple clients from having the same retry pattern
        - :attr:`dxlclient.client_config.DxlClientConfig.reconnect_delay_max` : The maximum delay between retry attempts
        """
        if self.connected:
            raise DxlException("Already connected")

        if self._thread is not None:
            raise DxlException("Already trying to connect")

        # Start the connect thread
        self._start_connect_thread(connect_retries=self.config.connect_retries)

        # Wait for the connect thread to finish
        if self._thread is not None:
            while self._thread.isAlive():
                self._thread.join(1)
            self._thread = None

        # Wait for the callback to be invoked
        with self._connected_lock:
            if not self.connected:
                self._connected_wait_condition.wait(5)

        # Check if we were connected
        if not self.connected:
            raise DxlException("Failed to establish connection")
Exemplo n.º 7
0
    def start_timer(self):
        """
        Starts the TTL timer task.
        
        :return: None.
        """
        if not self.client:
            raise DxlException("Client not defined")

        if self.client.connected and not self.deleted:
            self.ttl_timer = Timer(0, self._timer_callback)
            self.ttl_timer.start()
 def _wait_for_registration_notification(self, wait_time, is_register):
     """
     Waits for a registration notification (register or unregister).
     
     :param waitTime:   The amount of time to wait.
     :param isRegister Whether we are waiting for a register or unregister notification.
     :return: None.
     """
     with self._registration_sync:
         if wait_time > 0:
             self._registration_sync.wait(wait_time)
         else:
             raise DxlException("Timeout waiting for service related notification")
Exemplo n.º 9
0
    def remove_service(self, service_id):
        """
        Removes the specified service.
        
        :param instanceId: The instance ID of the service to remove.
        :return: None.
        """
        if not isinstance(service_id, (type, basestring)):
            raise ValueError("Expected service id")

        if not service_id:
            raise ValueError("Invalid service id")

        with self.lock:
            service_handler = self.services.get(service_id, None)
            if not service_handler:
                raise DxlException("Service instance ID unknown: " +
                                   str(service_id))

            service_handler.stop_timer()

            for channel in service_handler.channels:
                self.__client.unsubscribe(channel)
                self.__client.remove_request_callback(channel, self)

            service_handler.mark_for_deletion()

            #If the client is actually connected, send unregister event. Remove upon success.
            if self.__client.connected:
                try:
                    service_handler.send_unregister_service_event()
                except Exception, ex:
                    logger.error(
                        "Error sending unregister service event for " +
                        service_handler.service_type + " (" +
                        service_handler.instance_id + "): " + str(ex))

            # Remove the service handler from a copy of self.services. This
            # avoids causing issues with any readers using the current value of
            # the object.
            services = self.services.copy()
            del services[service_id]
            service_handler.destroy(unregister=False)
            self.services = services
Exemplo n.º 10
0
    def sync_request(self, request, timeout=_DEFAULT_WAIT):
        """
        Sends a :class:`dxlclient.message.Request` message to a remote DXL service.

        See module :mod:`dxlclient.service` for more information on DXL services.

        :param request: The :class:`dxlclient.message.Request` message to send to a remote DXL service
        :param timeout: The amount of time (in seconds) to wait for the :class:`dxlclient.message.Response`
            to the request. If the timeout is exceeded an exception will be raised. Defaults to ``3600``
            seconds (1 hour)
        """
        if threading.currentThread().name.startswith(
                self._message_pool_prefix):
            raise DxlException(
                "Synchronous requests may not be invoked while handling an incoming message. "
                +
                "The synchronous request must be made on a different thread.")

        return self._request_manager.sync_request(request, timeout)
Exemplo n.º 11
0
    def __init__(self, client, service):
        """
        Constructs the ServiceRegistrationHandler object.

        :param client:  The internal client reference.
        :param service: The service registration info.
        :return: None.
        """
        super(_ServiceRegistrationHandler, self).__init__()

        # The service type or name prefix
        self.service_type = service.service_type
        # The service instance ID
        self.instance_id = service.service_id
        # The list of full qualified registered channels
        self.channels = service.topics
        # The map of meta data associated with this service (name-value pairs)
        self.metadata = service.metadata.copy()
        # The Time-To-Live (TTL) grace period of the service registration (default: 10 minutes)
        self.ttl_grace_period = 10
        # The service registration info */
        self.service = service
        # The internal client reference */
        self.client = client
        # The request callback manager */
        self.request_callbacks = _RequestCallbackManager()
        self.deleted = False
        self.ttl_timer = None
        self.register_time = 0
        self.ttl = service.ttl
        self.lock = RLock()

        self._destroy_lock = RLock()
        self._destroyed = False

        service_ref = self.service
        if not service_ref:
            raise DxlException("Service no longer valid")

        for channel, callbacks in iter_dict_items(
                service_ref._callbacks_by_topic):
            for callback in callbacks:
                self.request_callbacks.add_callback(channel, callback)
    def send_register_service_request(self):
        """
        Send the registration request for the service.
        
        :return: None.
        """
        if not self.client:
            raise DxlException("Client not defined")
        req = Request(destination_topic=_ServiceManager.DXL_SERVICE_REGISTER_REQUEST_CHANNEL)
        req.payload = bytes(self.json_register_service())
        response = self.client.sync_request(req, timeout=10)

        if response.message_type != Message.MESSAGE_TYPE_ERROR:
            self.update_register_time()
            info = self.get_service()
            if info:
                info._notify_registration_succeeded()
        else:
            # TODO: Notify the client with an exception if an error occurred, so that it doesn't wait for timeout
            logger.error("Error registering service.")
Exemplo n.º 13
0
    def subscribe(self, topic):
        """
        Subscribes to the specified topic on the DXL fabric. This method is typically used in
        conjunction with the registration of :class:`dxlclient.callbacks.EventCallback` instances
        via the :func:`add_event_callback` method.

        The following is a simple example of using this:

        .. code-block:: python

            from dxlclient.callbacks import EventCallback

            class MyEventCallback(EventCallback):
                def on_event(self, event):
                    print "Received event! " + event.source_client_id

            dxl_client.add_event_callback("/testeventtopic", MyEventCallback(), False)
            dxl_client.subscribe("/testeventtopic")

        **NOTE:** By default when registering an event callback the client will automatically subscribe to the topic.
        In this example the :func:`dxlclient.client.DxlClient.add_event_callback` method is invoked with the
        ``subscribe_to_topic`` parameter set to ``False`` preventing the automatic subscription.

        :param topic: The topic to subscribe to
        """
        logger.debug("%s(): Waiting for Subscriptions lock...",
                     DxlUtils.func_name())
        self._subscriptions_lock.acquire()
        try:
            if topic not in self._subscriptions:
                self._subscriptions.add(topic)
                if self.connected:
                    self._client.subscribe(topic)
        except Exception as ex:
            logger.error("Error during subscribe: %s", ex.message)
            logger.debug(traceback.format_exc())
            raise DxlException("Error during subscribe" + str(ex))
        finally:
            logger.debug("%s(): Releasing Subscriptions lock.",
                         DxlUtils.func_name())
            self._subscriptions_lock.release()
Exemplo n.º 14
0
    def unsubscribe(self, topic):
        """
        Unsubscribes from the specified topic on the DXL fabric.

        See the :func:`subscribe` method for more information on subscriptions.

        :param topic: The topic to unsubscribe from
        """
        logger.debug("%s(): Waiting for Subscriptions lock...",
                     DxlUtils.func_name())
        self._subscriptions_lock.acquire()
        try:
            if topic in self._subscriptions:
                if self.connected:
                    self._client.unsubscribe(topic)
        except Exception as ex:
            logger.error("Error during unsubscribe: %s", ex.message)
            logger.debug(traceback.format_exc())
            raise DxlException("Error during unsubscribe")
        finally:
            self._subscriptions.remove(topic)
            logger.debug("%s(): Releasing Subscriptions lock.",
                         DxlUtils.func_name())
            self._subscriptions_lock.release()