torquebox-messaging 1.0.1-java → 1.1-java

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/lib/gem_hook.rb CHANGED
@@ -20,3 +20,4 @@ require 'torquebox/messaging/task'
20
20
  require 'torquebox/messaging/destination'
21
21
  require 'torquebox/messaging/message_processor'
22
22
  require 'torquebox/messaging/backgroundable'
23
+ require 'torquebox/messaging/web_sockets_processor'
@@ -16,12 +16,15 @@
16
16
  # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
17
17
 
18
18
  require 'torquebox/messaging/destination'
19
+ require 'torquebox/messaging/future'
20
+ require 'torquebox/messaging/future_status'
19
21
 
20
22
  module TorqueBox
21
23
  module Messaging
22
24
  module Backgroundable
23
25
  def self.included(base)
24
26
  base.extend(ClassMethods)
27
+ base.send(:include, FutureStatus)
25
28
  end
26
29
 
27
30
  def background(options = { })
@@ -104,7 +107,15 @@ module TorqueBox
104
107
 
105
108
  class << self
106
109
  def publish_message(receiver, method, args, options = { })
107
- Queue.new(QUEUE_NAME).publish({:receiver => receiver, :method => method, :args => args}, options)
110
+ queue = Queue.new(QUEUE_NAME)
111
+ future = Future.new( queue )
112
+ queue.publish({:receiver => receiver,
113
+ :future_id => future.correlation_id,
114
+ :future_queue => QUEUE_NAME,
115
+ :method => method,
116
+ :args => args}, options)
117
+
118
+ future
108
119
  rescue javax.naming.NameNotFoundException => ex
109
120
  raise RuntimeError.new("The backgroundable queue is not available. Did you disable it by setting its concurrency to 0?")
110
121
  end
@@ -56,6 +56,7 @@ module TorqueBox
56
56
  TorqueBox::Naming.connect( naming_host, naming_port ) do |context|
57
57
  connection_factory = context['/ConnectionFactory']
58
58
  connection = connection_factory.createConnection
59
+ connection.client_id = options[:client_id]
59
60
  session = connection.createSession( transacted, canonical_ack_mode( ack_mode ) )
60
61
  connection.start
61
62
  session.naming_context = context
@@ -1,15 +1,15 @@
1
1
  # Copyright 2008-2011 Red Hat, Inc, and individual contributors.
2
- #
2
+ #
3
3
  # This is free software; you can redistribute it and/or modify it
4
4
  # under the terms of the GNU Lesser General Public License as
5
5
  # published by the Free Software Foundation; either version 2.1 of
6
6
  # the License, or (at your option) any later version.
7
- #
7
+ #
8
8
  # This software is distributed in the hope that it will be useful,
9
9
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
11
  # Lesser General Public License for more details.
12
- #
12
+ #
13
13
  # You should have received a copy of the GNU Lesser General Public
14
14
  # License along with this software; if not, write to the Free
15
15
  # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
@@ -25,14 +25,16 @@ module TorqueBox
25
25
 
26
26
  module Destination
27
27
  include Enumerable
28
-
29
- attr_reader :name
30
28
 
29
+ attr_reader :name
30
+ attr_reader :connect_options
31
+ attr_reader :enumerable_options
32
+
31
33
  PRIORITY_MAP = {
32
- :low => 1,
33
- :normal => 4,
34
- :high => 7,
35
- :critical => 9
34
+ :low => 1,
35
+ :normal => 4,
36
+ :high => 7,
37
+ :critical => 9
36
38
  }
37
39
 
38
40
  def initialize(name, connect_options=nil, enumerable_options=nil)
@@ -91,7 +93,7 @@ module TorqueBox
91
93
  end
92
94
  end
93
95
  end
94
-
96
+
95
97
  def to_s
96
98
  name
97
99
  end
@@ -164,9 +166,19 @@ module TorqueBox
164
166
 
165
167
  class Topic
166
168
  include Destination
169
+ DEFAULT_SUBSCRIBER_NAME = 'subscriber-1'
170
+
167
171
  def destination
168
172
  @destination ||= Java::org.torquebox.messaging.core::ManagedTopic.new
169
173
  end
174
+
175
+ def unsubscribe(name = DEFAULT_SUBSCRIBER_NAME, options = {})
176
+ wait_for_destination(options[:startup_timeout]) {
177
+ Client.connect(@connect_options) do |session|
178
+ session.unsubscribe(name)
179
+ end
180
+ }
181
+ end
170
182
  end
171
183
 
172
184
  end
@@ -46,7 +46,15 @@ module javax.jms::Session
46
46
  timeout = options.fetch(:timeout, 0)
47
47
  selector = options.fetch(:selector, nil)
48
48
  destination = lookup_destination( destination_name )
49
- consumer = createConsumer( destination, selector )
49
+ if options[:durable] && destination.class.name =~ /Topic/
50
+ raise ArgumentError.new( "You must set the :client_id via Topic.new's connect_options to use :durable" ) unless connection.client_id
51
+ consumer = createDurableSubscriber( destination,
52
+ options.fetch(:subscriber_name, TorqueBox::Messaging::Topic::DEFAULT_SUBSCRIBER_NAME),
53
+ selector,
54
+ false )
55
+ else
56
+ consumer = createConsumer( destination, selector )
57
+ end
50
58
  jms_message = consumer.receive( timeout )
51
59
  if jms_message
52
60
  decode ? jms_message.decode : jms_message
@@ -0,0 +1,112 @@
1
+ # Copyright 2008-2011 Red Hat, Inc, and individual contributors.
2
+ #
3
+ # This is free software; you can redistribute it and/or modify it
4
+ # under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation; either version 2.1 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # This software is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this software; if not, write to the Free
15
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
16
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
17
+
18
+ module TorqueBox
19
+ module Messaging
20
+ # A Future encapsulates the result of a long running
21
+ # process, and is used in conjunction with a {FutureResponder}.
22
+ class Future
23
+
24
+ # Returns the remote error (if any)
25
+ attr_reader :error
26
+ attr_reader :correlation_id
27
+ attr_accessor :default_result_timeout
28
+
29
+ # @param [TorqueBox::Messaging::Queue] response_queue The queue
30
+ # where response messages are to be received.
31
+ # @param [Hash] options Additional options
32
+ # @option options [String] :correlation_id (Future.unique_id) The correlation_id used on
33
+ # the messages to uniquely identify the call they are for.
34
+ # @option options [Integer] :default_result_timeout (30_000) The timeout
35
+ # used by default for the receive call. The processing must at
36
+ # least start before the timeout expires, and finish before 2x
37
+ # this timeout.
38
+ def initialize(response_queue, options = { })
39
+ @queue = response_queue
40
+ @correlation_id = options[:correlation_id] || self.class.unique_id
41
+ @default_result_timeout = options[:default_result_timeout] || 30_000
42
+ end
43
+
44
+ def started?
45
+ receive unless @started
46
+ @started
47
+ end
48
+
49
+ def complete?
50
+ receive unless @complete || @error
51
+ @complete
52
+ end
53
+
54
+ def error?
55
+ receive unless @complete || @error
56
+ !!@error
57
+ end
58
+
59
+ # Returns the latest response from the remote processor, if
60
+ # any. Status reporting is optional, and must be handled by the
61
+ # processed task itself.
62
+ # @see FutureResponder#status
63
+ def status
64
+ receive unless @complete || @error
65
+ @status
66
+ end
67
+
68
+ # Attempts to return the remote result.
69
+ # @param [Integer] timeout The processing must at least start
70
+ # before the timeout expires, and finish before 2x this timeout.
71
+ # @raise [TimeoutException] if the timeout expires when
72
+ # receiving the result
73
+ # @return the remote result
74
+ def result(timeout = default_result_timeout)
75
+ receive( timeout ) unless @started
76
+ raise TimeoutException.new( "timeout expired waiting for processing to start" ) unless @started
77
+ receive( timeout ) unless @complete || @error
78
+ raise TimeoutException.new( "timeout expired waiting for processing to finish" ) unless @complete || @error
79
+ raise @error if @error
80
+ @result
81
+ end
82
+
83
+ # Delegates to {#result} with the default timeout.
84
+ def method_missing(method, *args, &block)
85
+ result.send( method, *args, &block )
86
+ end
87
+
88
+ # @return [String] a unique id useful for correlating a
89
+ # result to its call
90
+ def self.unique_id
91
+ java.util.UUID.randomUUID.to_s
92
+ end
93
+
94
+ protected
95
+ def receive(timeout = 1)
96
+ response = @queue.receive( :timeout => timeout, :selector => "JMSCorrelationID = '#{@correlation_id}'" )
97
+
98
+ if response
99
+ @started = true
100
+ @status = response[:status] if response.has_key?( :status )
101
+ @complete = response.has_key?( :result )
102
+ @result ||= response[:result]
103
+ @error ||= response[:error]
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ class TimeoutException < RuntimeError; end
110
+
111
+ end
112
+ end
@@ -0,0 +1,92 @@
1
+ # Copyright 2008-2011 Red Hat, Inc, and individual contributors.
2
+ #
3
+ # This is free software; you can redistribute it and/or modify it
4
+ # under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation; either version 2.1 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # This software is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this software; if not, write to the Free
15
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
16
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
17
+
18
+ module TorqueBox
19
+ module Messaging
20
+ # A FutureResponder encapsulates sending the results of a long
21
+ # running process to a {Future}.
22
+ class FutureResponder
23
+
24
+ # @param [TorqueBox::Messaging::Queue] response_queue The queue
25
+ # where response messages are to be published.
26
+ # @param [String] correlation_id The correlation_id used on
27
+ # the messages to uniquely identify the call they are for.
28
+ # @param [Integer] message_ttl The time-to-live used on messages
29
+ # to prevent them from staying in the queue indefinately if
30
+ # the result is never accessed.
31
+ def initialize(response_queue, correlation_id, message_ttl = 600_000)
32
+ @queue = response_queue
33
+ @correlation_id = correlation_id
34
+ @message_ttl = message_ttl
35
+ end
36
+
37
+ # Signal that processing has started.
38
+ def started
39
+ publish( :started => true, :priority => :low )
40
+ end
41
+
42
+ # Report the current status back to the client. The status value
43
+ # is application specific.
44
+ def status=(status)
45
+ publish( :status => status )
46
+ end
47
+
48
+ # Signal that processing has completed.
49
+ # @param The result of the operation.
50
+ def result=(result)
51
+ publish( :result => result, :priority => :high )
52
+ end
53
+
54
+ # Signal that an error occurred during processing.
55
+ # @param [Exception] The error!
56
+ def error=(error)
57
+ publish( :error => error, :priority => :high )
58
+ end
59
+
60
+ # Handles started/complete/error for you around the given
61
+ # block. The current responder is avaiable inside the block via
62
+ # {.current}, which is useful for sending {#status} messages.
63
+ def respond
64
+ started
65
+ Thread.current[:future_responder] = self
66
+ self.result = yield
67
+ rescue Exception => e
68
+ self.error = e
69
+ puts "FutureResponder#respond: An error occured: ", e
70
+ puts e.backtrace.join( "\n" )
71
+ end
72
+
73
+ # Convenience method that returns the thread local
74
+ # responder. Only valid inside a block passed to {#respond}.
75
+ def self.current
76
+ Thread.current[:future_responder]
77
+ end
78
+
79
+ # Convenience method that allows you to send a status message
80
+ # via the {.current} responder. Only valid inside a block passed
81
+ # to {#respond}.
82
+ def self.status=(status)
83
+ current.status = status if current
84
+ end
85
+
86
+ protected
87
+ def publish(message)
88
+ @queue.publish( message, :correlation_id => @correlation_id, :ttl => @message_ttl )
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright 2008-2011 Red Hat, Inc, and individual contributors.
2
+ #
3
+ # This is free software; you can redistribute it and/or modify it
4
+ # under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation; either version 2.1 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # This software is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this software; if not, write to the Free
15
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
16
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
17
+
18
+ require 'torquebox/messaging/future_responder'
19
+
20
+ module TorqueBox
21
+ module Messaging
22
+ module FutureStatus
23
+
24
+ def __future
25
+ @__future ||= FutureProxy.new
26
+ end
27
+ alias_method :future, :__future
28
+
29
+ class FutureProxy
30
+ def status=(status)
31
+ FutureResponder.status = status
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -16,27 +16,42 @@
16
16
  # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
17
17
 
18
18
  require 'torquebox/messaging/destination'
19
+ require 'torquebox/messaging/future_responder'
20
+ require 'torquebox/messaging/future'
21
+ require 'torquebox/messaging/future_status'
19
22
 
20
23
  module TorqueBox
21
24
  module Messaging
22
25
 
23
26
  class Task
24
-
27
+ include FutureStatus
28
+
25
29
  def self.queue_name
26
30
  suffix = org.torquebox.common.util.StringUtils.underscore(name[0...-4])
27
31
  "/queues/torquebox/#{ENV['TORQUEBOX_APP_NAME']}/tasks/#{suffix}"
28
32
  end
29
33
 
30
34
  def self.async(method, payload = {}, options = {})
31
- message = {:method => method, :payload => payload}
32
- Queue.new(queue_name).publish message, options
35
+ queue = Queue.new(queue_name)
36
+ future = Future.new( queue )
37
+ message = {
38
+ :method => method,
39
+ :payload => payload,
40
+ :future_id => future.correlation_id,
41
+ :future_queue => queue_name
42
+ }
43
+ queue.publish( message, options )
44
+
45
+ future
33
46
  rescue javax.naming.NameNotFoundException => ex
34
47
  raise RuntimeError.new("The queue for #{self.name} is not available. Did you disable it by setting its concurrency to 0?")
35
48
  end
36
49
 
37
50
  def process!(message)
38
51
  hash = message.decode
39
- self.send hash[:method].to_sym, hash[:payload]
52
+ FutureResponder.new( Queue.new( hash[:future_queue] ), hash[:future_id] ).respond do
53
+ self.send hash[:method].to_sym, hash[:payload]
54
+ end
40
55
  end
41
56
 
42
57
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  module TorqueboxMessaging
2
- VERSION = '1.0.1'
3
- MAVEN_VERSION = '1.0.1'
2
+ VERSION = '1.1'
3
+ MAVEN_VERSION = '1.1'
4
4
  end
5
5
  begin
6
6
  require 'java'
@@ -21,7 +21,7 @@ begin
21
21
  require File.dirname(__FILE__) + '/activation-1.1.jar'
22
22
  require File.dirname(__FILE__) + '/jaxb-api-2.1.9.jar'
23
23
  require File.dirname(__FILE__) + '/torquebox-base-spi.jar'
24
- require File.dirname(__FILE__) + '/jruby-complete-1.6.2.jar'
24
+ require File.dirname(__FILE__) + '/jruby-complete-1.6.3.jar'
25
25
  require File.dirname(__FILE__) + '/torquebox-base-metadata.jar'
26
26
  require File.dirname(__FILE__) + '/torquebox-base-core.jar'
27
27
  require File.dirname(__FILE__) + '/picketbox-bare-3.0.0.CR2.jar'
@@ -62,6 +62,7 @@ describe TorqueBox::Messaging::Backgroundable do
62
62
  @queue = mock('queue')
63
63
  @queue.stub(:publish)
64
64
  TorqueBox::Messaging::Queue.stub(:new).and_return(@queue)
65
+ TorqueBox::Messaging::Future.stub(:unique_id).and_return('1234')
65
66
  end
66
67
 
67
68
  it "should put a message on the queue" do
@@ -69,9 +70,20 @@ describe TorqueBox::Messaging::Backgroundable do
69
70
  MyTestModel.new.an_async_action(nil, nil)
70
71
  end
71
72
 
72
- it "should include the receiver, sync method, and args" do
73
+ it "should return a future" do
74
+ result = MyTestModel.new.an_async_action(nil, nil)
75
+ result.is_a?(TorqueBox::Messaging::Future).should be_true
76
+ end
77
+
78
+ it "should include the proper options in the message" do
73
79
  object = MyTestModel.new
74
- @queue.should_receive(:publish).with({:receiver => object, :method => '__sync_an_async_action', :args => [:a, :b]}, { })
80
+ @queue.should_receive(:publish).with({
81
+ :receiver => object,
82
+ :future_id => '1234',
83
+ :future_queue => "/queues/torquebox//backgroundable",
84
+ :method => '__sync_an_async_action',
85
+ :args => [:a, :b]
86
+ }, { })
75
87
  object.an_async_action(:a, :b)
76
88
  end
77
89
 
@@ -99,21 +111,27 @@ describe TorqueBox::Messaging::Backgroundable do
99
111
 
100
112
  it "should queue any method called on it" do
101
113
  @queue.should_receive(:publish).with({:receiver => anything,
102
- :method => :foo,
103
- :args => anything}, { })
114
+ :method => :foo,
115
+ :args => anything,
116
+ :future_id => anything,
117
+ :future_queue => anything}, { })
104
118
  @object.background.foo
105
119
  end
106
120
 
107
121
  it "should queue the receiver" do
108
122
  @queue.should_receive(:publish).with({:receiver => @object,
109
- :method => anything,
110
- :args => anything}, { })
123
+ :method => anything,
124
+ :args => anything,
125
+ :future_id => anything,
126
+ :future_queue => anything}, { })
111
127
  @object.background.foo
112
128
  end
113
129
 
114
130
  it "should queue the args" do
115
131
  @queue.should_receive(:publish).with({:receiver => anything,
116
- :method => anything,
132
+ :method => anything,
133
+ :future_id => anything,
134
+ :future_queue => anything,
117
135
  :args => [1,2]}, {})
118
136
  @object.background.foo(1,2)
119
137
  end
@@ -131,8 +149,10 @@ describe TorqueBox::Messaging::Backgroundable do
131
149
 
132
150
  it "should pass through any options" do
133
151
  @queue.should_receive(:publish).with({:receiver => anything,
134
- :method => anything,
135
- :args => anything},
152
+ :method => anything,
153
+ :args => anything,
154
+ :future_id => anything,
155
+ :future_queue => anything},
136
156
  {:ttl => 1})
137
157
  @object.background(:ttl => 1).foo
138
158
  end
data/spec/client_spec.rb CHANGED
@@ -79,7 +79,7 @@ describe TorqueBox::Messaging::Client do
79
79
  received_message.should eql( message )
80
80
  end
81
81
 
82
- it "should identify a non-String message with a text property" do
82
+ it "should properly decode a non-String message" do
83
83
  received_message = nil
84
84
  TorqueBox::Messaging::Client.connect() do |session|
85
85
  session.publish( '/queues/foo', [] )
@@ -88,10 +88,9 @@ describe TorqueBox::Messaging::Client do
88
88
  session.commit
89
89
  end
90
90
  received_message.decode.should eql( [] )
91
- received_message.get_string_property( 'torquebox_encoding' ).should eql( 'base64' )
92
91
  end
93
92
 
94
- it "should not identify a String message with a text property" do
93
+ it "should properly decode a String message" do
95
94
  received_message = nil
96
95
  TorqueBox::Messaging::Client.connect() do |session|
97
96
  session.publish( '/queues/foo', "foo" )
@@ -99,9 +98,7 @@ describe TorqueBox::Messaging::Client do
99
98
  received_message = session.receive( '/queues/foo', :decode => false )
100
99
  session.commit
101
100
  end
102
- received_message.text.should eql( "foo" )
103
101
  received_message.decode.should eql( "foo" )
104
- received_message.get_string_property( 'torquebox_encoding' ).should be_nil
105
102
  end
106
103
 
107
104
  it "should timeout if asked" do
@@ -144,7 +144,7 @@ describe TorqueBox::Messaging::Destination do
144
144
  queue = TorqueBox::Messaging::Queue.new "/queues/browseable"
145
145
  queue.start
146
146
  queue.publish "howdy"
147
- queue.first.text.should == 'howdy'
147
+ queue.first.decode.should == 'howdy'
148
148
  queue.destroy
149
149
  end
150
150
 
@@ -153,8 +153,9 @@ describe TorqueBox::Messaging::Destination do
153
153
  queue.start
154
154
  queue.publish "howdy", :properties => {:blurple => 5}
155
155
  queue.publish "ahoyhoy", :properties => {:blurple => 6}
156
- queue.first.text.should == 'ahoyhoy'
157
- queue.detect { |m| m.text == 'howdy' }.should be_nil
156
+ queue.first.decode.should == 'ahoyhoy'
157
+ queue.detect { |m| m.decode == 'howdy' }.should be_nil
158
+ queue.detect { |m| m.decode == 'ahoyhoy' }.should_not be_nil
158
159
  queue.destroy
159
160
 
160
161
  end
@@ -183,6 +184,18 @@ describe TorqueBox::Messaging::Destination do
183
184
  message.should eql( "howdy" )
184
185
  end
185
186
 
187
+ it "should receive a binary file correctly" do
188
+ queue = TorqueBox::Messaging::Queue.new "/queues/foo"
189
+ queue.start
190
+
191
+ data = File.open("#{File.dirname(__FILE__)}/../src/test/resources/sample.pdf", "r") { |file| file.read }
192
+ queue.publish data
193
+ message = queue.receive
194
+
195
+ queue.destroy
196
+ message.should eql( data )
197
+ end
198
+
186
199
  it "should publish to multiple topic consumers" do
187
200
  topic = TorqueBox::Messaging::Topic.new "/topics/foo"
188
201
  topic.start
@@ -201,6 +214,56 @@ describe TorqueBox::Messaging::Destination do
201
214
  msgs.to_a.should eql( ["howdy"] * count )
202
215
  end
203
216
 
217
+ context "durable topics" do
218
+ describe 'receive' do
219
+ it "should be durable" do
220
+ topic = TorqueBox::Messaging::Topic.new "/topics/foo", :client_id => 'blarg'
221
+ topic.start
222
+
223
+ topic.receive :durable => true, :timeout => 1
224
+ topic.publish 'biscuit'
225
+ response = topic.receive :durable => true, :timeout => 10_000
226
+ response.should == 'biscuit'
227
+
228
+ topic.destroy
229
+ end
230
+
231
+ it "should raise if client_id is not set" do
232
+ topic = TorqueBox::Messaging::Topic.new "/topics/foo"
233
+ topic.start
234
+
235
+ lambda { topic.receive :durable => true, :timeout => 1 }.should raise_error(ArgumentError)
236
+
237
+ topic.destroy
238
+ end
239
+ end
240
+
241
+ describe 'unsubscribe' do
242
+ it "should work" do
243
+ topic = TorqueBox::Messaging::Topic.new "/topics/foo", :client_id => 'blarg'
244
+ topic.start
245
+
246
+ topic.receive :durable => true, :timeout => 1
247
+ topic.publish 'biscuit'
248
+ response = topic.receive :durable => true, :timeout => 10_000
249
+ response.should == 'biscuit'
250
+
251
+ topic.unsubscribe
252
+
253
+ topic.publish 'ham'
254
+ response = topic.receive :durable => true, :timeout => 10
255
+ response.should be_nil
256
+
257
+ topic.publish 'gravy'
258
+ response = topic.receive :durable => true, :timeout => 10_000
259
+ response.should == 'gravy'
260
+
261
+ topic.destroy
262
+
263
+ end
264
+ end
265
+ end
266
+
204
267
  context "synchronous messaging" do
205
268
  it "should return value of block given to receive_and_publish" do
206
269
  queue = TorqueBox::Messaging::Queue.new "/queues/publish_and_receive"
@@ -0,0 +1,59 @@
1
+ require 'torquebox/messaging/future'
2
+
3
+ include TorqueBox::Messaging
4
+
5
+ class Future
6
+ attr_writer :started
7
+ attr_writer :complete
8
+ attr_writer :error
9
+ attr_writer :result
10
+ end
11
+
12
+ describe TorqueBox::Messaging::Future do
13
+
14
+ before(:each) do
15
+ @queue = mock( Queue )
16
+ @future = Future.new( @queue )
17
+ end
18
+
19
+ describe "#result" do
20
+
21
+ it "should raise if it fails to start before timeout" do
22
+ @future.stub(:receive)
23
+ lambda { @future.result( 1 ) }.should raise_error( TimeoutException )
24
+ end
25
+
26
+ it "should raise if it fails to complete before timeout" do
27
+ @future.stub(:receive)
28
+ @future.started = true
29
+ lambda { @future.result( 1 ) }.should raise_error( TimeoutException )
30
+ end
31
+
32
+ it "should raise if a remote error occurs" do
33
+ @future.stub(:receive)
34
+ @future.started = true
35
+ @future.error = ArgumentError.new
36
+ lambda { @future.result( 1 ) }.should raise_error( ArgumentError )
37
+ end
38
+
39
+ it "should return the result if complete" do
40
+ @future.stub(:receive)
41
+ @future.started = true
42
+ @future.complete = true
43
+ @future.result = :success!
44
+ @future.result.should == :success!
45
+ end
46
+
47
+ end
48
+
49
+ describe "#method_missing" do
50
+
51
+ it "should delegate to #result" do
52
+ @result = mock(:result)
53
+ @future.should_receive(:result).and_return(@result)
54
+ @result.should_receive(:blah)
55
+ @future.blah
56
+ end
57
+
58
+ end
59
+ end
data/spec/task_spec.rb CHANGED
@@ -1,39 +1,71 @@
1
-
1
+ require 'torquebox/messaging/future_responder'
2
+ require 'torquebox/messaging/future'
3
+ require 'torquebox/messaging/destination'
2
4
  require 'torquebox/messaging/task'
3
5
 
4
- class MyTestTask < TorqueBox::Messaging::Task
6
+ class MyTestTask < TorqueBox::Messaging::Task
5
7
  attr_accessor :payload
8
+
9
+ def action(payload={ })
10
+ future.status = 1
11
+ end
12
+ end
13
+
14
+ class TorqueBox::Messaging::FutureResponder
15
+ def respond
16
+ yield
17
+ end
6
18
  end
7
19
 
8
20
  describe TorqueBox::Messaging::Task do
9
21
 
10
- it "should send payload correctly" do
11
- expectation = [{:method => :payload=, :payload => {:foo => 'bar'}}, { }]
12
- queue = mock("queue")
13
- queue.should_receive(:publish).with(*expectation)
14
- TorqueBox::Messaging::Queue.should_receive(:new).with(MyTestTask.queue_name).and_return(queue)
15
22
 
16
- MyTestTask.async(:payload=, :foo => 'bar')
17
- end
23
+ describe '#async' do
24
+ before(:each) do
25
+ TorqueBox::Messaging::Future.stub(:unique_id).and_return('1234')
26
+ @send_queue = mock('queue')
27
+ @send_queue.stub(:publish)
28
+ TorqueBox::Messaging::Queue.should_receive(:new).with(MyTestTask.queue_name).and_return(@send_queue)
29
+ end
30
+
31
+ it "should send payload correctly" do
32
+ expectation = [{:method => :payload=, :payload => {:foo => 'bar'}, :future_id => '1234', :future_queue => MyTestTask.queue_name}, { }]
33
+ @send_queue.should_receive(:publish).with(*expectation)
34
+
35
+ MyTestTask.async(:payload=, :foo => 'bar')
36
+ end
18
37
 
19
- it "should process payload correctly" do
20
- expectation = {:method => :payload=, :payload => {:foo => 'bar'}}
21
- message = mock("message")
22
- message.should_receive(:decode).and_return(expectation)
38
+ it "should handle nil payload as empty hash" do
39
+ @send_queue.should_receive(:publish).with(hash_including(:payload => {}), {})
23
40
 
24
- task = MyTestTask.new
25
- task.process! message
26
- task.payload[:foo].should == 'bar'
27
- end
41
+ MyTestTask.async(:payload=)
42
+ end
28
43
 
29
- it "should handle nil payload as empty hash" do
30
- queue = mock("queue")
31
- queue.should_receive(:publish).with(hash_including(:payload => {}), {})
32
- TorqueBox::Messaging::Queue.should_receive(:new).with(MyTestTask.queue_name).and_return(queue)
33
- MyTestTask.async(:payload=)
44
+ it "should return a future" do
45
+ result = MyTestTask.async(:payload=)
46
+ result.is_a?(TorqueBox::Messaging::Future).should be_true
47
+ end
34
48
  end
35
49
 
50
+ describe "#process!" do
51
+ it "should process payload correctly" do
52
+ expectation = {:method => :payload=, :payload => {:foo => 'bar'}, :future_id => '1234', :future_queue => MyTestTask.queue_name}
53
+ message = mock("message")
54
+ message.should_receive(:decode).and_return(expectation)
36
55
 
56
+ task = MyTestTask.new
57
+ task.process! message
58
+ task.payload[:foo].should == 'bar'
59
+ end
60
+ end
61
+
62
+ describe 'the future proxy' do
63
+ it "should be available inside a task method" do
64
+ TorqueBox::Messaging::FutureResponder.should_receive(:status=).with(1)
65
+ MyTestTask.new.action
66
+ end
67
+ end
68
+
37
69
  describe "queue_name" do
38
70
  before(:each) do
39
71
  ENV['TORQUEBOX_APP_NAME'] = 'app_name'
@@ -42,11 +74,11 @@ describe TorqueBox::Messaging::Task do
42
74
  after(:each) do
43
75
  ENV.delete('TORQUEBOX_APP_NAME')
44
76
  end
45
-
77
+
46
78
  it "should derive the queue name from the class name" do
47
79
  MyTestTask.queue_name.should =~ %r{/my_test$}
48
80
  end
49
-
81
+
50
82
  it "should include the app name in the queue name" do
51
83
  MyTestTask.queue_name.should =~ %r{/app_name/}
52
84
  end
metadata CHANGED
@@ -2,15 +2,15 @@
2
2
  name: torquebox-messaging
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.0.1
5
+ version: "1.1"
6
6
  platform: java
7
- authors: []
7
+ authors: ["The TorqueBox Team"]
8
8
 
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-25 00:00:00 -04:00
13
+ date: 2011-07-13 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -21,7 +21,7 @@ dependencies:
21
21
  requirements:
22
22
  - - "="
23
23
  - !ruby/object:Gem::Version
24
- version: 1.0.1
24
+ version: "1.1"
25
25
  type: :runtime
26
26
  version_requirements: *id001
27
27
  - !ruby/object:Gem::Dependency
@@ -32,7 +32,7 @@ dependencies:
32
32
  requirements:
33
33
  - - "="
34
34
  - !ruby/object:Gem::Version
35
- version: 1.0.1
35
+ version: "1.1"
36
36
  type: :development
37
37
  version_requirements: *id002
38
38
  - !ruby/object:Gem::Dependency
@@ -43,7 +43,7 @@ dependencies:
43
43
  requirements:
44
44
  - - "="
45
45
  - !ruby/object:Gem::Version
46
- version: 1.0.1
46
+ version: "1.1"
47
47
  type: :development
48
48
  version_requirements: *id003
49
49
  - !ruby/object:Gem::Dependency
@@ -54,7 +54,7 @@ dependencies:
54
54
  requirements:
55
55
  - - "="
56
56
  - !ruby/object:Gem::Version
57
- version: 1.0.1
57
+ version: "1.1"
58
58
  type: :runtime
59
59
  version_requirements: *id004
60
60
  - !ruby/object:Gem::Dependency
@@ -96,7 +96,7 @@ files:
96
96
  - lib/activation-1.1.jar
97
97
  - lib/jaxb-api-2.1.9.jar
98
98
  - lib/torquebox-base-spi.jar
99
- - lib/jruby-complete-1.6.2.jar
99
+ - lib/jruby-complete-1.6.3.jar
100
100
  - lib/torquebox-base-metadata.jar
101
101
  - lib/torquebox-base-core.jar
102
102
  - lib/picketbox-bare-3.0.0.CR2.jar
@@ -116,6 +116,9 @@ files:
116
116
  - lib/torquebox/messaging/backgroundable.rb
117
117
  - lib/torquebox/messaging/client.rb
118
118
  - lib/torquebox/messaging/destination.rb
119
+ - lib/torquebox/messaging/future.rb
120
+ - lib/torquebox/messaging/future_responder.rb
121
+ - lib/torquebox/messaging/future_status.rb
119
122
  - lib/torquebox/messaging/task.rb
120
123
  - lib/torquebox/messaging/ext/javax_jms_queue_browser.rb
121
124
  - lib/torquebox/messaging/ext/javax_jms_session.rb
@@ -124,6 +127,7 @@ files:
124
127
  - spec/destination_spec.rb
125
128
  - spec/dispatcher-queues.yml
126
129
  - spec/dispatcher_not_running.rb
130
+ - spec/future_spec.rb
127
131
  - spec/queues.yml
128
132
  - spec/task_spec.rb
129
133
  - spec/ext/java_jmx_session_spec.rb
@@ -159,5 +163,6 @@ test_files:
159
163
  - spec/backgroundable_spec.rb
160
164
  - spec/client_spec.rb
161
165
  - spec/destination_spec.rb
166
+ - spec/future_spec.rb
162
167
  - spec/task_spec.rb
163
168
  - spec/ext/java_jmx_session_spec.rb