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
data/lib/stomper/subscription.rb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
module Stomper
|
2
|
-
# A representation of a subscription to a stomp broker destination. The
|
3
|
-
# attributes +id+, +destination+, +ack+ and +selector+ have the same
|
4
|
-
# semantic meaning as the headers of a Stomp "SUBSCRIBE" frame with the same
|
5
|
-
# name.
|
6
|
-
class Subscription
|
7
|
-
attr_reader :id, :destination, :ack, :selector
|
8
|
-
|
9
|
-
# Creates a new Subscription instance from the given parameters.
|
10
|
-
# The +destination_or_options+ parameter can either be a string
|
11
|
-
# specification of the destination, such as "/queue/target", or a hash
|
12
|
-
# corresponding to the headers of a "SUBSCRIBE" frame
|
13
|
-
# (eg: { :destination => "/queue/target", :id => "sub-001", ... })
|
14
|
-
#
|
15
|
-
# The optional +subscription_id+ parameter is a string corresponding
|
16
|
-
# to the name of this subscription. If this parameter is specified, it
|
17
|
-
# should be unique within the context of a given Stomper::Client, otherwise
|
18
|
-
# the behavior of the Stomper::Client#unsubscribe method may have unintended
|
19
|
-
# consequences.
|
20
|
-
#
|
21
|
-
# The optional +ack+ parameter specifies the mode that a client
|
22
|
-
# will use to acknowledge received messages and may be either :client or :auto.
|
23
|
-
# The default, :auto, does not require the client to notify the broker when
|
24
|
-
# it has received a message; however, setting +ack+ to :client will require
|
25
|
-
# each message received by this subscription to be acknowledged through the
|
26
|
-
# use of Stomper::Client#ack in order to ensure proper interaction between
|
27
|
-
# client and broker.
|
28
|
-
#
|
29
|
-
# The +selector+ parameter (again, optional) sets a SQL 92 selector for
|
30
|
-
# this subscription with the stomp broker as per the Stomp Protocol specification.
|
31
|
-
# Support of this functionality is entirely the responsibility of the broker,
|
32
|
-
# there is no client side filtering being done on incoming messages.
|
33
|
-
#
|
34
|
-
# When a message is "received" by an instance of Subscription, the supplied
|
35
|
-
# +block+ is inovked with the received message sent as a parameter.
|
36
|
-
#
|
37
|
-
# If no +subscription_id+ is specified, either explicitly or through a
|
38
|
-
# hash key of 'id' in +destination_or_options+, one may be automatically
|
39
|
-
# generated of the form +sub-<Time.now.to_f>+. The automatic generation
|
40
|
-
# of a subscription id occurs if and only if naive? returns false.
|
41
|
-
#
|
42
|
-
# While direct creation of Subscription instances is possible, the preferred
|
43
|
-
# method is for them to be constructed by a Stomper::Client through the use
|
44
|
-
# of the Stomper::Client#subscribe method.
|
45
|
-
#
|
46
|
-
# @see naive?
|
47
|
-
# @see Subscriber#subscribe
|
48
|
-
# @see Subscriber#unsubscribe
|
49
|
-
# @see Client#ack
|
50
|
-
def initialize(destination_or_options, subscription_id=nil, ack=nil, selector=nil, &block)
|
51
|
-
if destination_or_options.is_a?(Hash)
|
52
|
-
destination = destination_or_options[:destination]
|
53
|
-
subscription_id ||= destination_or_options[:id]
|
54
|
-
ack ||= destination_or_options[:ack]
|
55
|
-
selector ||= destination_or_options[:selector]
|
56
|
-
else
|
57
|
-
destination = destination_or_options.to_s
|
58
|
-
end
|
59
|
-
@id = subscription_id
|
60
|
-
@destination = destination
|
61
|
-
@ack = (ack || :auto).to_sym
|
62
|
-
@selector = selector
|
63
|
-
@call_back = block
|
64
|
-
@id ||= "sub-#{Time.now.to_f}" unless naive?
|
65
|
-
end
|
66
|
-
|
67
|
-
# Returns true if this subscription has no explicitly specified id,
|
68
|
-
# has no selector specified, and acknowledges messages through the :auto
|
69
|
-
# mode.
|
70
|
-
def naive?
|
71
|
-
@id.nil? && @selector.nil? && @ack == :auto
|
72
|
-
end
|
73
|
-
|
74
|
-
# Returns true if this subscription is responsible for a Stomper::Client
|
75
|
-
# instance receiving +message_frame+.
|
76
|
-
#
|
77
|
-
# See also: receives_for?, perform
|
78
|
-
def accepts?(message_frame)
|
79
|
-
receives_for?(message_frame.destination, message_frame.subscription)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Returns true if this subscription is responsible for receiving
|
83
|
-
# messages for the given destination or subscription id, specified
|
84
|
-
# by +dest+ and +subid+ respectively.
|
85
|
-
#
|
86
|
-
# Note: if +subid+ is non-nil or this subscription is not naive?,
|
87
|
-
# then this method returns true if and only if the supplied +subid+ is
|
88
|
-
# equal to the +id+ of this subscription. Otherwise, the return value
|
89
|
-
# depends only upon the equality of +dest+ and this subscriptions +destination+
|
90
|
-
# attribute.
|
91
|
-
#
|
92
|
-
# See also: naive?
|
93
|
-
def receives_for?(dest, subid=nil)
|
94
|
-
if naive? && subid.nil?
|
95
|
-
@destination == dest
|
96
|
-
else
|
97
|
-
@id == subid
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# Invokes the block associated with this subscription if
|
102
|
-
# this subscription accepts the supplied +message_frame+.
|
103
|
-
#
|
104
|
-
# See also: accepts?
|
105
|
-
def perform(message_frame)
|
106
|
-
@call_back.call(message_frame) if accepts?(message_frame)
|
107
|
-
end
|
108
|
-
|
109
|
-
# Converts this representation of a subscription into a
|
110
|
-
# Stomper::Frames::Subscribe client frame that can be transmitted
|
111
|
-
# to a stomp broker through a Stomper::Connection instance.
|
112
|
-
def to_subscribe
|
113
|
-
headers = { :destination => @destination, :ack => @ack.to_s }
|
114
|
-
headers[:id] = @id unless @id.nil?
|
115
|
-
headers[:selector] = @selector unless @selector.nil?
|
116
|
-
Stomper::Frames::Subscribe.new(@destination, headers)
|
117
|
-
end
|
118
|
-
|
119
|
-
# Converts this representation of a subscription into a
|
120
|
-
# Stomper::Frames::Unsubscribe client frame that can be transmitted
|
121
|
-
# to a stomp broker through a Stomper::Connection instance.
|
122
|
-
def to_unsubscribe
|
123
|
-
headers = { :destination => @destination }
|
124
|
-
headers[:id] = @id unless @id.nil?
|
125
|
-
Stomper::Frames::Unsubscribe.new(@destination, headers)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
module Stomper
|
2
|
-
# A Subscription collection class used internally by Stomper::Client to store
|
3
|
-
# its subscriptions. Instances of this class utilize synchronization making
|
4
|
-
# it safe to use in a multi-threaded context.
|
5
|
-
class Subscriptions
|
6
|
-
include Enumerable
|
7
|
-
|
8
|
-
# Creates a new Subscriptions container.
|
9
|
-
def initialize
|
10
|
-
@subs = []
|
11
|
-
@sub_lock = Mutex.new
|
12
|
-
end
|
13
|
-
|
14
|
-
# Adds the supplied subscription, +sub+, to the collection.
|
15
|
-
def <<(sub)
|
16
|
-
add(sub)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Adds the supplied subscription, +sub+, to the collection.
|
20
|
-
def add(sub)
|
21
|
-
raise ArgumentError, "appended object must be a subscription" unless sub.is_a?(Subscription)
|
22
|
-
@sub_lock.synchronize { @subs << sub }
|
23
|
-
end
|
24
|
-
|
25
|
-
# Removes all Subscription objects from the collection that match
|
26
|
-
# the supplied destination, +dest+, and subscription id, +subid+.
|
27
|
-
# If +dest+ is a hash, the value referenced by the :destination key
|
28
|
-
# will be used as the destination, and +subid+ will be set to the value
|
29
|
-
# referenced by :id, unless it is explicitly set beforehand. If +dest+ is
|
30
|
-
# an instance of Subscription, the +destination+ attribute will be used
|
31
|
-
# as the destination, and +subid+ will be set to the +id+ attribute, unless
|
32
|
-
# explicitly set beforehand. The Subscription objects removed are all of
|
33
|
-
# those, and only those, for which the Stomper::Subscription#receives_for?
|
34
|
-
# method returns true given the destination and/or subscription id.
|
35
|
-
#
|
36
|
-
# This method returns an array of all the Subscription objects that were
|
37
|
-
# removed, or an empty array if none were removed.
|
38
|
-
#
|
39
|
-
# See also: Stomper::Subscription#receives_for?, Stomper::Client#unsubscribe
|
40
|
-
def remove(dest, subid=nil)
|
41
|
-
if dest.is_a?(Hash)
|
42
|
-
subid ||= dest[:id]
|
43
|
-
dest = dest[:destination]
|
44
|
-
elsif dest.is_a?(Subscription)
|
45
|
-
subid ||= dest.id
|
46
|
-
dest = dest.destination
|
47
|
-
end
|
48
|
-
_remove(dest, subid)
|
49
|
-
end
|
50
|
-
|
51
|
-
# Returns the number of Subscription objects within the container through
|
52
|
-
# the use of synchronization.
|
53
|
-
def size
|
54
|
-
@sub_lock.synchronize { @subs.size }
|
55
|
-
end
|
56
|
-
|
57
|
-
# Returns the first Subscription object within the container through
|
58
|
-
# the use of synchronization.
|
59
|
-
def first
|
60
|
-
@sub_lock.synchronize { @subs.first }
|
61
|
-
end
|
62
|
-
|
63
|
-
# Returns the last Subscription object within the container through
|
64
|
-
# the use of synchronization.
|
65
|
-
def last
|
66
|
-
@sub_lock.synchronize { @subs.last }
|
67
|
-
end
|
68
|
-
|
69
|
-
# Evaluates the supplied +block+ for each Subscription object
|
70
|
-
# within the container, or yields an Enumerator for the collection
|
71
|
-
# if no +block+ is given. As this method is synchronized, it is
|
72
|
-
# entirely possible to enter into a dead-lock if the supplied block
|
73
|
-
# in turn calls any other synchronized method of the container.
|
74
|
-
# [This could be remedied by creating a new array with
|
75
|
-
# the same Subscription objects currently contained, and performing
|
76
|
-
# the +each+ call on the new array. Give this some thought.]
|
77
|
-
def each(&block)
|
78
|
-
@sub_lock.synchronize { @subs.each(&block) }
|
79
|
-
end
|
80
|
-
|
81
|
-
# Passes the supplied +message+ to all Subscription objects within the
|
82
|
-
# collection through their Stomper::Subscription#perform method.
|
83
|
-
def perform(message)
|
84
|
-
@sub_lock.synchronize { @subs.each { |sub| sub.perform(message) } }
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
def _remove(dest, subid)
|
89
|
-
@sub_lock.synchronize do
|
90
|
-
to_remove, @subs = @subs.partition { |s| s.receives_for?(dest,subid) }
|
91
|
-
to_remove
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Stomper
|
2
|
-
module ThreadedReceiver
|
3
|
-
def self.extended(base)
|
4
|
-
base.instance_eval do
|
5
|
-
@receiver_mutex = Mutex.new
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
# Starts the threaded receiver on a connection, calling receive
|
10
|
-
# on the connection repeatedly in a separate thread until the receiver
|
11
|
-
# is stopped or the connection is closed.
|
12
|
-
#
|
13
|
-
# @return self
|
14
|
-
# @see ThreadedReceiver#stop
|
15
|
-
# @see Connection#receive
|
16
|
-
# @see Connection#connected?
|
17
|
-
def start(opts={})
|
18
|
-
connect unless connected?
|
19
|
-
do_start = false
|
20
|
-
@receiver_mutex.synchronize do
|
21
|
-
do_start = !started?
|
22
|
-
end
|
23
|
-
if do_start
|
24
|
-
@started = true
|
25
|
-
@run_thread = Thread.new() do
|
26
|
-
while started? && connected?
|
27
|
-
receive
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
self
|
32
|
-
end
|
33
|
-
|
34
|
-
# Stops the threaded receiver on a connection thereby stopping further
|
35
|
-
# calls to receive.
|
36
|
-
#
|
37
|
-
# @return self
|
38
|
-
# @see ThreadedReceiver#start
|
39
|
-
# @see Connection#receive
|
40
|
-
# @see Connection#connected?
|
41
|
-
def stop
|
42
|
-
do_stop = false
|
43
|
-
@receiver_mutex.synchronize do
|
44
|
-
do_stop = started?
|
45
|
-
end
|
46
|
-
if do_stop
|
47
|
-
@started = false
|
48
|
-
@run_thread.join
|
49
|
-
@run_thread = nil
|
50
|
-
end
|
51
|
-
self
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
def started?
|
56
|
-
@started
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/lib/stomper/transaction.rb
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
module Stomper
|
2
|
-
# An exception raised whenever a Transaction object has been aborted
|
3
|
-
# due to an unhandled exception generated by its supplied block, or when
|
4
|
-
# the block explicitly aborts the transaction.
|
5
|
-
#
|
6
|
-
# See also: Stomper::Transaction#perform
|
7
|
-
class TransactionAborted < RuntimeError; end
|
8
|
-
|
9
|
-
# An encapsulation of a stomp transaction. Manually managing transactions
|
10
|
-
# is possible through the use of Stomper::Client#begin, Stomper::Client#commit,
|
11
|
-
# and Stomper::Client#abort.
|
12
|
-
#
|
13
|
-
# === Example Usage
|
14
|
-
#
|
15
|
-
# When the transaction is passed to the block:
|
16
|
-
#
|
17
|
-
# client.transaction do |t|
|
18
|
-
# t.send("/queue/target", "doing some work")
|
19
|
-
#
|
20
|
-
# # do something that might raise an exception, indicating that any
|
21
|
-
# # messages and acknowledgements we have sent should be "undone"
|
22
|
-
#
|
23
|
-
# t.send("/queue/target", "completed work")
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# When the block is evaluated within the transaction:
|
27
|
-
#
|
28
|
-
# client.transaction do
|
29
|
-
# send("/queue/target", "doing some work")
|
30
|
-
#
|
31
|
-
# # ...
|
32
|
-
#
|
33
|
-
# send("/queue/target", "completed work")
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# Nesting transactions:
|
37
|
-
#
|
38
|
-
# client.transaction do |t|
|
39
|
-
# t.transaction do |nt|
|
40
|
-
# nt.send("/queue/target", ...)
|
41
|
-
#
|
42
|
-
# nt.transaction do |nnt|
|
43
|
-
# nnt.send("/queue/target", ...)
|
44
|
-
#
|
45
|
-
# # do something with potentially exceptional results
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# nt.send("/queue/target", ...)
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# t.send("/queue/target", ...)
|
52
|
-
# end
|
53
|
-
#
|
54
|
-
# See also: Stomper::Client#transaction
|
55
|
-
#
|
56
|
-
class Transaction
|
57
|
-
# The id of this transaction, used to reference the transaction with the stomp broker.
|
58
|
-
attr_reader :id
|
59
|
-
|
60
|
-
# Creates a new Transaction instance. The +client+ parameter
|
61
|
-
# is an instance of Stomper::Client and is required so that the Transaction
|
62
|
-
# instance has somewhere to forward +begin+, +ack+ and +abort+ methods
|
63
|
-
# to. If the +trans_id+ parameter is not specified, an id is automatically
|
64
|
-
# generated of the form +tx-<Time.now.to_f>+. This name can be accessed
|
65
|
-
# through the +id+ attribute and is used in naming the transaction to
|
66
|
-
# the stomp broker. If +block+ is given, the Transaction instance immediately
|
67
|
-
# calls its perform method with the supplied +block+.
|
68
|
-
def initialize(client, trans_id=nil, &block)
|
69
|
-
@client = client
|
70
|
-
@id = trans_id || "tx-#{Time.now.to_f}"
|
71
|
-
@committed = false
|
72
|
-
@aborted = false
|
73
|
-
perform(&block) if block_given?
|
74
|
-
end
|
75
|
-
|
76
|
-
# Invokes the given +block+. If the +block+ executes normally, the
|
77
|
-
# transaction is committed, otherwise it is aborted.
|
78
|
-
# If +block+ accepts a parameter, this method yields itself to the block,
|
79
|
-
# otherwise, +block+ is evaluated within the context of this instance through
|
80
|
-
# +instance_eval+.
|
81
|
-
#
|
82
|
-
# If a call to +abort+ is issued within the block, the transaction is aborted
|
83
|
-
# as demanded, and no attempt is made to commit it; however, no code after the
|
84
|
-
# call to +abort+ will be evaluated, as +abort+ raises a TransactionAborted
|
85
|
-
# exception.
|
86
|
-
#
|
87
|
-
# If a call to +commit+ is issued within the block, the transaction is committed
|
88
|
-
# as demanded, and no attempt is made to commit it after +block+ has finished
|
89
|
-
# executing. As +commit+ does not raise an excpetion, all code after the call
|
90
|
-
# to commit will be evaluated.
|
91
|
-
#
|
92
|
-
# If you are using Transaction objects directly, and not relying on their
|
93
|
-
# generation through Stomper::Client#transaction, be warned that this method
|
94
|
-
# will raise a TransactionAborted exception if the +block+ evaluation fails.
|
95
|
-
# This behavior allows for nesting transactions and ensuring that if a nested
|
96
|
-
# transaction fails, so do all of its ancestors.
|
97
|
-
#
|
98
|
-
# @param [Proc] block A block of code that is evaluated as part of the transaction.
|
99
|
-
# @raise [TransactionAborted] raises an exception if the given block raises an exception
|
100
|
-
def perform(&block) #:yields: transaction
|
101
|
-
begin
|
102
|
-
@client.begin(@id)
|
103
|
-
if block.arity == 1
|
104
|
-
yield self
|
105
|
-
else
|
106
|
-
instance_eval(&block)
|
107
|
-
end
|
108
|
-
commit
|
109
|
-
rescue => err
|
110
|
-
_abort
|
111
|
-
raise TransactionAborted, "aborted transaction '#{@id}' originator: #{err.to_s}"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Returns true if the Transaction object has already been committed, false
|
116
|
-
# otherwise.
|
117
|
-
def committed?
|
118
|
-
@committed
|
119
|
-
end
|
120
|
-
|
121
|
-
# Returns true if the Transaction object has already been aborted, false
|
122
|
-
# otherwise.
|
123
|
-
def aborted?
|
124
|
-
@aborted
|
125
|
-
end
|
126
|
-
|
127
|
-
# Similar to Stomper::Client#transaction, this method creates a new
|
128
|
-
# Transaction object, nested inside of this one. To prevent name
|
129
|
-
# collisions, this method automatically generates a transaction id,
|
130
|
-
# if one is not specified, of the form +<parent_transaction_id>-<Time.now.to_f>+.
|
131
|
-
def transaction(transaction_id=nil,&block)
|
132
|
-
# To get a transaction name guaranteed to not collide with this one
|
133
|
-
# we will supply an explicit id to the constructor unless an id was
|
134
|
-
# provided
|
135
|
-
transaction_id ||= "#{@id}-#{Time.now.to_f}"
|
136
|
-
self.class.new(@client, transaction_id, &block)
|
137
|
-
end
|
138
|
-
|
139
|
-
# Wraps the Stomper::Client#send method, injecting a "transaction" header
|
140
|
-
# into the +headers+ hash, thus informing the stomp broker that the message
|
141
|
-
# generated here is part of this transaction.
|
142
|
-
def send(destination, body, headers={})
|
143
|
-
@client.send(destination, body, headers.merge({:transaction => @id }))
|
144
|
-
end
|
145
|
-
|
146
|
-
# Wraps the Stomper::Client#ack method, injecting a "transaction" header
|
147
|
-
# into the +headers+ hash, thus informing the stomp broker that the message
|
148
|
-
# acknowledgement is part of this transaction.
|
149
|
-
def ack(message_or_id, headers={})
|
150
|
-
@client.ack(message_or_id, headers.merge({ :transaction => @id }))
|
151
|
-
end
|
152
|
-
|
153
|
-
# Aborts this transaction if it has not already been committed or aborted.
|
154
|
-
# Note that it does so by raising a TransactionAborted exception, allowing
|
155
|
-
# the +abort+ call to force any ancestral transactions to also fail.
|
156
|
-
#
|
157
|
-
# @see Transaction#commit
|
158
|
-
# @see Transaction#committed?
|
159
|
-
# @see Transaction#aborted?
|
160
|
-
def abort
|
161
|
-
raise TransactionAborted, "transaction '#{@id}' aborted explicitly" if _abort
|
162
|
-
end
|
163
|
-
|
164
|
-
# Commits this transaction unless it has already been committed or aborted.
|
165
|
-
#
|
166
|
-
# @see Transaction#abort
|
167
|
-
# @see Transaction#committed?
|
168
|
-
# @see Transaction#aborted?
|
169
|
-
def commit
|
170
|
-
# Guard against sending multiple commit messages to the server for a
|
171
|
-
# single transaction.
|
172
|
-
@client.commit(@id) unless committed? || aborted?
|
173
|
-
@committed = true
|
174
|
-
end
|
175
|
-
|
176
|
-
private
|
177
|
-
def _abort
|
178
|
-
# Guard against sending multiple abort messages to the server for a
|
179
|
-
# single transaction.
|
180
|
-
return false if committed? || aborted?
|
181
|
-
@client.abort(@id)
|
182
|
-
@aborted = true
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|