stomper 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/.gitignore +5 -0
  2. data/{spec/spec.opts → .rspec} +0 -2
  3. data/Gemfile +4 -0
  4. data/LICENSE +201 -201
  5. data/README.md +130 -0
  6. data/Rakefile +5 -0
  7. data/examples/basic.rb +38 -0
  8. data/examples/events.rb +54 -0
  9. data/features/acking_messages.feature +147 -0
  10. data/features/disconnecting.feature +12 -0
  11. data/features/establish_connection.feature +44 -0
  12. data/features/protocol_version_negotiation.feature +61 -0
  13. data/features/receipts.feature +72 -0
  14. data/features/scopes.feature +32 -0
  15. data/features/secure_connections.feature +38 -0
  16. data/features/send_and_message.feature +28 -0
  17. data/features/steps/acking_messages_steps.rb +39 -0
  18. data/features/steps/disconnecting_steps.rb +8 -0
  19. data/features/steps/establish_connection_steps.rb +74 -0
  20. data/features/steps/frame_transmission_steps.rb +35 -0
  21. data/features/steps/protocol_version_negotiation_steps.rb +15 -0
  22. data/features/steps/receipts_steps.rb +79 -0
  23. data/features/steps/scopes_steps.rb +52 -0
  24. data/features/steps/secure_connections_steps.rb +41 -0
  25. data/features/steps/send_and_message_steps.rb +35 -0
  26. data/features/steps/subscribing_steps.rb +36 -0
  27. data/features/steps/threaded_receiver_steps.rb +8 -0
  28. data/features/steps/transactions_steps.rb +0 -0
  29. data/features/subscribing.feature +151 -0
  30. data/features/support/env.rb +11 -0
  31. data/features/support/header_helpers.rb +12 -0
  32. data/features/support/ssl/README +6 -0
  33. data/features/support/ssl/broker_cert.csr +17 -0
  34. data/features/support/ssl/broker_cert.pem +72 -0
  35. data/features/support/ssl/broker_key.pem +27 -0
  36. data/features/support/ssl/client_cert.csr +17 -0
  37. data/features/support/ssl/client_cert.pem +72 -0
  38. data/features/support/ssl/client_key.pem +27 -0
  39. data/features/support/ssl/demoCA/cacert.pem +17 -0
  40. data/features/support/ssl/demoCA/index.txt +2 -0
  41. data/features/support/ssl/demoCA/index.txt.attr +1 -0
  42. data/features/support/ssl/demoCA/index.txt.attr.old +1 -0
  43. data/features/support/ssl/demoCA/index.txt.old +1 -0
  44. data/features/support/ssl/demoCA/newcerts/01.pem +72 -0
  45. data/features/support/ssl/demoCA/newcerts/02.pem +72 -0
  46. data/features/support/ssl/demoCA/private/cakey.pem +17 -0
  47. data/features/support/ssl/demoCA/serial +1 -0
  48. data/features/support/ssl/demoCA/serial.old +1 -0
  49. data/features/support/test_stomp_server.rb +150 -0
  50. data/features/threaded_receiver.feature +11 -0
  51. data/features/transactions.feature +66 -0
  52. data/lib/stomper.rb +30 -20
  53. data/lib/stomper/connection.rb +442 -102
  54. data/lib/stomper/errors.rb +59 -0
  55. data/lib/stomper/extensions.rb +10 -0
  56. data/lib/stomper/extensions/common.rb +258 -0
  57. data/lib/stomper/extensions/events.rb +213 -0
  58. data/lib/stomper/extensions/heartbeat.rb +101 -0
  59. data/lib/stomper/extensions/scoping.rb +56 -0
  60. data/lib/stomper/frame.rb +54 -0
  61. data/lib/stomper/frame_serializer.rb +217 -0
  62. data/lib/stomper/headers.rb +15 -0
  63. data/lib/stomper/receipt_manager.rb +36 -0
  64. data/lib/stomper/receivers.rb +7 -0
  65. data/lib/stomper/receivers/threaded.rb +71 -0
  66. data/lib/stomper/scopes.rb +9 -0
  67. data/lib/stomper/scopes/header_scope.rb +49 -0
  68. data/lib/stomper/scopes/receipt_scope.rb +44 -0
  69. data/lib/stomper/scopes/transaction_scope.rb +109 -0
  70. data/lib/stomper/sockets.rb +66 -28
  71. data/lib/stomper/subscription_manager.rb +79 -0
  72. data/lib/stomper/support.rb +68 -0
  73. data/lib/stomper/support/1.8/frame_serializer.rb +53 -0
  74. data/lib/stomper/support/1.8/headers.rb +183 -0
  75. data/lib/stomper/support/1.9/frame_serializer.rb +64 -0
  76. data/lib/stomper/support/1.9/headers.rb +172 -0
  77. data/lib/stomper/support/ruby.rb +13 -0
  78. data/lib/stomper/uris.rb +49 -0
  79. data/lib/stomper/version.rb +7 -0
  80. data/spec/spec_helper.rb +13 -9
  81. data/spec/stomper/connection_spec.rb +712 -0
  82. data/spec/stomper/extensions/common_spec.rb +187 -0
  83. data/spec/stomper/extensions/events_spec.rb +78 -0
  84. data/spec/stomper/extensions/heartbeat_spec.rb +103 -0
  85. data/spec/stomper/extensions/scoping_spec.rb +21 -0
  86. data/spec/stomper/frame_serializer_1.8_spec.rb +318 -0
  87. data/spec/stomper/frame_serializer_spec.rb +316 -0
  88. data/spec/stomper/frame_spec.rb +36 -0
  89. data/spec/stomper/headers_spec.rb +224 -0
  90. data/spec/stomper/receipt_manager_spec.rb +91 -0
  91. data/spec/stomper/receivers/threaded_spec.rb +116 -0
  92. data/spec/stomper/scopes/header_scope_spec.rb +42 -0
  93. data/spec/stomper/scopes/receipt_scope_spec.rb +51 -0
  94. data/spec/stomper/scopes/transaction_scope_spec.rb +183 -0
  95. data/spec/stomper/sockets_spec.rb +113 -0
  96. data/spec/stomper/subscription_manager_spec.rb +107 -0
  97. data/spec/stomper/support_spec.rb +69 -0
  98. data/spec/stomper/uris_spec.rb +54 -0
  99. data/spec/stomper_spec.rb +9 -0
  100. data/spec/support/custom_argument_matchers.rb +57 -0
  101. data/spec/support/existential_frame_matchers.rb +19 -0
  102. data/spec/support/frame_header_matchers.rb +10 -0
  103. data/stomper.gemspec +30 -0
  104. metadata +272 -97
  105. data/AUTHORS +0 -21
  106. data/CHANGELOG +0 -20
  107. data/README.rdoc +0 -120
  108. data/lib/stomper/client.rb +0 -34
  109. data/lib/stomper/frame_reader.rb +0 -73
  110. data/lib/stomper/frame_writer.rb +0 -21
  111. data/lib/stomper/frames.rb +0 -39
  112. data/lib/stomper/frames/abort.rb +0 -10
  113. data/lib/stomper/frames/ack.rb +0 -25
  114. data/lib/stomper/frames/begin.rb +0 -11
  115. data/lib/stomper/frames/client_frame.rb +0 -89
  116. data/lib/stomper/frames/commit.rb +0 -10
  117. data/lib/stomper/frames/connect.rb +0 -10
  118. data/lib/stomper/frames/connected.rb +0 -30
  119. data/lib/stomper/frames/disconnect.rb +0 -10
  120. data/lib/stomper/frames/error.rb +0 -21
  121. data/lib/stomper/frames/message.rb +0 -48
  122. data/lib/stomper/frames/receipt.rb +0 -19
  123. data/lib/stomper/frames/send.rb +0 -10
  124. data/lib/stomper/frames/server_frame.rb +0 -38
  125. data/lib/stomper/frames/subscribe.rb +0 -42
  126. data/lib/stomper/frames/unsubscribe.rb +0 -19
  127. data/lib/stomper/open_uri_interface.rb +0 -41
  128. data/lib/stomper/receipt_handlers.rb +0 -23
  129. data/lib/stomper/receiptor.rb +0 -38
  130. data/lib/stomper/subscriber.rb +0 -76
  131. data/lib/stomper/subscription.rb +0 -128
  132. data/lib/stomper/subscriptions.rb +0 -95
  133. data/lib/stomper/threaded_receiver.rb +0 -59
  134. data/lib/stomper/transaction.rb +0 -185
  135. data/lib/stomper/transactor.rb +0 -50
  136. data/lib/stomper/uri.rb +0 -55
  137. data/spec/client_spec.rb +0 -29
  138. data/spec/connection_spec.rb +0 -22
  139. data/spec/frame_reader_spec.rb +0 -37
  140. data/spec/frame_writer_spec.rb +0 -27
  141. data/spec/frames/client_frame_spec.rb +0 -66
  142. data/spec/frames/indirect_frame_spec.rb +0 -45
  143. data/spec/frames/server_frame_spec.rb +0 -85
  144. data/spec/open_uri_interface_spec.rb +0 -132
  145. data/spec/receiptor_spec.rb +0 -35
  146. data/spec/shared_connection_examples.rb +0 -79
  147. data/spec/subscriber_spec.rb +0 -77
  148. data/spec/subscription_spec.rb +0 -157
  149. data/spec/subscriptions_spec.rb +0 -145
  150. data/spec/threaded_receiver_spec.rb +0 -33
  151. data/spec/transaction_spec.rb +0 -139
  152. data/spec/transactor_spec.rb +0 -46
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "COMMIT" frame from the Stomp Protocol.
4
- class Commit < Stomper::Frames::ClientFrame
5
- def initialize(transaction_id, headers={})
6
- super(headers.merge({ :transaction => transaction_id }))
7
- end
8
- end
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "CONNECT" frame from the Stomp Protocol.
4
- class Connect < Stomper::Frames::ClientFrame
5
- def initialize(login='', passcode='', headers={})
6
- super(headers.merge({ :login => login, :passcode => passcode }))
7
- end
8
- end
9
- end
10
- end
@@ -1,30 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "CONNECTED" server side frame for the Stomp Protocol.
4
- class Connected < Stomper::Frames::ServerFrame
5
-
6
- # Builds a Connected frame instance with the supplied
7
- # +headers+ and +body+
8
- def initialize(headers, body)
9
- super(headers, body)
10
- end
11
-
12
- # A convenience method that returns the value of
13
- # the session header, if it is set.
14
- #
15
- # This value can also be accessed as:
16
- # frame.headers[:session]
17
- def session
18
- @headers[:session]
19
- end
20
-
21
- def perform
22
- # TODO: I want the frames, particularly the server frames, to know
23
- # 'what to do' when they are received. For instance, when a CONNECTED
24
- # frame is received, the connection it is received on should be marked
25
- # as being "connected". This way we can get rid of the various conditional
26
- # behavior based on Frame classes in connection and client.
27
- end
28
- end
29
- end
30
- end
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "DISCONNECT" frame from the Stomp Protocol.
4
- class Disconnect < Stomper::Frames::ClientFrame
5
- def initialize(headers={})
6
- super(headers)
7
- end
8
- end
9
- end
10
- end
@@ -1,21 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates an "ERROR" server side frame for the Stomp Protocol.
4
- class Error < Stomper::Frames::ServerFrame
5
-
6
- # Creates a new Error frame with the supplied +headers+ and +body+
7
- def initialize(headers, body)
8
- super(headers, body)
9
- end
10
-
11
- # Returns the message responsible for the generation of this Error frame,
12
- # if applicable.
13
- #
14
- # This is a convenience method for:
15
- # frame.headers[:message]
16
- def message
17
- @headers[:message]
18
- end
19
- end
20
- end
21
- end
@@ -1,48 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "MESSAGE" server side frame for the Stomp Protocol.
4
- class Message < Stomper::Frames::ServerFrame
5
-
6
- # Creates a new message frame with the given +headers+ and +body+
7
- def initialize(headers, body)
8
- super(headers, body)
9
- end
10
-
11
- # Returns the message id generated by the stomp broker.
12
- #
13
- # This is a convenience method for:
14
- # frame.headers[:'message-id']
15
- def id
16
- @headers[:'message-id']
17
- end
18
-
19
- # Returns the destination from which this message was delivered.
20
- #
21
- # This is a convenience method for:
22
- # frame.headers[:destination]
23
- def destination
24
- @headers[:destination]
25
- end
26
-
27
- # Returns the name of the subscription which is responsible for the
28
- # client having received this message.
29
- #
30
- # This is a convenience method for:
31
- # frame.headers[:subscription]
32
- def subscription
33
- @headers[:subscription]
34
- end
35
-
36
- def perform
37
- # TODO: I want the frames, particularly the server frames, to know
38
- # 'what to do' when they are received. For instance, when a MESSAGE
39
- # frame is received, the message should be applied to all
40
- # stored subscriptions on the receiving connection. This will
41
- # remove the conditional branching in the Client class, and provide
42
- # a more flexible means of adding additional behaviors, instead of
43
- # relying on what is sure to become a giant case statement in Client
44
- # if we must change state on other Frames later.
45
- end
46
- end
47
- end
48
- end
@@ -1,19 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "RECEIPT" server side frame for the Stomp Protocol.
4
- class Receipt < Stomper::Frames::ServerFrame
5
-
6
- # Creates a new Receipt frame with the supplied +headers+ and +body+
7
- def initialize(headers, body)
8
- super(headers, body)
9
- end
10
-
11
- # Returns the 'receipt-id' header of the frame, which
12
- # will correspond to the 'receipt' header of the message
13
- # that caused this receipt to be sent by the stomp broker.
14
- def for
15
- @headers[:'receipt-id']
16
- end
17
- end
18
- end
19
- end
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "SEND" frame from the Stomp Protocol.
4
- class Send < Stomper::Frames::ClientFrame
5
- def initialize(destination, body, headers={})
6
- super(headers.merge({ :destination => destination }), body)
7
- end
8
- end
9
- end
10
- end
@@ -1,38 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a server side frame for the Stomp Protocol.
4
- class ServerFrame < IndirectFrame
5
-
6
- # Creates a new server frame corresponding to the
7
- # supplied +command+ with the given +headers+ and +body+.
8
- def initialize(headers={}, body=nil, command = nil)
9
- super
10
- end
11
-
12
- class << self
13
- def inherited(server_frame) #:nodoc:
14
- declared_frames << { :class => server_frame, :command => server_frame.name.split("::").last.downcase.to_sym }
15
- end
16
-
17
- def declared_frames
18
- @declared_frames ||= []
19
- end
20
-
21
- # Builds a new ServerFrame instance by first checking to
22
- # see if some subclass of ServerFrame has registered itself
23
- # as a builder of the particular command. If so, a new
24
- # instance of that subclass is created, otherwise a generic
25
- # ServerFrame instance is created with its +command+ attribute
26
- # set appropriately.
27
- def build(command, headers, body)
28
- com_sym = command.downcase.to_sym
29
- if klass = declared_frames.detect { |frame| com_sym == frame[:command] }
30
- klass[:class].new(headers, body)
31
- else
32
- ServerFrame.new(headers, body, command)
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,42 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "SUBSCRIBE" frame from the Stomp Protocol.
4
- class Subscribe < Stomper::Frames::ClientFrame
5
- def initialize(destination, headers={})
6
- super({ :ack => 'auto' }.merge(headers).merge({ :destination => destination }))
7
- end
8
-
9
- # Returns the ack mode of this subscription. (defaults to 'auto')
10
- #
11
- # This is a convenience method, and may also be accessed through
12
- # frame.headers[:ack]
13
- def ack
14
- @headers[:ack]
15
- end
16
-
17
- # Returns the destination to which we are subscribing.
18
- #
19
- # This is a convenience method, and may also be accessed through
20
- # frame.headers[:destination]
21
- def destination
22
- @headers[:destination]
23
- end
24
-
25
- # Returns the id of this subscription, if it has been set.
26
- #
27
- # This is a convenience method, and may also be accessed through
28
- # frame.headers[:id]
29
- def id
30
- @headers[:id]
31
- end
32
-
33
- # Returns the selector header of this subscription, if it has been set.
34
- #
35
- # This is a convenience method, and may also be accessed through
36
- # frame.headers[:selector]
37
- def selector
38
- @headers[:selector]
39
- end
40
- end
41
- end
42
- end
@@ -1,19 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates an "UNSUBSCRIBE" frame from the Stomp Protocol.
4
- class Unsubscribe < Stomper::Frames::ClientFrame
5
- def initialize(destination, headers={})
6
- super(headers.merge({ :destination => destination }))
7
- end
8
-
9
- # Returns the id of the subscription being unsubscribed from, if it
10
- # exists.
11
- #
12
- # This is a convenience method, and may also be accessed through
13
- # frame.headers[:id]
14
- def id
15
- @headers[:id]
16
- end
17
- end
18
- end
19
- end
@@ -1,41 +0,0 @@
1
- module Stomper
2
- module OpenUriInterface
3
- def put(msg, headers={})
4
- send(default_destination, msg, headers.merge(:generate_content_length => false))
5
- end
6
- alias_method :puts, :put
7
-
8
- def write(msg, headers={})
9
- send(default_destination, msg, headers.merge(:generate_content_length => true))
10
- end
11
-
12
- def first(n=1)
13
- received = []
14
- each do |m|
15
- received << m
16
- break if received.size == n
17
- end
18
- n == 1 ? received.first : received
19
- end
20
- alias_method :get, :first
21
- alias_method :gets, :first
22
- alias_method :read, :first
23
-
24
- # This is the tricky one.
25
- # The subscriber interface is not going to work here, because it is built
26
- # for an entirely different use case (threaded receiving)
27
- # This interface, by contrast, is blocking... fudge.
28
- def each(&block)
29
- subscription = subscribe(default_destination) { |m| m }
30
- loop do
31
- m = receive
32
- yield m if m.is_a?(Stomper::Frames::Message) && subscription.accepts?(m)
33
- end
34
- end
35
-
36
- private
37
- def default_destination
38
- uri.path
39
- end
40
- end
41
- end
@@ -1,23 +0,0 @@
1
- module Stomper
2
- class ReceiptHandlers
3
- def initialize
4
- @recps = {}
5
- @recp_lock = Mutex.new
6
- end
7
-
8
- def add(receipt_id, callback)
9
- @recp_lock.synchronize { @recps[receipt_id] = callback }
10
- end
11
-
12
- def size
13
- @recps.size
14
- end
15
-
16
- def perform(receipt)
17
- @recp_lock.synchronize do
18
- callback = @recps.delete(receipt.for)
19
- callback.call(receipt) if callback
20
- end
21
- end
22
- end
23
- end
@@ -1,38 +0,0 @@
1
- module Stomper
2
- module Receiptor
3
- def self.included(base)
4
- if base.method_defined?(:receive)
5
- base.instance_eval do
6
- alias_method :receive_without_receipt_dispatch, :receive
7
- alias_method :receive, :receive_with_receipt_dispatch
8
- end
9
- end
10
- if base.method_defined?(:send)
11
- base.instance_eval do
12
- alias_method :send_without_receipt_handler, :send
13
- alias_method :send, :send_with_receipt_handler
14
- end
15
- end
16
- end
17
-
18
- # Receives a frame and dispatches it to the known receipt handlers, if the
19
- # received frame is a RECEIPT frame.
20
- def receive_with_receipt_dispatch
21
- frame = receive_without_receipt_dispatch
22
- receipt_handlers.perform(frame) if frame.is_a?(::Stomper::Frames::Receipt)
23
- frame
24
- end
25
-
26
- def send_with_receipt_handler(destination, body, headers={}, &block)
27
- if block_given?
28
- headers[:receipt] ||= "rcpt-#{Time.now.to_f}"
29
- receipt_handlers.add(headers[:receipt], block)
30
- end
31
- send_without_receipt_handler(destination, body, headers)
32
- end
33
-
34
- def receipt_handlers
35
- @receipt_handlers ||= ::Stomper::ReceiptHandlers.new
36
- end
37
- end
38
- end
@@ -1,76 +0,0 @@
1
- module Stomper
2
- module Subscriber
3
- def self.included(base)
4
- if base.method_defined?(:receive)
5
- base.instance_eval do
6
- alias_method :receive_without_message_dispatch, :receive
7
- alias_method :receive, :receive_with_message_dispatch
8
- end
9
- end
10
- end
11
-
12
- # Receives a frame and dispatches it to the known subscriptions, if the
13
- # received frame is a MESSAGE frame.
14
- def receive_with_message_dispatch
15
- frame = receive_without_message_dispatch
16
- subscriptions.perform(frame) if frame.is_a?(::Stomper::Frames::Message)
17
- frame
18
- end
19
-
20
- # Subscribes to the specified +destination+, passing along
21
- # the optional +headers+ inside the subscription frame. When a message
22
- # is received for this subscription, the supplied +block+ is
23
- # called with the received message as its argument.
24
- #
25
- # Examples:
26
- #
27
- # client.subscribe("/queue/test") { |msg| puts "Got message: #{msg.body}" }
28
- #
29
- # client.subscribe("/queue/test", :ack => 'client', 'id' => 'subscription-001') do |msg|
30
- # puts "Got message: #{msg.body}"
31
- # end
32
- #
33
- # client.subscribe("/queue/test", :selector => 'cost > 5') do |msg|
34
- # puts "Got message: #{msg.body}"
35
- # end
36
- #
37
- # See also: unsubscribe, Stomper::Subscription
38
- def subscribe(destination, headers={}, &block)
39
- unless destination.is_a?(Subscription)
40
- destination = Subscription.new(headers.merge(:destination => destination), &block)
41
- end
42
- subscriptions << destination
43
- transmit(destination.to_subscribe)
44
- destination
45
- end
46
-
47
- # Unsubscribes from the specified +destination+. The +destination+
48
- # parameter may be either a string, such as "/queue/test", or Stomper::Subscription
49
- # object. If the optional +sub_id+ is supplied, the client will unsubscribe
50
- # from the subscription with an id matching +sub_id+, regardless if the
51
- # +destination+ parameter matches that of the registered subscription. For
52
- # this reason, it is vital that subscription ids, if manually specified, be
53
- # unique.
54
- #
55
- # Examples:
56
- #
57
- # client.unsubscribe("/queue/test")
58
- # # unsubscribes from all "naive" subscriptions for "/queue/test"
59
- #
60
- # client.unsubscribe("/queue/does/not/matter", "sub-0013012031")
61
- # # unsubscribes from all subscriptions with id of "sub-0013012031"
62
- #
63
- # client.unsubscribe(some_subscription)
64
- #
65
- # See also: subscribe, Stomper::Subscription
66
- def unsubscribe(destination, sub_id=nil)
67
- subscriptions.remove(destination, sub_id).each do |unsub|
68
- transmit(unsub.to_unsubscribe)
69
- end
70
- end
71
-
72
- def subscriptions
73
- @subscriptions ||= ::Stomper::Subscriptions.new
74
- end
75
- end
76
- end