Example #1
0
    def __init__(self, ordered=False):
        # flag indicating whether observers should be maintained in a SortedSet
        # or a regular set (unordered)
        self._ordered = ordered

        # map: URI => ExactUriObservation
        self._observations_exact = {}

        # map: URI => PrefixUriObservation
        self._observations_prefix = StringTrie()

        # map: URI => WildcardUriObservation
        self._observations_wildcard = WildcardTrieMatcher()

        # map: observation ID => UriObservation
        self._observation_id_to_observation = {}
Example #2
0
    def __init__(self, ordered=False):
        # flag indicating whether observers should be maintained in a SortedSet
        # or a regular set (unordered)
        self._ordered = ordered

        # map: URI => ExactUriObservation
        self._observations_exact = {}

        # map: URI => PrefixUriObservation
        self._observations_prefix = StringTrie()

        # map: URI => WildcardUriObservation
        self._observations_wildcard = WildcardTrieMatcher()

        # map: observation ID => UriObservation
        self._observation_id_to_observation = {}
Example #3
0
    def __init__(self, ordered=False):
        # flag indicating whether observers should be maintained in a SortedSet
        # or a regular set (unordered)
        self._ordered = ordered

        # map: URI => ExactUriObservation
        self._observations_exact = {}

        # map: URI => PrefixUriObservation
        self._observations_prefix = StringTrie()

        # map: URI => WildcardUriObservation
        if True:
            # use a Trie-based implementation (supposed to be faster, but
            # otherwise compatible to the naive implementation below)
            self._observations_wildcard = WildcardTrieMatcher()
        else:
            self._observations_wildcard = WildcardMatcher()

        # map: observation ID => UriObservation
        self._observation_id_to_observation = {}
Example #4
0
class UriObservationMap(object):
    """
    Represents the current set of observations maintained by a broker/dealer.

    To test: trial crossbar.router.test.test_subscription
    """
    def __init__(self, ordered=False):
        # flag indicating whether observers should be maintained in a SortedSet
        # or a regular set (unordered)
        self._ordered = ordered

        # map: URI => ExactUriObservation
        self._observations_exact = {}

        # map: URI => PrefixUriObservation
        self._observations_prefix = StringTrie()

        # map: URI => WildcardUriObservation
        self._observations_wildcard = WildcardTrieMatcher()

        # map: observation ID => UriObservation
        self._observation_id_to_observation = {}

    def add_observer(self, observer, uri, match=u"exact", extra=None):
        """
        Adds a observer to the observation set and returns the respective observation.

        :param observer: The observer to add (this can be any opaque object).
        :type observer: obj
        :param uri: The URI (or URI pattern) to add the observer to add to.
        :type uri: unicode
        :param match: The matching policy for observing, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``.
        :type match: unicode

        :returns: A tuple ``(observation, was_already_observed, was_first_observer)``. Here,
            ``observation`` is an instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``.
        :rtype: tuple
        """
        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(
                type(uri).__name__))

        if match == u"exact":

            # if the exact-matching URI isn't in our map, create a new observation
            #
            if uri not in self._observations_exact:
                self._observations_exact[uri] = ExactUriObservation(
                    uri, ordered=self._ordered, extra=extra)
                is_first_observer = True
            else:
                is_first_observer = False

            # get the observation
            #
            observation = self._observations_exact[uri]

        elif match == u"prefix":

            # if the prefix-matching URI isn't in our map, create a new observation
            #
            if uri not in self._observations_prefix:
                self._observations_prefix[uri] = PrefixUriObservation(
                    uri, ordered=self._ordered, extra=extra)
                is_first_observer = True
            else:
                is_first_observer = False

            # get the observation
            #
            observation = self._observations_prefix[uri]

        elif match == u"wildcard":

            # if the wildcard-matching URI isn't in our map, create a new observation
            #
            if uri not in self._observations_wildcard:
                self._observations_wildcard[uri] = WildcardUriObservation(
                    uri, ordered=self._ordered, extra=extra)
                is_first_observer = True
            else:
                is_first_observer = False

            # get the observation
            #
            observation = self._observations_wildcard[uri]

        else:
            raise Exception("invalid match strategy '{}'".format(match))

        # note observation in observation ID map
        #
        if is_first_observer:
            self._observation_id_to_observation[observation.id] = observation

        # add observer if not already in observation
        #
        if observer not in observation.observers:
            observation.observers.add(observer)
            was_already_observed = False
        else:
            was_already_observed = True

        return observation, was_already_observed, is_first_observer

    def get_observation(self, uri, match=u"exact"):
        """
        Get a observation (if any) for given URI and match policy.

        :param uri: The URI (or URI pattern) to get the observation for.
        :type uri: unicode
        :param match: The matching policy for observation to retrieve, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``.
        :type match: unicode

        :returns: The observation (instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``)
            or ``None``.
        :rtype: obj or None
        """

        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(
                type(uri).__name__))

        if match == u"exact":
            return self._observations_exact.get(uri, None)

        elif match == u"prefix":
            return self._observations_prefix.get(uri, None)

        elif match == u"wildcard":
            return self._observations_wildcard.get(uri, None)

        else:
            raise Exception("invalid match strategy '{}'".format(match))

    def match_observations(self, uri):
        """
        Returns the observations matching the given URI. This is the core method called
        by a broker to actually dispatch events.

        :param uri: The URI to match.
        :type uri: unicode

        :returns: A list of observations matching the URI. This is a list of instance of
            one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``.
        :rtype: list
        """
        observations = []

        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(
                type(uri).__name__))

        if uri in self._observations_exact:
            observations.append(self._observations_exact[uri])

        for observation in self._observations_prefix.iter_prefix_values(uri):
            observations.append(observation)

        for observation in self._observations_wildcard.iter_matches(uri):
            observations.append(observation)

        return observations

    def best_matching_observation(self, uri):
        """
        Returns the observation that best matches the given URI. This is the core method called
        by a dealer to actually forward calls.

        :param uri: The URI to match.
        :type uri: unicode

        :returns: The observation best matching the URI. This is an instance of
            ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` or ``None``.
        :rtype: obj or None
        """
        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(
                type(uri).__name__))

        # a exact matching observation is always "best", if any
        #
        if uri in self._observations_exact:
            return self._observations_exact[uri]

        # "second best" is the longest prefix-matching observation, if any
        # FIXME: do we want this to take precedence over _any_ wildcard (see below)?
        #
        try:
            return self._observations_prefix.longest_prefix_value(uri)
        except KeyError:
            pass

        # FIXME: for wildcard observations, when there are multiple matching, we'd
        # like to deterministically select the "most selective one"
        # We first need a definition of "most selective", and then we need to implement
        # this here.
        #
        for observation in self._observations_wildcard.iter_matches(uri):
            return observation

    def get_observation_by_id(self, id):
        """
        Get a observation by ID.

        :param id: The ID of the observation to retrieve.
        :type id: int

        :returns: The observation for the given ID or ``None``.
        :rtype: obj or None
        """
        return self._observation_id_to_observation.get(id, None)

    def drop_observer(self, observer, observation):
        """
        Drop a observer from a observation.

        :param observer: The observer to drop from the given observation.
        :type observer: obj
        :param observation: The observation from which to drop the observer. An instance
            of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` previously
            created and handed out by this observation map.
        :type observation: obj

        :returns: A tuple ``(was_observed, was_last_observer)``.
        :rtype: tuple
        """
        if observer in observation.observers:

            was_observed = True

            # remove observer from observation
            #
            observation.observers.discard(observer)

            # no more observers on this observation!
            #
            if not observation.observers:

                if observation.match == u"exact":
                    del self._observations_exact[observation.uri]

                elif observation.match == u"prefix":
                    del self._observations_prefix[observation.uri]

                elif observation.match == u"wildcard":
                    del self._observations_wildcard[observation.uri]

                else:
                    # should not arrive here
                    raise Exception("logic error")

                was_last_observer = True

                del self._observation_id_to_observation[observation.id]

            else:
                was_last_observer = False

        else:
            # observer wasn't on this observation
            was_observed = False

        return was_observed, was_last_observer
Example #5
0
class UriObservationMap(object):

    """
    Represents the current set of observations maintained by a broker/dealer.

    To test: trial crossbar.router.test.test_subscription
    """

    def __init__(self, ordered=False):
        # flag indicating whether observers should be maintained in a SortedSet
        # or a regular set (unordered)
        self._ordered = ordered

        # map: URI => ExactUriObservation
        self._observations_exact = {}

        # map: URI => PrefixUriObservation
        self._observations_prefix = StringTrie()

        # map: URI => WildcardUriObservation
        self._observations_wildcard = WildcardTrieMatcher()

        # map: observation ID => UriObservation
        self._observation_id_to_observation = {}

    def add_observer(self, observer, uri, match=u"exact", extra=None):
        """
        Adds a observer to the observation set and returns the respective observation.

        :param observer: The observer to add (this can be any opaque object).
        :type observer: obj
        :param uri: The URI (or URI pattern) to add the observer to add to.
        :type uri: unicode
        :param match: The matching policy for observing, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``.
        :type match: unicode

        :returns: A tuple ``(observation, was_already_observed, was_first_observer)``. Here,
            ``observation`` is an instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``.
        :rtype: tuple
        """
        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__))

        if match == u"exact":

            # if the exact-matching URI isn't in our map, create a new observation
            #
            if uri not in self._observations_exact:
                self._observations_exact[uri] = ExactUriObservation(uri, ordered=self._ordered, extra=extra)
                is_first_observer = True
            else:
                is_first_observer = False

            # get the observation
            #
            observation = self._observations_exact[uri]

        elif match == u"prefix":

            # if the prefix-matching URI isn't in our map, create a new observation
            #
            if uri not in self._observations_prefix:
                self._observations_prefix[uri] = PrefixUriObservation(uri, ordered=self._ordered, extra=extra)
                is_first_observer = True
            else:
                is_first_observer = False

            # get the observation
            #
            observation = self._observations_prefix[uri]

        elif match == u"wildcard":

            # if the wildcard-matching URI isn't in our map, create a new observation
            #
            if uri not in self._observations_wildcard:
                self._observations_wildcard[uri] = WildcardUriObservation(uri, ordered=self._ordered, extra=extra)
                is_first_observer = True
            else:
                is_first_observer = False

            # get the observation
            #
            observation = self._observations_wildcard[uri]

        else:
            raise Exception("invalid match strategy '{}'".format(match))

        # note observation in observation ID map
        #
        if is_first_observer:
            self._observation_id_to_observation[observation.id] = observation

        # add observer if not already in observation
        #
        if observer not in observation.observers:
            observation.observers.add(observer)
            was_already_observed = False
        else:
            was_already_observed = True

        return observation, was_already_observed, is_first_observer

    def get_observation(self, uri, match=u"exact"):
        """
        Get a observation (if any) for given URI and match policy.

        :param uri: The URI (or URI pattern) to get the observation for.
        :type uri: unicode
        :param match: The matching policy for observation to retrieve, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``.
        :type match: unicode

        :returns: The observation (instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``)
            or ``None``.
        :rtype: obj or None
        """

        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__))

        if match == u"exact":
            return self._observations_exact.get(uri, None)

        elif match == u"prefix":
            return self._observations_prefix.get(uri, None)

        elif match == u"wildcard":
            return self._observations_wildcard.get(uri, None)

        else:
            raise Exception("invalid match strategy '{}'".format(match))

    def match_observations(self, uri):
        """
        Returns the observations matching the given URI. This is the core method called
        by a broker to actually dispatch events.

        :param uri: The URI to match.
        :type uri: unicode

        :returns: A list of observations matching the URI. This is a list of instance of
            one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``.
        :rtype: list
        """
        observations = []

        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__))

        if uri in self._observations_exact:
            observations.append(self._observations_exact[uri])

        for observation in self._observations_prefix.iter_prefix_values(uri):
            observations.append(observation)

        for observation in self._observations_wildcard.iter_matches(uri):
            observations.append(observation)

        return observations

    def best_matching_observation(self, uri):
        """
        Returns the observation that best matches the given URI. This is the core method called
        by a dealer to actually forward calls.

        :param uri: The URI to match.
        :type uri: unicode

        :returns: The observation best matching the URI. This is an instance of
            ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` or ``None``.
        :rtype: obj or None
        """
        if not isinstance(uri, six.text_type):
            raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__))

        # a exact matching observation is always "best", if any
        #
        if uri in self._observations_exact:
            return self._observations_exact[uri]

        # "second best" is the longest prefix-matching observation, if any
        # FIXME: do we want this to take precedence over _any_ wildcard (see below)?
        #
        try:
            return self._observations_prefix.longest_prefix_value(uri)
        except KeyError:
            pass

        # FIXME: for wildcard observations, when there are multiple matching, we'd
        # like to deterministically select the "most selective one"
        # We first need a definition of "most selective", and then we need to implement
        # this here.
        #
        for observation in self._observations_wildcard.iter_matches(uri):
            return observation

    def get_observation_by_id(self, id):
        """
        Get a observation by ID.

        :param id: The ID of the observation to retrieve.
        :type id: int

        :returns: The observation for the given ID or ``None``.
        :rtype: obj or None
        """
        return self._observation_id_to_observation.get(id, None)

    def drop_observer(self, observer, observation):
        """
        Drop a observer from a observation.

        :param observer: The observer to drop from the given observation.
        :type observer: obj
        :param observation: The observation from which to drop the observer. An instance
            of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` previously
            created and handed out by this observation map.
        :type observation: obj

        :returns: A tuple ``(was_observed, was_last_observer)``.
        :rtype: tuple
        """
        if observer in observation.observers:

            was_observed = True

            # remove observer from observation
            #
            observation.observers.discard(observer)

            # no more observers on this observation!
            #
            if not observation.observers:

                if observation.match == u"exact":
                    del self._observations_exact[observation.uri]

                elif observation.match == u"prefix":
                    del self._observations_prefix[observation.uri]

                elif observation.match == u"wildcard":
                    del self._observations_wildcard[observation.uri]

                else:
                    # should not arrive here
                    raise Exception("logic error")

                was_last_observer = True

                del self._observation_id_to_observation[observation.id]

            else:
                was_last_observer = False

        else:
            # observer wasn't on this observation
            was_observed = False

        return was_observed, was_last_observer