stomper 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|