stomper 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/{spec/spec.opts → .rspec} +0 -2
- data/Gemfile +4 -0
- data/LICENSE +201 -201
- data/README.md +130 -0
- data/Rakefile +5 -0
- data/examples/basic.rb +38 -0
- data/examples/events.rb +54 -0
- data/features/acking_messages.feature +147 -0
- data/features/disconnecting.feature +12 -0
- data/features/establish_connection.feature +44 -0
- data/features/protocol_version_negotiation.feature +61 -0
- data/features/receipts.feature +72 -0
- data/features/scopes.feature +32 -0
- data/features/secure_connections.feature +38 -0
- data/features/send_and_message.feature +28 -0
- data/features/steps/acking_messages_steps.rb +39 -0
- data/features/steps/disconnecting_steps.rb +8 -0
- data/features/steps/establish_connection_steps.rb +74 -0
- data/features/steps/frame_transmission_steps.rb +35 -0
- data/features/steps/protocol_version_negotiation_steps.rb +15 -0
- data/features/steps/receipts_steps.rb +79 -0
- data/features/steps/scopes_steps.rb +52 -0
- data/features/steps/secure_connections_steps.rb +41 -0
- data/features/steps/send_and_message_steps.rb +35 -0
- data/features/steps/subscribing_steps.rb +36 -0
- data/features/steps/threaded_receiver_steps.rb +8 -0
- data/features/steps/transactions_steps.rb +0 -0
- data/features/subscribing.feature +151 -0
- data/features/support/env.rb +11 -0
- data/features/support/header_helpers.rb +12 -0
- data/features/support/ssl/README +6 -0
- data/features/support/ssl/broker_cert.csr +17 -0
- data/features/support/ssl/broker_cert.pem +72 -0
- data/features/support/ssl/broker_key.pem +27 -0
- data/features/support/ssl/client_cert.csr +17 -0
- data/features/support/ssl/client_cert.pem +72 -0
- data/features/support/ssl/client_key.pem +27 -0
- data/features/support/ssl/demoCA/cacert.pem +17 -0
- data/features/support/ssl/demoCA/index.txt +2 -0
- data/features/support/ssl/demoCA/index.txt.attr +1 -0
- data/features/support/ssl/demoCA/index.txt.attr.old +1 -0
- data/features/support/ssl/demoCA/index.txt.old +1 -0
- data/features/support/ssl/demoCA/newcerts/01.pem +72 -0
- data/features/support/ssl/demoCA/newcerts/02.pem +72 -0
- data/features/support/ssl/demoCA/private/cakey.pem +17 -0
- data/features/support/ssl/demoCA/serial +1 -0
- data/features/support/ssl/demoCA/serial.old +1 -0
- data/features/support/test_stomp_server.rb +150 -0
- data/features/threaded_receiver.feature +11 -0
- data/features/transactions.feature +66 -0
- data/lib/stomper.rb +30 -20
- data/lib/stomper/connection.rb +442 -102
- data/lib/stomper/errors.rb +59 -0
- data/lib/stomper/extensions.rb +10 -0
- data/lib/stomper/extensions/common.rb +258 -0
- data/lib/stomper/extensions/events.rb +213 -0
- data/lib/stomper/extensions/heartbeat.rb +101 -0
- data/lib/stomper/extensions/scoping.rb +56 -0
- data/lib/stomper/frame.rb +54 -0
- data/lib/stomper/frame_serializer.rb +217 -0
- data/lib/stomper/headers.rb +15 -0
- data/lib/stomper/receipt_manager.rb +36 -0
- data/lib/stomper/receivers.rb +7 -0
- data/lib/stomper/receivers/threaded.rb +71 -0
- data/lib/stomper/scopes.rb +9 -0
- data/lib/stomper/scopes/header_scope.rb +49 -0
- data/lib/stomper/scopes/receipt_scope.rb +44 -0
- data/lib/stomper/scopes/transaction_scope.rb +109 -0
- data/lib/stomper/sockets.rb +66 -28
- data/lib/stomper/subscription_manager.rb +79 -0
- data/lib/stomper/support.rb +68 -0
- data/lib/stomper/support/1.8/frame_serializer.rb +53 -0
- data/lib/stomper/support/1.8/headers.rb +183 -0
- data/lib/stomper/support/1.9/frame_serializer.rb +64 -0
- data/lib/stomper/support/1.9/headers.rb +172 -0
- data/lib/stomper/support/ruby.rb +13 -0
- data/lib/stomper/uris.rb +49 -0
- data/lib/stomper/version.rb +7 -0
- data/spec/spec_helper.rb +13 -9
- data/spec/stomper/connection_spec.rb +712 -0
- data/spec/stomper/extensions/common_spec.rb +187 -0
- data/spec/stomper/extensions/events_spec.rb +78 -0
- data/spec/stomper/extensions/heartbeat_spec.rb +103 -0
- data/spec/stomper/extensions/scoping_spec.rb +21 -0
- data/spec/stomper/frame_serializer_1.8_spec.rb +318 -0
- data/spec/stomper/frame_serializer_spec.rb +316 -0
- data/spec/stomper/frame_spec.rb +36 -0
- data/spec/stomper/headers_spec.rb +224 -0
- data/spec/stomper/receipt_manager_spec.rb +91 -0
- data/spec/stomper/receivers/threaded_spec.rb +116 -0
- data/spec/stomper/scopes/header_scope_spec.rb +42 -0
- data/spec/stomper/scopes/receipt_scope_spec.rb +51 -0
- data/spec/stomper/scopes/transaction_scope_spec.rb +183 -0
- data/spec/stomper/sockets_spec.rb +113 -0
- data/spec/stomper/subscription_manager_spec.rb +107 -0
- data/spec/stomper/support_spec.rb +69 -0
- data/spec/stomper/uris_spec.rb +54 -0
- data/spec/stomper_spec.rb +9 -0
- data/spec/support/custom_argument_matchers.rb +57 -0
- data/spec/support/existential_frame_matchers.rb +19 -0
- data/spec/support/frame_header_matchers.rb +10 -0
- data/stomper.gemspec +30 -0
- metadata +272 -97
- data/AUTHORS +0 -21
- data/CHANGELOG +0 -20
- data/README.rdoc +0 -120
- data/lib/stomper/client.rb +0 -34
- data/lib/stomper/frame_reader.rb +0 -73
- data/lib/stomper/frame_writer.rb +0 -21
- data/lib/stomper/frames.rb +0 -39
- data/lib/stomper/frames/abort.rb +0 -10
- data/lib/stomper/frames/ack.rb +0 -25
- data/lib/stomper/frames/begin.rb +0 -11
- data/lib/stomper/frames/client_frame.rb +0 -89
- data/lib/stomper/frames/commit.rb +0 -10
- data/lib/stomper/frames/connect.rb +0 -10
- data/lib/stomper/frames/connected.rb +0 -30
- data/lib/stomper/frames/disconnect.rb +0 -10
- data/lib/stomper/frames/error.rb +0 -21
- data/lib/stomper/frames/message.rb +0 -48
- data/lib/stomper/frames/receipt.rb +0 -19
- data/lib/stomper/frames/send.rb +0 -10
- data/lib/stomper/frames/server_frame.rb +0 -38
- data/lib/stomper/frames/subscribe.rb +0 -42
- data/lib/stomper/frames/unsubscribe.rb +0 -19
- data/lib/stomper/open_uri_interface.rb +0 -41
- data/lib/stomper/receipt_handlers.rb +0 -23
- data/lib/stomper/receiptor.rb +0 -38
- data/lib/stomper/subscriber.rb +0 -76
- data/lib/stomper/subscription.rb +0 -128
- data/lib/stomper/subscriptions.rb +0 -95
- data/lib/stomper/threaded_receiver.rb +0 -59
- data/lib/stomper/transaction.rb +0 -185
- data/lib/stomper/transactor.rb +0 -50
- data/lib/stomper/uri.rb +0 -55
- data/spec/client_spec.rb +0 -29
- data/spec/connection_spec.rb +0 -22
- data/spec/frame_reader_spec.rb +0 -37
- data/spec/frame_writer_spec.rb +0 -27
- data/spec/frames/client_frame_spec.rb +0 -66
- data/spec/frames/indirect_frame_spec.rb +0 -45
- data/spec/frames/server_frame_spec.rb +0 -85
- data/spec/open_uri_interface_spec.rb +0 -132
- data/spec/receiptor_spec.rb +0 -35
- data/spec/shared_connection_examples.rb +0 -79
- data/spec/subscriber_spec.rb +0 -77
- data/spec/subscription_spec.rb +0 -157
- data/spec/subscriptions_spec.rb +0 -145
- data/spec/threaded_receiver_spec.rb +0 -33
- data/spec/transaction_spec.rb +0 -139
- data/spec/transactor_spec.rb +0 -46
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Namespace for exceptions associated with this gem.
|
4
|
+
module Stomper::Errors
|
5
|
+
# A common base class for errors raised by the Stomper gem
|
6
|
+
# @abstract
|
7
|
+
class StomperError < StandardError; end
|
8
|
+
|
9
|
+
# Low level error raised when the broker transmits data that violates
|
10
|
+
# the Stomp protocol specification.
|
11
|
+
# @abstract
|
12
|
+
class FatalProtocolError < StomperError; end
|
13
|
+
|
14
|
+
# Raised when an invalid character is encountered in a header
|
15
|
+
class InvalidHeaderCharacterError < FatalProtocolError; end
|
16
|
+
|
17
|
+
# Raised when an invalid escape sequence is encountered in a header name or value
|
18
|
+
class InvalidHeaderEscapeSequenceError < FatalProtocolError; end
|
19
|
+
|
20
|
+
# Raised when a malformed header is encountered. For example, if a header
|
21
|
+
# line does not contain ':'
|
22
|
+
class MalformedHeaderError < FatalProtocolError; end
|
23
|
+
|
24
|
+
# Raised when a malformed frame is encountered on the stream. For example,
|
25
|
+
# if a frame is not properly terminated with the {Stomper::FrameIO::FRAME_TERMINATOR}
|
26
|
+
# character.
|
27
|
+
class MalformedFrameError < FatalProtocolError; end
|
28
|
+
|
29
|
+
# An error that is raised as a result of a misconfiguration of the client
|
30
|
+
# connection
|
31
|
+
# @abstract
|
32
|
+
class FatalConnectionError < StomperError; end
|
33
|
+
|
34
|
+
# Raised when a connection has been configured with an unsupported protocol
|
35
|
+
# version. This can be due to end user misconfiguration, or due to improper
|
36
|
+
# protocol negotiation with the message broker.
|
37
|
+
class UnsupportedProtocolVersionError < FatalConnectionError; end
|
38
|
+
|
39
|
+
# Raised when an attempt to connect to the broker results in an unexpected
|
40
|
+
# exchange.
|
41
|
+
class ConnectFailedError < FatalConnectionError; end
|
42
|
+
|
43
|
+
# Raised if the command issued is not supported by the protocol version
|
44
|
+
# negotiated between the client and broker.
|
45
|
+
class UnsupportedCommandError < StomperError; end
|
46
|
+
|
47
|
+
# An error that is raised as a result frames being generated on
|
48
|
+
# a transaction while it is in an invalid state.
|
49
|
+
# @abstract
|
50
|
+
class TransactionError < StomperError; end
|
51
|
+
|
52
|
+
# Raised if a transactionable frame is sent in a transaction that has
|
53
|
+
# already been aborted or committed.
|
54
|
+
class TransactionFinalizedError < TransactionError; end
|
55
|
+
|
56
|
+
# Raised if a BEGIN frame is sent on a transaction that has already
|
57
|
+
# begun.
|
58
|
+
class TransactionStartedError < TransactionError; end
|
59
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Extensions to {Stomper::Connection} objects
|
4
|
+
module Stomper::Extensions
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'stomper/extensions/common'
|
8
|
+
require 'stomper/extensions/scoping'
|
9
|
+
require 'stomper/extensions/heartbeat'
|
10
|
+
require 'stomper/extensions/events'
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Provides the common interface for a {Stomper::Connection} object.
|
4
|
+
module Stomper::Extensions::Common
|
5
|
+
# Extends an object with any additional modules that are appropriate
|
6
|
+
# for the Stomp protocol being used.
|
7
|
+
def self.extend_by_protocol_version(instance, version)
|
8
|
+
if EXTEND_BY_VERSION[version]
|
9
|
+
EXTEND_BY_VERSION[version].each do |mod|
|
10
|
+
instance.extend mod
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Transmits a SEND frame to the broker with the specified destination, body
|
17
|
+
# and headers. If a block is given, a +receipt+ header will be included in the frame
|
18
|
+
# and the block will be invoked when the corresponding RECEIPT frame
|
19
|
+
# is received from the broker. The naming of this method bothers me as it
|
20
|
+
# overwrites a core Ruby method but doing so maintains the consistency of
|
21
|
+
# this interface. If you want to pass a message ala +Object#send+, use the
|
22
|
+
# +__send__+ method instead.
|
23
|
+
# @note You will need to use +__send__+ if you want the behavior of +Object#send+
|
24
|
+
# @param [String] dest the destination for the SEND frame to be delivered to
|
25
|
+
# @param [String] body the body of the SEND frame
|
26
|
+
# @param [{Symbol => String}] additional headers to include in the generated frame
|
27
|
+
# @yield [receipt] invoked when the receipt for this SEND frame has been received
|
28
|
+
# @yieldparam [Stomper::Frame] receipt the RECEIPT frame sent by the broker
|
29
|
+
# @return [Stomper::Frame] the SEND frame sent to the broker
|
30
|
+
def send(dest, body, headers={}, &block)
|
31
|
+
scoped_to = block ? with_receipt(&block) : self
|
32
|
+
scoped_to.transmit create_frame('SEND', headers, { :destination => dest }, body)
|
33
|
+
end
|
34
|
+
alias :puts :send
|
35
|
+
|
36
|
+
# Transmits a SUBSCRIBE frame to the broker with the specified destination
|
37
|
+
# and headers. If a block is given, it will be invoked every time a MESSAGE
|
38
|
+
# frame is received from the broker for this subscription.
|
39
|
+
# @param [String] dest the destination for the SEND frame to be delivered to
|
40
|
+
# @param [{Symbol => String}] additional headers to include in the generated frame
|
41
|
+
# @yield [message] invoked when a MESSAGE frame for this subscription is received
|
42
|
+
# @yieldparam [Stomper::Frame] message the MESSAGE frame sent by the broker
|
43
|
+
# @return [Stomper::Frame] the SUBSCRIBE frame sent to the broker
|
44
|
+
def subscribe(dest, headers={}, &block)
|
45
|
+
::Stomper::Support.keys_to_sym!(headers)
|
46
|
+
if headers[:id].nil? || headers[:id].empty?
|
47
|
+
headers[:id] = ::Stomper::Support.next_serial
|
48
|
+
end
|
49
|
+
subscribe = create_frame('SUBSCRIBE', headers, { :destination => dest })
|
50
|
+
subscription_manager.add(subscribe, block) if block
|
51
|
+
transmit subscribe
|
52
|
+
end
|
53
|
+
|
54
|
+
# Transmits an UNSUBSCRIBE frame to the broker for the supplied subscription ID,
|
55
|
+
# or SUBSCRIBE frame.
|
56
|
+
# @param [Stomper::Frame, String] frame_or_id the subscription ID or SUBSCRIBE
|
57
|
+
# frame to unsubscribe from
|
58
|
+
# @return [Stomper::Frame] the UNSUBSCRIBE frame sent to the broker
|
59
|
+
# @raise [ArgumentError] if subscription ID cannot be determined.
|
60
|
+
def unsubscribe(frame_or_id, headers={})
|
61
|
+
sub_id = frame_or_id.is_a?(::Stomper::Frame) ? frame_or_id[:id] : frame_or_id
|
62
|
+
raise ArgumentError, 'subscription ID could not be determined' if sub_id.nil? || sub_id.empty?
|
63
|
+
if subscription_manager.subscribed_id? sub_id
|
64
|
+
transmit create_frame('UNSUBSCRIBE', headers, { :id => sub_id })
|
65
|
+
elsif subscription_manager.subscribed_destination? sub_id
|
66
|
+
subscription_manager.ids_for_destination(sub_id).map do |id|
|
67
|
+
transmit create_frame('UNSUBSCRIBE', headers, { :id => id })
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Transmits a BEGIN frame to the broker to start a transaction named by +tx_id+.
|
73
|
+
# When directly handling transaction management in this fashion, it is up to
|
74
|
+
# you to ensure the uniqueness of transaction ids, that frames within this
|
75
|
+
# transaction have their +transaction+ header set, and that transactions are
|
76
|
+
# appropriately committed or aborted.
|
77
|
+
# @see Stomper::Extensions::Scoping#with_transaction
|
78
|
+
# @see #abort
|
79
|
+
# @see #commit
|
80
|
+
# @param [String] tx_id ID of the transaction to begin
|
81
|
+
# @param [{Symbol => String}] additional headers to include in the generated frame
|
82
|
+
# @return [Stomper::Frame] the BEGIN frame sent to the broker
|
83
|
+
def begin(tx_id, headers={})
|
84
|
+
transmit create_frame('BEGIN', headers, {:transaction => tx_id})
|
85
|
+
end
|
86
|
+
|
87
|
+
# Transmits an ABORT frame to the broker to rollback a transaction named by +tx_id+.
|
88
|
+
# When directly handling transaction management in this fashion, it is up to
|
89
|
+
# you to ensure the uniqueness of transaction ids, that frames within this
|
90
|
+
# transaction have their +transaction+ header set, and that transactions are
|
91
|
+
# appropriately committed or aborted.
|
92
|
+
# @see Stomper::Extensions::Scoping#with_transaction
|
93
|
+
# @see #begin
|
94
|
+
# @see #commit
|
95
|
+
# @param [String] tx_id ID of the transaction to abort
|
96
|
+
# @param [{Symbol => String}] additional headers to include in the generated frame
|
97
|
+
# @return [Stomper::Frame] the ABORT frame sent to the broker
|
98
|
+
def abort(tx_id, headers={})
|
99
|
+
transmit create_frame('ABORT', headers, {:transaction => tx_id})
|
100
|
+
end
|
101
|
+
|
102
|
+
# Transmits a COMMIT frame to the broker to complete a transaction named by +tx_id+.
|
103
|
+
# When directly handling transaction management in this fashion, it is up to
|
104
|
+
# you to ensure the uniqueness of transaction ids, that frames within this
|
105
|
+
# transaction have their +transaction+ header set, and that transactions are
|
106
|
+
# appropriately committed or aborted.
|
107
|
+
# @see Stomper::Extensions::Scoping#with_transaction
|
108
|
+
# @see #begin
|
109
|
+
# @see #abort
|
110
|
+
# @param [String] tx_id ID of the transaction to complete
|
111
|
+
# @param [{Symbol => String}] additional headers to include in the generated frame
|
112
|
+
# @return [Stomper::Frame] the COMMIT frame sent to the broker
|
113
|
+
def commit(tx_id, headers={})
|
114
|
+
transmit create_frame('COMMIT', headers, {:transaction => tx_id})
|
115
|
+
end
|
116
|
+
|
117
|
+
# Disconnects from the broker. This is polite disconnect, in that it first
|
118
|
+
# transmits a DISCONNECT frame before closing the underlying socket. If the
|
119
|
+
# broker and client are using the Stomp 1.1 protocol, a receipt can be requested
|
120
|
+
# for the DISCONNECT frame, and the connection will remain active until
|
121
|
+
# the receipt is received or the broker closes the connection on its end.
|
122
|
+
# @param [{Symbol => String}] an optional set of headers to include in the
|
123
|
+
# DISCONNECT frame (these can include event handlers, such as :on_receipt)
|
124
|
+
def disconnect(headers={})
|
125
|
+
transmit create_frame('DISCONNECT', headers, {})
|
126
|
+
end
|
127
|
+
|
128
|
+
# Transmits an ACK frame to the broker to acknowledge that a corresponding
|
129
|
+
# MESSAGE frame has been processed by the client.
|
130
|
+
# @note If the negotiated Stomp protocol version is 1.1, this method will be
|
131
|
+
# overridden by {Stomper::Extensions::Common::V1_1#ack}
|
132
|
+
# @overload ack(message, headers={})
|
133
|
+
# @param [Stomper::Frame] message the MESSAGE frame to acknowledge
|
134
|
+
# @param [{Object => String}] headers optional headers to include with the ACK frame
|
135
|
+
# @overload ack(message_id, headers={})
|
136
|
+
# @param [String] message_id the ID of a MESSAGE frame to acknowledge
|
137
|
+
# @param [{Object => String}] headers optional headers to include with the ACK frame
|
138
|
+
# @return [Stomper::Frame] the ACK frame sent to the broker
|
139
|
+
# @example Gonna need some examples for this one...
|
140
|
+
def ack(*args)
|
141
|
+
headers = args.last.is_a?(Hash) ? args.pop : {}
|
142
|
+
m_id = args.shift
|
143
|
+
if m_id.is_a?(::Stomper::Frame)
|
144
|
+
m_id = m_id[:'message-id']
|
145
|
+
end
|
146
|
+
m_headers = [ [:'message-id', m_id] ].inject({}) do |mh, (k,v)|
|
147
|
+
mh[k] = v unless v.nil? || v.empty?
|
148
|
+
mh
|
149
|
+
end
|
150
|
+
an_frame = create_frame('ACK', headers, m_headers)
|
151
|
+
raise ::ArgumentError, 'message ID could not be determined' if an_frame[:'message-id'].nil? || an_frame[:'message-id'].empty?
|
152
|
+
transmit an_frame
|
153
|
+
end
|
154
|
+
|
155
|
+
# Always raises an error because the NACK frame is only available to connections
|
156
|
+
# using version 1.1 of the Stomp protocol.
|
157
|
+
# @note If the negotiated Stomp protocol version is 1.1, this method will be
|
158
|
+
# overridden by {Stomper::Extensions::Common::V1_1#nack}
|
159
|
+
# @see Stomper::Extensions::Protocol_1_1#nack
|
160
|
+
# @raise [Stomper::Errors::UnsupportedCommandError]
|
161
|
+
def nack(*args)
|
162
|
+
raise ::Stomper::Errors::UnsupportedCommandError, 'NACK'
|
163
|
+
end
|
164
|
+
|
165
|
+
def create_frame(command, u_head, m_head, body=nil)
|
166
|
+
::Stomper::Frame.new(command,
|
167
|
+
::Stomper::Support.keys_to_sym(u_head).merge(m_head), body)
|
168
|
+
end
|
169
|
+
private :create_frame
|
170
|
+
|
171
|
+
# Stomp Protocol 1.1 extensions to the common interface.
|
172
|
+
module V1_1
|
173
|
+
# Acknowledge that a MESSAGE frame has been received and successfully
|
174
|
+
# processed. The Stomp 1.1 protocol now requires that both ID of the
|
175
|
+
# message and the ID of the subscription the message arrived on must be
|
176
|
+
# specified in the ACK frame's headers.
|
177
|
+
# @overload ack(message, headers={})
|
178
|
+
# Transmit an ACK frame fro the MESSAGE frame. The appropriate
|
179
|
+
# subscription ID will be determined from the MESSAGE frame's
|
180
|
+
# +subscription+ header value.
|
181
|
+
# @param [Stomper::Frame] message the MESSAGE frame to acknowledge
|
182
|
+
# @param [{Object => String}] headers optional headers to include with the ACK frame
|
183
|
+
# @overload ack(message, subscription_id, headers={})
|
184
|
+
# Transmit an ACK frame for the MESSAGE frame, but use the supplied
|
185
|
+
# subscription ID instead of trying to determine it from the MESSAGE
|
186
|
+
# frame's headers. You should use this method of the broker you are
|
187
|
+
# connected to does not include a +subscribe+ header on MESSAGE frames.
|
188
|
+
# @param [Stomper::Frame] message the MESSAGE frame to acknowledge
|
189
|
+
# @param [String] subscription_id the ID of the subscription MESSAGE was delivered on.
|
190
|
+
# @param [{Object => String}] headers optional headers to include with the ACK frame
|
191
|
+
# @overload ack(message_id, subscription_id, headers={})
|
192
|
+
# Transmit an ACK frame for the MESSAGE frame with an ID of +message_id+
|
193
|
+
# delivered on the subscription with an ID of +subscription_id+.
|
194
|
+
# @param [String] message_id the ID of the MESSAGE frame to acknowledge
|
195
|
+
# @param [String] subscription_id the ID of the subscription MESSAGE was delivered on.
|
196
|
+
# @param [{Object => String}] headers optional headers to include with the ACK frame
|
197
|
+
# @return [Stomper::Frame] the ACK frame sent to the broker
|
198
|
+
# @raise [ArgumentError] if the message or subscription IDs cannot be
|
199
|
+
# determined
|
200
|
+
# @example Gonna need some examples for this one...
|
201
|
+
def ack(message_or_id, *args)
|
202
|
+
transmit create_ack_or_nack('ACK', message_or_id, args)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Inform the broker that a MESSAGE frame was not processed. A NACK frame
|
206
|
+
# is, in effect, the opposite of an ACK frame. The NACK command is a new
|
207
|
+
# feature introduced in Stomp 1.1, hence why it is unavailable to Stomp
|
208
|
+
# 1.0 connections.
|
209
|
+
# @overload nack(message, headers={})
|
210
|
+
# Transmit a NACK frame fro the MESSAGE frame. The appropriate
|
211
|
+
# subscription ID will be determined from the MESSAGE frame's
|
212
|
+
# +subscription+ header value.
|
213
|
+
# @param [Stomper::Frame] message the MESSAGE frame to un-acknowledge
|
214
|
+
# @param [{Object => String}] headers optional headers to include with the NACK frame
|
215
|
+
# @overload nack(message, subscription_id, headers={})
|
216
|
+
# Transmit a NACK frame for the MESSAGE frame, but use the supplied
|
217
|
+
# subscription ID instead of trying to determine it from the MESSAGE
|
218
|
+
# frame's headers. You should use this method of the broker you are
|
219
|
+
# connected to does not include a +subscribe+ header on MESSAGE frames.
|
220
|
+
# @param [Stomper::Frame] message the MESSAGE frame to un-acknowledge
|
221
|
+
# @param [String] subscription_id the ID of the subscription MESSAGE was delivered on.
|
222
|
+
# @param [{Object => String}] headers optional headers to include with the NACK frame
|
223
|
+
# @overload nack(message_id, subscription_id, headers={})
|
224
|
+
# Transmit a NACK frame for the MESSAGE frame with an ID of +message_id+
|
225
|
+
# delivered on the subscription with an ID of +subscription_id+.
|
226
|
+
# @param [String] message_id the ID of the MESSAGE frame to un-acknowledge
|
227
|
+
# @param [String] subscription_id the ID of the subscription MESSAGE was delivered on.
|
228
|
+
# @param [{Object => String}] headers optional headers to include with the NACK frame
|
229
|
+
# @return [Stomper::Frame] the NACK frame sent to the broker
|
230
|
+
# @raise [ArgumentError] if the message or subscription IDs cannot be
|
231
|
+
# determined
|
232
|
+
# @example Gonna need some examples for this one...
|
233
|
+
def nack(message_or_id, *args)
|
234
|
+
transmit create_ack_or_nack('NACK', message_or_id, args)
|
235
|
+
end
|
236
|
+
|
237
|
+
def create_ack_or_nack(command, m_id, args)
|
238
|
+
headers = args.last.is_a?(Hash) ? args.pop : {}
|
239
|
+
sub_id = args.shift
|
240
|
+
if m_id.is_a?(::Stomper::Frame)
|
241
|
+
sub_id = m_id[:subscription] if sub_id.nil? || sub_id.empty?
|
242
|
+
m_id = m_id[:'message-id']
|
243
|
+
end
|
244
|
+
[[:message, m_id], [:subscription, sub_id]].each do |(k,v)|
|
245
|
+
raise ::ArgumentError, "#{k} ID could not be determined" if v.nil? || v.empty?
|
246
|
+
end
|
247
|
+
create_frame(command, headers,
|
248
|
+
{:'message-id' => m_id, :subscription => sub_id })
|
249
|
+
end
|
250
|
+
private :create_ack_or_nack
|
251
|
+
end
|
252
|
+
|
253
|
+
# A mapping between protocol versions and modules to include
|
254
|
+
EXTEND_BY_VERSION = {
|
255
|
+
'1.0' => [ ],
|
256
|
+
'1.1' => [ ::Stomper::Extensions::Common::V1_1 ]
|
257
|
+
}
|
258
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Module for event based extensions.
|
4
|
+
module Stomper::Extensions::Events
|
5
|
+
# A mapping of event names that are just aliases for other event names.
|
6
|
+
ALIASED_EVENTS = {
|
7
|
+
:on_stomp => :on_connect,
|
8
|
+
:before_stomp => :before_connect,
|
9
|
+
:on_connection_disconnected => :on_connection_closed
|
10
|
+
}
|
11
|
+
|
12
|
+
# Register a callback to be fired when an ABORT frame is sent to the broker.
|
13
|
+
# @return [self]
|
14
|
+
def on_abort(&block); bind_callback(:on_abort, block); end
|
15
|
+
# Register a callback to be fired before an ABORT frame is sent to the broker.
|
16
|
+
# @return [self]
|
17
|
+
def before_abort(&block); bind_callback(:before_abort, block); end
|
18
|
+
|
19
|
+
# Register a callback to be fired when an ACK frame is sent to the broker.
|
20
|
+
# @return [self]
|
21
|
+
def on_ack(&block); bind_callback(:on_ack, block); end
|
22
|
+
# Register a callback to be fired before an ACK frame is sent to the broker.
|
23
|
+
# @return [self]
|
24
|
+
def before_ack(&block); bind_callback(:before_ack, block); end
|
25
|
+
|
26
|
+
# Register a callback to be fired when a BEGIN frame is sent to the broker.
|
27
|
+
# @return [self]
|
28
|
+
def on_begin(&block); bind_callback(:on_begin, block); end
|
29
|
+
# Register a callback to be fired before a BEGIN frame is sent to the broker.
|
30
|
+
# @return [self]
|
31
|
+
def before_begin(&block); bind_callback(:before_begin, block); end
|
32
|
+
|
33
|
+
# Register a callback to be fired when a COMMIT frame is sent to the broker.
|
34
|
+
# @return [self]
|
35
|
+
def on_commit(&block); bind_callback(:on_commit, block); end
|
36
|
+
# Register a callback to be fired before a COMMIT frame is sent to the broker.
|
37
|
+
# @return [self]
|
38
|
+
def before_commit(&block); bind_callback(:before_commit, block); end
|
39
|
+
|
40
|
+
# Register a callback to be fired when a CONNECT frame is sent to the broker.
|
41
|
+
# @return [self]
|
42
|
+
def on_connect(&block); bind_callback(:on_connect, block); end
|
43
|
+
# Register a callback to be fired before a CONNECT frame is sent to the broker.
|
44
|
+
# @return [self]
|
45
|
+
def before_connect(&block); bind_callback(:before_connect, block); end
|
46
|
+
alias :on_stomp :on_connect
|
47
|
+
alias :before_stomp :before_connect
|
48
|
+
|
49
|
+
# Register a callback to be fired when a CONNECTED frame is received from the broker.
|
50
|
+
# @return [self]
|
51
|
+
def on_connected(&block); bind_callback(:on_connected, block); end
|
52
|
+
|
53
|
+
# Register a callback to be fired when a DISCONNECT frame is sent to the broker.
|
54
|
+
# @return [self]
|
55
|
+
def on_disconnect(&block); bind_callback(:on_disconnect, block); end
|
56
|
+
# Register a callback to be fired before a DISCONNECT frame is sent to the broker.
|
57
|
+
# @return [self]
|
58
|
+
def before_disconnect(&block); bind_callback(:before_disconnect, block); end
|
59
|
+
|
60
|
+
# Register a callback to be fired when an ERROR frame is received from the broker.
|
61
|
+
# @return [self]
|
62
|
+
def on_error(&block); bind_callback(:on_error, block); end
|
63
|
+
|
64
|
+
# Register a callback to be fired when a MESSAGE frame is received from the broker.
|
65
|
+
# @return [self]
|
66
|
+
def on_message(&block); bind_callback(:on_message, block); end
|
67
|
+
|
68
|
+
# Register a callback to be fired when a NACK frame is sent to the broker.
|
69
|
+
# @return [self]
|
70
|
+
def on_nack(&block); bind_callback(:on_nack, block); end
|
71
|
+
# Register a callback to be fired before a NACK frame is sent to the broker.
|
72
|
+
# @return [self]
|
73
|
+
def before_nack(&block); bind_callback(:before_nack, block); end
|
74
|
+
|
75
|
+
# Register a callback to be fired when a RECEIPT frame is received from the broker.
|
76
|
+
# @return [self]
|
77
|
+
def on_receipt(&block); bind_callback(:on_receipt, block); end
|
78
|
+
|
79
|
+
# Register a callback to be fired when a SEND frame is sent to the broker.
|
80
|
+
# @return [self]
|
81
|
+
def on_send(&block); bind_callback(:on_send, block); end
|
82
|
+
# Register a callback to be fired before a SEND frame is sent to the broker.
|
83
|
+
# @return [self]
|
84
|
+
def before_send(&block); bind_callback(:before_send, block); end
|
85
|
+
|
86
|
+
# Register a callback to be fired when a SUBSCRIBE frame is sent to the broker.
|
87
|
+
# @return [self]
|
88
|
+
def on_subscribe(&block); bind_callback(:on_subscribe, block); end
|
89
|
+
# Register a callback to be fired before a SUBSCRIBE frame is sent to the broker.
|
90
|
+
# @return [self]
|
91
|
+
def before_subscribe(&block); bind_callback(:before_subscribe, block); end
|
92
|
+
|
93
|
+
# Register a callback to be fired when an UNSUBSCRIBE frame is sent to the broker.
|
94
|
+
# @return [self]
|
95
|
+
def on_unsubscribe(&block); bind_callback(:on_unsubscribe, block); end
|
96
|
+
# Register a callback to be fired before an UNSUBSCRIBE frame is sent to the broker.
|
97
|
+
# @return [self]
|
98
|
+
def before_unsubscribe(&block); bind_callback(:before_unsubscribe, block); end
|
99
|
+
|
100
|
+
# Register a callback to be fired when a heartbeat is sent to the broker.
|
101
|
+
# @return [self]
|
102
|
+
def on_client_beat(&block); bind_callback(:on_client_beat, block); end
|
103
|
+
# Register a callback to be fired before a heartbeat frame is sent to the broker.
|
104
|
+
# @return [self]
|
105
|
+
def before_client_beat(&block); bind_callback(:before_client_beat, block); end
|
106
|
+
|
107
|
+
# Register a callback to be fired when a heartbeat is received from the
|
108
|
+
# broker.
|
109
|
+
# @return [self]
|
110
|
+
def on_broker_beat(&block); bind_callback(:on_broker_beat, block); end
|
111
|
+
|
112
|
+
# Register a callback to be fired when a connection to the broker has
|
113
|
+
# been fully established. The connection is fully established once the
|
114
|
+
# client has sent a CONNECT frame, the broker has replied with CONNECTED
|
115
|
+
# and protocol versions and heartbeat strategies have been negotiated (if
|
116
|
+
# applicable.)
|
117
|
+
# @return [self]
|
118
|
+
def on_connection_established(&block); bind_callback(:on_connection_established, block); end
|
119
|
+
|
120
|
+
# Register a callback to be fired when a connection to the broker has
|
121
|
+
# been closed. This event will be triggered by
|
122
|
+
# {Stomper::Connection#disconnect} as well as any IO exception that shuts
|
123
|
+
# the connection down. In the event that the socket closes unexpectedly,
|
124
|
+
# {#on_connection_terminated} will be triggered before this event.
|
125
|
+
# @see #on_connection_terminated
|
126
|
+
# @return [self]
|
127
|
+
def on_connection_closed(&block); bind_callback(:on_connection_closed, block); end
|
128
|
+
alias :on_connection_disconnected :on_connection_closed
|
129
|
+
|
130
|
+
# Register a callback to be fired when a connection to the broker has
|
131
|
+
# died as per the negotiated heartbeat strategy. This event is triggered
|
132
|
+
# through {Stomper::Connection#transmit} and {Stomper::Connection#receive}
|
133
|
+
# when heartbeat death has been detected. You should not expect this event
|
134
|
+
# to trigger at the precise moment the heartbeat strategy failed.
|
135
|
+
# @note This event is not triggered the moment heartbeat death occurs.
|
136
|
+
# @return [self]
|
137
|
+
def on_connection_died(&block); bind_callback(:on_connection_died, block); end
|
138
|
+
|
139
|
+
# Register a callback to be fired when a connection to the broker has
|
140
|
+
# been unexpectedly terminated. This event will NOT be triggered by
|
141
|
+
# {Stomper::Connection#disconnect}.
|
142
|
+
# @see #on_connection_closed
|
143
|
+
# @return [self]
|
144
|
+
def on_connection_terminated(&block); bind_callback(:on_connection_terminated, block); end
|
145
|
+
|
146
|
+
# Register a callback to be fired before transmitting any frame. If the
|
147
|
+
# supplied block makes any changes to the frame argument, those changes
|
148
|
+
# will be sent to the remaining #before_transmitting callbacks, and
|
149
|
+
# ultimately will be passed on to the broker. This provides a convenient
|
150
|
+
# way to modify frames before transmission without having to subclass or
|
151
|
+
# otherwise extend the {Stomper::Connection} class. Furhter,
|
152
|
+
# changing the {Stomper::Frame#command command} attribute of the frame
|
153
|
+
# will change the frame-specific event that is triggered.
|
154
|
+
# @return [self]
|
155
|
+
def before_transmitting(&block); bind_callback(:before_transmitting, block); end
|
156
|
+
|
157
|
+
# Register a callback to be fired after transmitting any frame.
|
158
|
+
# Changes made to the frame object will be passed along to all remaining
|
159
|
+
# {#after_transmitting} callbacks. Furhter,
|
160
|
+
# changing the {Stomper::Frame#command command} attribute of the frame
|
161
|
+
# will change the frame-specific event that is triggered.
|
162
|
+
# @return [self]
|
163
|
+
def after_transmitting(&block); bind_callback(:after_transmitting, block); end
|
164
|
+
|
165
|
+
# Register a callback to be fired before receiving any frame. As a frame
|
166
|
+
# has not yet been received, callbacks invoked on this event will have
|
167
|
+
# to work with very limited information.
|
168
|
+
# @return [self]
|
169
|
+
def before_receiving(&block); bind_callback(:before_receiving, block); end
|
170
|
+
|
171
|
+
# Register a callback to be fired after receiving any frame. Like
|
172
|
+
# the #before_transmitting event, any changes made to the frame will be
|
173
|
+
# passed along to all remaining {#after_receiving} callbacks. Furhter,
|
174
|
+
# changing the {Stomper::Frame#command command} attribute of the frame
|
175
|
+
# will change the frame-specific event that is triggered.
|
176
|
+
# @return [self]
|
177
|
+
def after_receiving(&block); bind_callback(:after_receiving, block); end
|
178
|
+
|
179
|
+
# Binds a +Proc+ to be invoked when the given +event_name+ is triggered.
|
180
|
+
# @param [Symbol] event_name
|
181
|
+
# @param [Proc] cb_proc
|
182
|
+
# @return [self]
|
183
|
+
def bind_callback(event_name, cb_proc)
|
184
|
+
@event_callbacks ||= {}
|
185
|
+
@event_callbacks[event_name] ||= []
|
186
|
+
@event_callbacks[event_name] << cb_proc
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
def trigger_event(event_name, *args)
|
191
|
+
event_name = ALIASED_EVENTS[event_name] ? ALIASED_EVENTS[event_name] : event_name
|
192
|
+
@event_callbacks[event_name] && @event_callbacks[event_name].each { |cb| cb.call(*args) }
|
193
|
+
end
|
194
|
+
private :trigger_event
|
195
|
+
|
196
|
+
def trigger_received_frame(frame, *args); trigger_frame(frame, :on, :on_broker_beat, args); end
|
197
|
+
private :trigger_received_frame
|
198
|
+
|
199
|
+
def trigger_transmitted_frame(frame, *args); trigger_frame(frame, :on, :on_client_beat, args); end
|
200
|
+
private :trigger_transmitted_frame
|
201
|
+
|
202
|
+
def trigger_before_transmitted_frame(frame, *args); trigger_frame(frame, :before, :before_client_beat, args); end
|
203
|
+
private :trigger_before_transmitted_frame
|
204
|
+
|
205
|
+
def trigger_frame(frame, timing, beat_event, args)
|
206
|
+
if (f_comm = frame.command && frame.command.downcase.to_sym)
|
207
|
+
trigger_event(:"#{timing}_#{f_comm}", frame, *args)
|
208
|
+
else
|
209
|
+
trigger_event(beat_event, frame, *args)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
private :trigger_frame
|
213
|
+
end
|