예제 #1
0
 def __init__(self, handler, ack=True, errorDestination=None, onMessageFailed=None):
     if not callable(handler):
         raise ValueError('Handler is not callable: %s' % handler)
     self._handler = handler
     self._ack = ack
     self._errorDestination = errorDestination
     self._onMessageFailed = onMessageFailed or sendToErrorDestination
     self._headers = None
     self._messages = InFlightOperations('Handler for message')
     self.log = logging.getLogger(LOG_CATEGORY)
예제 #2
0
class ReceiptListener(Listener):
    """:param timeout: When a STOMP frame was sent to the broker and a **RECEIPT** frame was requested, this is the time (in seconds) to wait for **RECEIPT** frames to arrive. If :obj:`None`, we will wait indefinitely.
    
    **Example**:
    
    >>> client.add(ReceiptListener(1.0))
    """
    def __init__(self, timeout=None):
        self._timeout = timeout
        self._receipts = InFlightOperations('Waiting for receipt')
        self.log = logging.getLogger(LOG_CATEGORY)

    def onConnectionLost(self, connection, reason): # @UnusedVariable
        for waiting in list(self._receipts.values()):
            if waiting.called:
                continue
            waiting.errback(StompCancelledError('Receipt did not arrive (connection lost)'))

    @defer.inlineCallbacks
    def onSend(self, connection, frame): # @UnusedVariable
        if not frame:
            defer.returnValue(None)
        receipt = frame.headers.get(StompSpec.RECEIPT_HEADER)
        if receipt is None:
            defer.returnValue(None)
        with self._receipts(receipt, self.log) as receiptArrived:
            yield receiptArrived.wait(self._timeout, StompCancelledError('Receipt did not arrive on time: %s [timeout=%s]' % (receipt, self._timeout)))

    def onReceipt(self, connection, frame, receipt): # @UnusedVariable
        self._receipts[receipt].callback(None)
예제 #3
0
 def test_timeout(self):
     op = InFlightOperations('test')
     with op(None) as w:
         try:
             yield w.wait(timeout=0, fail=RuntimeError('hi'))
         except RuntimeError as e:
             self.assertEquals(str(e), 'hi')
         else:
             raise
         self.assertEquals(list(op), [None])
     self.assertEquals(list(op), [])
예제 #4
0
 def test_dict_interface(self):
     op = InFlightOperations('test')
     self.assertEquals(list(op), [])
     self.assertRaises(KeyError, op.__getitem__, 1)
     self.assertRaises(KeyError, lambda: op[1])
     self.assertRaises(KeyError, op.pop, 1)
     self.assertIdentical(op.get(1), None)
     self.assertIdentical(op.get(1, 2), 2)
     op[1] = w = defer.Deferred()
     self.assertEquals(list(op), [1])
     self.assertIdentical(op[1], w)
     self.assertIdentical(op.get(1), w)
     self.assertRaises(KeyError, op.__setitem__, 1, defer.Deferred())
     self.assertIdentical(op.pop(1), w)
     self.assertRaises(KeyError, op.pop, 1)
     op[1] = w
     self.assertEquals(op.popitem(), (1, w))
     self.assertEquals(list(op), [])
     self.assertIdentical(op.setdefault(1, w), w)
     self.assertIdentical(op.setdefault(1, w), w)
예제 #5
0
 def test_dict_interface(self):
     op = InFlightOperations("test")
     self.assertEquals(list(op), [])
     self.assertRaises(KeyError, op.__getitem__, 1)
     self.assertRaises(KeyError, lambda: op[1])
     self.assertRaises(KeyError, op.pop, 1)
     self.assertIdentical(op.get(1), None)
     self.assertIdentical(op.get(1, 2), 2)
     op[1] = w = defer.Deferred()
     self.assertEquals(list(op), [1])
     self.assertIdentical(op[1], w)
     self.assertIdentical(op.get(1), w)
     self.assertRaises(KeyError, op.__setitem__, 1, defer.Deferred())
     self.assertIdentical(op.pop(1), w)
     self.assertRaises(KeyError, op.pop, 1)
     op[1] = w
     self.assertEquals(op.popitem(), (1, w))
     self.assertEquals(list(op), [])
     self.assertIdentical(op.setdefault(1, w), w)
     self.assertIdentical(op.setdefault(1, w), w)
예제 #6
0
    def test_context_single(self):
        op = InFlightOperations("test")
        with op(1) as w:
            self.assertEquals(list(op), [1])
            self.assertIsInstance(w, defer.Deferred)
            self.assertIdentical(w, op[1])
            self.assertIdentical(op.get(1), op[1])
        self.assertEquals(list(op), [])

        with op(key=2, log=logging.getLogger(LOG_CATEGORY)):
            self.assertEquals(list(op), [2])
            self.assertIsInstance(op.get(2), defer.Deferred)
            self.assertIdentical(op.get(2), op[2])
        self.assertEquals(list(op), [])

        try:
            with op(None, logging.getLogger(LOG_CATEGORY)) as w:
                reactor.callLater(0, w.cancel)  # @UndefinedVariable
                yield w.wait(timeout=None, fail=None)
        except CancelledError:
            pass
        else:
            raise
        self.assertEquals(list(op), [])

        try:
            with op(None, logging.getLogger(LOG_CATEGORY)) as w:
                reactor.callLater(0, w.errback, StompCancelledError("4711"))  # @UndefinedVariable
                yield w.wait()
        except StompCancelledError as e:
            self.assertEquals(str(e), "4711")
        else:
            raise
        self.assertEquals(list(op), [])

        with op(None, logging.getLogger(LOG_CATEGORY)) as w:
            reactor.callLater(0, w.callback, 4711)  # @UndefinedVariable
            result = yield w.wait()
            self.assertEquals(result, 4711)
        self.assertEquals(list(op), [])

        try:
            with op(None) as w:
                raise RuntimeError("hi")
        except RuntimeError:
            pass
        self.assertEquals(list(op), [])
        try:
            yield w
        except RuntimeError as e:
            self.assertEquals(str(e), "hi")
        else:
            raise

        try:
            with op(None) as w:
                d = w.wait()
                raise RuntimeError("hi")
        except RuntimeError:
            pass
        self.assertEquals(list(op), [])
        try:
            yield d
        except RuntimeError as e:
            self.assertEquals(str(e), "hi")
        else:
            pass
예제 #7
0
class SubscriptionListener(Listener):
    """Corresponds to a STOMP subscription.
    
    :param handler: A callable :obj:`f(client, frame)` which accepts a :class:`~.async.client.Stomp` connection and the received :class:`~.StompFrame`.
    :param ack: Check this option if you wish to automatically ack **MESSAGE** frames after they were handled (successfully or not).
    :param errorDestination: If a frame was not handled successfully, forward a copy of the offending frame to this destination. Example: ``errorDestination='/queue/back-to-square-one'``
    :param onMessageFailed: You can specify a custom error handler which must be a callable with signature :obj:`f(connection, failure, frame, errorDestination)`. Note that a non-trivial choice of this error handler overrides the default behavior (forward frame to error destination and ack it).
    
    .. seealso :: The unit tests in the module :mod:`.tests.async_client_integration_test` cover a couple of usage scenarios.

    """
    DEFAULT_ACK_MODE = 'client-individual'

    def __init__(self,
                 handler,
                 ack=True,
                 errorDestination=None,
                 onMessageFailed=None):
        if not callable(handler):
            raise ValueError('Handler is not callable: %s' % handler)
        self._handler = handler
        self._ack = ack
        self._errorDestination = errorDestination
        self._onMessageFailed = onMessageFailed or sendToErrorDestination
        self._headers = None
        self._messages = InFlightOperations('Handler for message')
        self.log = logging.getLogger(LOG_CATEGORY)

    @defer.inlineCallbacks
    def onDisconnect(self, connection, failure, timeout):  # @UnusedVariable
        if not self._messages:
            defer.returnValue(None)
        self.log.info(
            'Waiting for outstanding message handlers to finish ... [timeout=%s]'
            % timeout)
        yield self._waitForMessages(timeout)
        self.log.info('All handlers complete. Resuming disconnect ...')

    @defer.inlineCallbacks
    def onMessage(self, connection, frame, context):
        """onMessage(connection, frame, context)
        
        Handle a message originating from this listener's subscription."""
        if context is not self:
            return
        with self._messages(frame.headers[StompSpec.MESSAGE_ID_HEADER],
                            self.log) as waiting:
            try:
                yield self._handler(connection, frame)
            except Exception as e:
                yield self._onMessageFailed(connection, e, frame,
                                            self._errorDestination)
            finally:
                if self._ack and (self._headers[StompSpec.ACK_HEADER]
                                  in StompSpec.CLIENT_ACK_MODES):
                    connection.ack(frame)
                if not waiting.called:
                    waiting.callback(None)

    def onSubscribe(self, connection, frame, context):  # @UnusedVariable
        """Set the **ack** header of the **SUBSCRIBE** frame initiating this listener's subscription to the value of the class atrribute :attr:`DEFAULT_ACK_MODE` (if it isn't set already). Keep a copy of the headers for handling messages originating from this subscription."""
        if context is not self:
            return
        if self._headers is not None:  # already subscribed
            return
        frame.headers.setdefault(StompSpec.ACK_HEADER, self.DEFAULT_ACK_MODE)
        self._headers = frame.headers

    @defer.inlineCallbacks
    def onUnsubscribe(self, connection, frame, context):  # @UnusedVariable
        """onUnsubscribe(connection, frame, context)
        
        Forget everything about this listener's subscription and unregister from the **connection**."""
        if context is not self:
            return
        yield self._waitForMessages(None)
        connection.remove(self)

    def onConnectionLost(self, connection, reason):  # @UnusedVariable
        """onConnectionLost(connection, reason)
        
        Forget everything about this listener's subscription and unregister from the **connection**."""
        connection.remove(self)

    def _waitForMessages(self, timeout):
        return task.cooperate(
            handler.wait(
                timeout, StompCancelledError(
                    'Handlers did not finish in time.'))
            for handler in self._messages.values()).whenDone()
예제 #8
0
 def __init__(self, timeout=None):
     self._timeout = timeout
     self._receipts = InFlightOperations('Waiting for receipt')
     self.log = logging.getLogger(LOG_CATEGORY)
예제 #9
0
class SubscriptionListener(Listener):
    """Corresponds to a STOMP subscription.
    
    :param handler: A callable :obj:`f(client, frame)` which accepts a :class:`~.async.client.Stomp` connection and the received :class:`~.StompFrame`.
    :param ack: Check this option if you wish to automatically ack **MESSAGE** frames after they were handled (successfully or not).
    :param errorDestination: If a frame was not handled successfully, forward a copy of the offending frame to this destination. Example: ``errorDestination='/queue/back-to-square-one'``
    :param onMessageFailed: You can specify a custom error handler which must be a callable with signature :obj:`f(connection, failure, frame, errorDestination)`. Note that a non-trivial choice of this error handler overrides the default behavior (forward frame to error destination and ack it).
    
    .. seealso :: The unit tests in the module :mod:`.tests.async_client_integration_test` cover a couple of usage scenarios.

    """
    DEFAULT_ACK_MODE = 'client-individual'

    def __init__(self, handler, ack=True, errorDestination=None, onMessageFailed=None):
        if not callable(handler):
            raise ValueError('Handler is not callable: %s' % handler)
        self._handler = handler
        self._ack = ack
        self._errorDestination = errorDestination
        self._onMessageFailed = onMessageFailed or sendToErrorDestination
        self._headers = None
        self._messages = InFlightOperations('Handler for message')
        self.log = logging.getLogger(LOG_CATEGORY)

    @defer.inlineCallbacks
    def onDisconnect(self, connection, reason, timeout): # @UnusedVariable
        connection.remove(self)
        if not self._messages:
            defer.returnValue(None)
        self.log.info('Waiting for outstanding message handlers to finish ... [timeout=%s]' % timeout)
        yield self._waitForMessages(timeout)
        self.log.info('All handlers complete. Resuming disconnect ...')

    @defer.inlineCallbacks
    def onMessage(self, connection, frame, context):
        """onMessage(connection, frame, context)
        
        Handle a message originating from this listener's subscription."""
        if context is not self:
            return
        with self._messages(frame.headers[StompSpec.MESSAGE_ID_HEADER], self.log) as waiting:
            try:
                yield self._handler(connection, frame)
            except Exception as e:
                yield self._onMessageFailed(connection, e, frame, self._errorDestination)
            finally:
                if self._ack and (self._headers[StompSpec.ACK_HEADER] in StompSpec.CLIENT_ACK_MODES):
                    yield connection.ack(frame)
                if not waiting.called:
                    waiting.callback(None)

    def onSubscribe(self, connection, frame, context): # @UnusedVariable
        """Set the **ack** header of the **SUBSCRIBE** frame initiating this listener's subscription to the value of the class atrribute :attr:`DEFAULT_ACK_MODE` (if it isn't set already). Keep a copy of the headers for handling messages originating from this subscription."""
        if context is not self:
            return
        if self._headers is not None: # already subscribed
            return
        frame.headers.setdefault(StompSpec.ACK_HEADER, self.DEFAULT_ACK_MODE)
        self._headers = frame.headers

    @defer.inlineCallbacks
    def onUnsubscribe(self, connection, frame, context): # @UnusedVariable
        """onUnsubscribe(connection, frame, context)
        
        Forget everything about this listener's subscription and unregister from the **connection**."""
        if context is not self:
            return
        connection.remove(self)
        yield self._waitForMessages(None)

    def onConnectionLost(self, connection, reason): # @UnusedVariable
        """onConnectionLost(connection, reason)
        
        Forget everything about this listener's subscription and unregister from the **connection**."""
        connection.remove(self)

    def _waitForMessages(self, timeout):
        return task.cooperate(handler.wait(timeout, StompCancelledError('Handlers did not finish in time.')) for handler in list(self._messages.values())).whenDone()
예제 #10
0
 def __init__(self, timeout=None):
     self._timeout = timeout
     self._receipts = InFlightOperations('Waiting for receipt')
     self.log = logging.getLogger(LOG_CATEGORY)
예제 #11
0
    def test_context_single(self):
        op = InFlightOperations('test')
        with op(1) as w:
            self.assertEquals(list(op), [1])
            self.assertIsInstance(w, defer.Deferred)
            self.assertIdentical(w, op[1])
            self.assertIdentical(op.get(1), op[1])
        self.assertEquals(list(op), [])

        with op(key=2, log=logging.getLogger(LOG_CATEGORY)):
            self.assertEquals(list(op), [2])
            self.assertIsInstance(op.get(2), defer.Deferred)
            self.assertIdentical(op.get(2), op[2])
        self.assertEquals(list(op), [])

        try:
            with op(None, logging.getLogger(LOG_CATEGORY)) as w:
                reactor.callLater(0, w.cancel)  # @UndefinedVariable
                yield w.wait(timeout=None, fail=None)
        except CancelledError:
            pass
        else:
            raise
        self.assertEquals(list(op), [])

        try:
            with op(None, logging.getLogger(LOG_CATEGORY)) as w:
                reactor.callLater(
                    0, w.errback,
                    StompCancelledError('4711'))  # @UndefinedVariable
                yield w.wait()
        except StompCancelledError as e:
            self.assertEquals(str(e), '4711')
        else:
            raise
        self.assertEquals(list(op), [])

        with op(None, logging.getLogger(LOG_CATEGORY)) as w:
            reactor.callLater(0, w.callback, 4711)  # @UndefinedVariable
            result = yield w.wait()
            self.assertEquals(result, 4711)
        self.assertEquals(list(op), [])

        try:
            with op(None) as w:
                raise RuntimeError('hi')
        except RuntimeError:
            pass
        self.assertEquals(list(op), [])
        try:
            yield w
        except RuntimeError as e:
            self.assertEquals(str(e), 'hi')
        else:
            raise

        try:
            with op(None) as w:
                d = w.wait()
                raise RuntimeError('hi')
        except RuntimeError:
            pass
        self.assertEquals(list(op), [])
        try:
            yield d
        except RuntimeError as e:
            self.assertEquals(str(e), 'hi')
        else:
            pass