jruby-hornetq 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/HISTORY.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.4.0 (2011-04-01)
2
+
3
+ * Upgrade to HornetQ 2.2.2
4
+ * Migrate to new HornetQClient factory API's
5
+
1
6
  ## 0.3.0 (2011-02-16)
2
7
 
3
8
  * Rename HornetQ::Client::Factory to HornetQ::Client::Connection
data/README.md CHANGED
@@ -3,17 +3,11 @@ jruby-hornetq
3
3
 
4
4
  * http://github.com/ClarityServices/jruby-hornetq
5
5
 
6
- ### WARNING: Alpha code!!
7
-
8
- This code should only be used for prototyping at this time, since breaking
9
- changes are made between every release. Once the code goes to V1.0.0 we will
10
- make every effort to not break the existing interface in any way.
11
-
12
6
  Feedback is welcome and appreciated :)
13
7
 
14
8
  ### Introduction
15
9
 
16
- jruby-hornetq attempts to "rubify" the HornetQ Java libraries without
10
+ jruby-hornetq create a Ruby friendly API into the HornetQ Java libraries without
17
11
  compromising performance. It does this by sprinkling "Ruby-goodness" into the
18
12
  existing HornetQ Java classes and interfaces, I.e. By adding Ruby methods to
19
13
  the existing classes and interfaces. Since jruby-hornetq exposes the HornetQ
@@ -36,15 +30,14 @@ several reasons for choosing the HornetQ Core API over its JMS API:
36
30
  * The HornetQ team recommend the Core API for performance
37
31
  * The HornetQ JMS API is just another wrapper on top of its Core API
38
32
 
39
- To use the JMS API, see the jruby-jms project. (Not yet released into the wild,
40
- let me know if you want it :) )
33
+ To use the JMS API from JRuby see the jruby-jms project
41
34
 
42
35
  HornetQ
43
36
  -------
44
37
 
45
- For information on the HornetQ messaging and queuing system, see:
38
+ For information on the HornetQ messaging and queuing system, see: http://www.jboss.org/hornetq
46
39
 
47
- For more documentation on any of the classes, see: http://hornetq.sourceforge.net/docs/hornetq-2.1.0.Final/api/index.html?org/hornetq/api/core/client/package-summary.html
40
+ For more documentation on any of the classes, see: http://docs.jboss.org/hornetq/2.2.2.Final/api/index.html
48
41
 
49
42
  Concepts & Terminology
50
43
  ----------------------
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ desc "Build gem"
9
9
  task :gem do |t|
10
10
  gemspec = Gem::Specification.new do |s|
11
11
  s.name = 'jruby-hornetq'
12
- s.version = '0.3.3'
12
+ s.version = '0.4.0'
13
13
  s.authors = ['Reid Morrison', 'Brad Pardee']
14
14
  s.email = ['rubywmq@gmail.com', 'bpardee@gmail.com']
15
15
  s.homepage = 'https://github.com/ClarityServices/jruby-hornetq'
@@ -0,0 +1,25 @@
1
+ #
2
+ # HornetQ Consumer:
3
+ # Read a single message from the queue
4
+ #
5
+
6
+ # Allow examples to be run in-place without requiring a gem install
7
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
8
+
9
+ require 'rubygems'
10
+ require 'hornetq'
11
+
12
+ # Using Connect.start since a session must be started in order to consume messages
13
+ HornetQ::Client::Connection.start_session('hornetq://localhost') do |session|
14
+
15
+ # Create a non-durable TestQueue to receive messages sent to the TestAddress
16
+ session.create_queue_ignore_exists('TestAddress', 'TestQueue', false)
17
+
18
+ # Consume All messages from the queue and gather statistics
19
+ stats = session.consume(:queue_name => 'TestQueue', :timeout=> 0, :statistics=>true, :browse_only=>true) do |message|
20
+ p message
21
+ puts "=================================="
22
+ message.acknowledge
23
+ end
24
+ puts "Browsed #{stats[:count]} messages in #{stats[:duration]} seconds at #{stats[:messages_per_second]} messages per second"
25
+ end
@@ -21,5 +21,5 @@ HornetQ::Client::Connection.start_session('hornetq://localhost') do |session|
21
21
  puts "=================================="
22
22
  message.acknowledge
23
23
  end
24
- puts "Received #{stats[:count]} messages in #{stats[:duration]} seconds at #{stats[:messages_per_second]} messages per second"
24
+ puts "Consumed #{stats[:count]} messages in #{stats[:duration]} seconds at #{stats[:messages_per_second]} messages per second"
25
25
  end
@@ -25,6 +25,11 @@ module HornetQ
25
25
  return java.lang.Integer.new(port)
26
26
  end
27
27
 
28
+ # Convert string into a HornetQ SimpleString
29
+ def self.as_simple_string(str)
30
+ str.is_a?(Java::org.hornetq.api.core::SimpleString) ? str : Java::org.hornetq.api.core::SimpleString.new(str.to_s)
31
+ end
32
+
28
33
  end
29
34
 
30
35
  require 'hornetq/server'
@@ -34,7 +34,7 @@ module HornetQ
34
34
  # block. Upon completion the session and connection are both closed
35
35
  # See Connection::initialize and Connection::create_session for the list
36
36
  # of parameters
37
- #
37
+ #
38
38
  # Returns result of block
39
39
  def self.start_session(params={},&proc)
40
40
  session(params) do |session|
@@ -63,50 +63,8 @@ module HornetQ
63
63
 
64
64
  # Create a new Connection from which sessions can be created
65
65
  #
66
- # Parameters:
67
- # * a Hash consisting of one or more of the named parameters
68
- # * Summary of parameters and their default values
69
- # HornetQ::Client::Connection.new(
70
- # :uri => 'hornetq://localhost',
71
- # :ack_batch_size => ,
72
- # :auto_group => ,
73
- # :block_on_acknowledge => ,
74
- # :block_on_durable_send => ,
75
- # :block_on_non_durable_send => ,
76
- # :cache_large_messages_client => ,
77
- # :call_timeout => ,
78
- # :client_failure_check_period => ,
79
- # :confirmation_window_size => ,
80
- # :connection_load_balancing_policy_class_name => ,
81
- # :connection_ttl => ,
82
- # :consumer_max_rate => ,
83
- # :consumer_window_size => ,
84
- # :discovery_address => ,
85
- # :discovery_initial_wait_timeout => ,
86
- # :discovery_port => ,
87
- # :discovery_refresh_timeout => ,
88
- # :failover_on_initial_connection => true,
89
- # :failover_on_server_shutdown => true,
90
- # :group_id => ,
91
- # :initial_message_packet_size => ,
92
- # :java_object => ,
93
- # :local_bind_address => ,
94
- # :max_retry_interval => ,
95
- # :min_large_message_size => ,
96
- # :pre_acknowledge => ,
97
- # :producer_max_rate => ,
98
- # :producer_window_size => ,
99
- # :reconnect_attempts => 1,
100
- # :retry_interval => ,
101
- # :retry_interval_multiplier => ,
102
- # :scheduled_thread_pool_max_size => ,
103
- # :static_connectors => ,
104
- # :thread_pool_max_size => ,
105
- # :use_global_pools =>
106
- # )
107
- #
108
66
  # Mandatory Parameters
109
- # * :uri
67
+ # * :uri => 'hornetq://localhost',
110
68
  # * The hornetq uri as to which server to connect with and which
111
69
  # transport protocol to use. Format:
112
70
  # hornetq://server:port,backupserver:port/?protocol=[netty|discover]
@@ -118,44 +76,204 @@ module HornetQ
118
76
  # hornetq://server:port/?protocol=discovery
119
77
  # * To use HornetQ within the current JVM
120
78
  # hornetq://invm
121
- #
122
79
  # Optional Parameters
123
- # * :ack_batch_size
124
- # * :auto_group
125
- # * :block_on_acknowledge
126
- # * :block_on_durable_send
127
- # * :block_on_non_durable_send
128
- # * :cache_large_messages_client
129
- # * :call_timeout
130
- # * :client_failure_check_period
131
- # * :confirmation_window_size
132
- # * :connection_load_balancing_policy_class_name
133
- # * :connection_ttl
134
- # * :consumer_max_rate
135
- # * :consumer_window_size
136
- # * :discovery_address
137
- # * :discovery_initial_wait_timeout
138
- # * :discovery_port
139
- # * :discovery_refresh_timeout
140
- # * :failover_on_initial_connection
141
- # * :failover_on_server_shutdown
142
- # * :group_id
143
- # * :initial_message_packet_size
144
- # * :java_object
145
- # * :local_bind_address
146
- # * :max_retry_interval
147
- # * :min_large_message_size
148
- # * :pre_acknowledge
149
- # * :producer_max_rate
150
- # * :producer_window_size
151
- # * :reconnect_attempts
152
- # * :retry_interval
153
- # * :retry_interval_multiplier
154
- # * :scheduled_thread_pool_max_size
155
- # * :static_connectors
156
- # * :thread_pool_max_size
157
- # * :use_global_pools
158
-
80
+ #
81
+ # High Availability
82
+ #
83
+ # * :ha => true | false,
84
+ # true: Receives cluster topology updates from the cluster as
85
+ # servers leave or join and new backups are appointed or removed.
86
+ # false: Uses the suplied static list of hosts in :uri
87
+ # and no HA backup information is propagated to the client
88
+ # Default: false
89
+ #
90
+ # Flow Control
91
+ #
92
+ # :ack_batch_size => integer,
93
+ # Sets the acknowledgements batch size. Must be > 0
94
+ #
95
+ # :pre_acknowledge => true | false,
96
+ # Sets whether messages will pre-acknowledged on the server before
97
+ # they are sent to the consumers or not
98
+ # true : Pre-acknowledge consumed messages on the server before they are sent to consumers
99
+ # false: Clients acknowledge the message they consume.
100
+ # Default: false
101
+ #
102
+ # Grouping:
103
+ #
104
+ # :auto_group => true | false,
105
+ # Sets whether producers will automatically assign a group ID
106
+ # to sent messages
107
+ # true: A random unique group ID is created and set on each message
108
+ # for the property Message.HDR_GROUP_ID
109
+ # Default: false
110
+ #
111
+ # :group_id => string,
112
+ # Sets the group ID that will be set on each message sent
113
+ # Default: nil (no goup id will be set)
114
+ #
115
+ # Blocking calls:
116
+ #
117
+ # :block_on_acknowledge => true | false,
118
+ # Sets whether consumers created through this factory will block
119
+ # while sending message acknowledgements or do it asynchronously.
120
+ # Default: false
121
+ #
122
+ # :block_on_durable_send => true | false,
123
+ # Sets whether producers will block while sending durable messages
124
+ # or do it asynchronously.
125
+ # If the session is configured to send durable message asynchronously,
126
+ # the client can set a SendAcknowledgementHandler on the ClientSession
127
+ # to be notified once the message has been handled by the server.
128
+ # Default: true
129
+ #
130
+ # :block_on_non_durable_send => true | false,
131
+ # Sets whether producers will block while sending non-durable messages
132
+ # or do it asynchronously.
133
+ # If the session is configured to send non-durable message asynchronously,
134
+ # the client can set a SendAcknowledgementHandler on the ClientSession
135
+ # to be notified once the message has been handled by the server.
136
+ # Default: false
137
+ #
138
+ # :call_timeout => long,
139
+ # Sets the blocking calls timeout in milliseconds. If client's blocking calls to the
140
+ # server take more than this timeout, the call will throw a
141
+ # HornetQException with the code HornetQException.CONNECTION_TIMEDOUT.
142
+ # Value is in milliseconds, default value is HornetQClient.DEFAULT_CALL_TIMEOUT.
143
+ # Must be >= 0
144
+ #
145
+ # Client Reconnection Parameters:
146
+ #
147
+ # :connection_ttl => long,
148
+ # Set the connection time-to-live
149
+ # -1 : Disable
150
+ # >=0 : milliseconds the server will keep a connection alive in the
151
+ # absence of any data arriving from the client.
152
+ # Default: 60,000
153
+ #
154
+ # :client_failure_check_period => long,
155
+ # Sets the period in milliseconds used to check if a client has
156
+ # failed to receive pings from the server.
157
+ # Value must be -1 (to disable) or greater than 0
158
+ # Default: 30,000
159
+ #
160
+ # :initial_connect_attempts => int,
161
+ # ?
162
+ #
163
+ # :failover_on_initial_connection => true | false,
164
+ # Sets whether the client will automatically attempt to connect to
165
+ # the backup server if the initial connection to the live server fails
166
+ # true : If live server is not reachable try to connect to backup server
167
+ # false: Fail to start if live server is not reachable
168
+ # Default: false
169
+ #
170
+ # :max_retry_interval => long,
171
+ # Sets the maximum retry interval in milliseconds.
172
+ # Only appicable if the retry interval multiplier has been specified
173
+ # Default: 2000 (2 seconds)
174
+ #
175
+ # :reconnect_attempts => 1,
176
+ # :retry_interval => long,
177
+ # Returns the time to retry the connection after failure.
178
+ # Value is in milliseconds.
179
+ # Default: 2000 (2 seconds)
180
+ #
181
+ # :retry_interval_multiplier => double,
182
+ # Sets the multiplier to apply to successive retry intervals.
183
+ # Value must be positive.
184
+ # Default: 1
185
+ #
186
+ # Large Message parameters:
187
+ #
188
+ # :cache_large_messages_client => true | false,
189
+ # Sets whether large messages received by consumers will be
190
+ # cached in temporary files or not.
191
+ # When true, consumers will create temporary files to cache large messages.
192
+ # There is 1 temporary file created for each large message.
193
+ # Default: false
194
+ #
195
+ # :min_large_message_size => int,
196
+ # Sets the large message size threshold in bytes. Value must be > 0
197
+ # Messages whose size is if greater than this value will be handled as large messages
198
+ # Default: 102400 bytes (100 KBytes)
199
+ #
200
+ # :compress_large_message => true | false,
201
+ #
202
+ # Message Rate Management:
203
+ #
204
+ # :consumer_max_rate => int,
205
+ # Sets the maximum rate of message consumption for consumers.
206
+ # Controls the rate at which a consumer can consume messages.
207
+ # A consumer will never consume messages at a rate faster than the
208
+ # rate specified.
209
+ # -1 : Disable
210
+ # >=0 : Maximum desired message consumption rate specified
211
+ # in units of messages per second.
212
+ # Default: -1
213
+ #
214
+ # :producer_max_rate => int,
215
+ # Sets the maximum rate of message production for producers.
216
+ # Controls the rate at which a producer can produce messages.
217
+ # A producer will never produce messages at a rate faster than the rate specified.
218
+ # -1 : Disabled
219
+ # >0 : Maximum desired message production rate specified in units of messages per second.
220
+ # Default: -1 (Disabled)
221
+ #
222
+ # Thread Pools:
223
+ #
224
+ # :scheduled_thread_pool_max_size => int,
225
+ # Sets the maximum size of the scheduled thread pool.
226
+ # This setting is relevant only if this factory does not use global pools.
227
+ # Value must be greater than 0.
228
+ # Default: 5
229
+ #
230
+ # :thread_pool_max_size => int,
231
+ # Sets the maximum size of the thread pool.
232
+ # This setting is relevant only if this factory does not use
233
+ # global pools.
234
+ # -1 : Unlimited thread pool
235
+ # >0 : Number of threads in pool
236
+ # Default: -1 (Unlimited)
237
+ #
238
+ # :use_global_pools => true | false,
239
+ # Sets whether this factory will use global thread pools
240
+ # (shared among all the factories in the same JVM) or its own pools.
241
+ # true: Uses global JVM thread pools across all HornetQ connections
242
+ # false: Use a thread pool just for this connection
243
+ # Default: true
244
+ #
245
+ # Window Sizes:
246
+ #
247
+ # :confirmation_window_size => int,
248
+ # Set the size in bytes for the confirmation window of this connection.
249
+ # -1 : Disable the window
250
+ # >0 : Size in bytes
251
+ # Default: -1 (Disabled)
252
+ #
253
+ # :consumer_window_size => int,
254
+ # Sets the window size for flow control for consumers.
255
+ # -1 : Disable flow control
256
+ # 0 : Do Not buffer any messages
257
+ # >0 : Set the maximum size of the buffer
258
+ # Default: 1048576 (1 MB)
259
+ #
260
+ # :producer_window_size => int,
261
+ # Sets the window size for flow control of the producers.
262
+ # -1 : Disable flow control
263
+ # >0 : The maximum amount of bytes at any give time (to prevent overloading the connection).
264
+ # Default: 65536 (64 KBytes)
265
+ #
266
+ # Other:
267
+ #
268
+ # :connection_load_balancing_policy_class_name => string,
269
+ # Set the class name of the connection load balancing policy
270
+ # Value must be the name of a class implementing org.hornetq.api.core.client.loadbalance.ConnectionLoadBalancingPolicy
271
+ # Default: "org.hornetq.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy"
272
+ #
273
+ # :initial_message_packet_size => int,
274
+ # Sets the initial size of messages in bytes
275
+ # Value must be greater than 0
276
+ #
159
277
  def initialize(params={})
160
278
  params =params.clone
161
279
  uri = nil
@@ -170,50 +288,55 @@ module HornetQ
170
288
  params = uri.params.merge(params)
171
289
  end
172
290
 
173
- @connection = nil
174
- @sessions = []
175
- @consumers = []
176
291
  # In-VM Transport has no fail-over or additional parameters
177
292
  @is_invm = uri.host == 'invm'
293
+ transport_list = []
178
294
  if @is_invm
179
- transport = Java::org.hornetq.api.core.TransportConfiguration.new(HornetQ::INVM_CONNECTOR_CLASS_NAME)
180
- @connection = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(transport)
181
- elsif params[:protocol]
182
- # Auto-Discovery just has a host name and port
183
- if params[:protocol] == 'discovery'
184
- @connection = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(uri.host, uri.port)
185
- elsif params[:protocol] != 'netty'
186
- raise "Unknown HornetQ protocol:#{params[:protocol]}"
187
- end
188
- end
189
-
190
- # Unless already created, then the connection will use the netty protocol
191
- unless @connection
192
- # Primary Transport
193
- transport = Java::org.hornetq.api.core.TransportConfiguration.new(HornetQ::NETTY_CONNECTOR_CLASS_NAME, {'host' => uri.host, 'port' => uri.port })
295
+ transport_list << Java::org.hornetq.api.core::TransportConfiguration.new(HornetQ::INVM_CONNECTOR_CLASS_NAME)
296
+ else
297
+ case params[:protocol]
298
+ when 'discovery'
299
+ #TODO: Also support: DiscoveryGroupConfiguration(String name, String localBindAddress, String groupAddress, int groupPort, long refreshTimeout, long discoveryInitialWaitTimeout)
300
+ transport_list << Java::org.hornetq.api.core::DiscoveryGroupConfiguration.new(uri.host, uri.port)
301
+ when 'netty', nil
302
+ transport_list << Java::org.hornetq.api.core::TransportConfiguration.new(HornetQ::NETTY_CONNECTOR_CLASS_NAME, {'host' => uri.host, 'port' => uri.port })
194
303
 
195
- # Check for backup server connection information
196
- if uri.backup_host
197
- backup_transport = Java::org.hornetq.api.core.TransportConfiguration.new(HornetQ::NETTY_CONNECTOR_CLASS_NAME, {'host' => uri.backup_host, 'port' => uri.backup_port })
198
- @connection = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(transport, backup_transport)
304
+ if uri.backup_host
305
+ transport_list << Java::org.hornetq.api.core::TransportConfiguration.new(HornetQ::NETTY_CONNECTOR_CLASS_NAME, {'host' => uri.backup_host, 'port' => uri.backup_port })
306
+ end
199
307
  else
200
- @connection = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(transport)
308
+ raise "Unknown HornetQ protocol:'#{params[:protocol]}'"
201
309
  end
202
310
  end
203
311
 
204
- # If any other options were supplied, apply them to the created Connection instance
312
+ #TODO: Support: server_locator.addInterceptor
313
+
314
+ # Create server locator with or without HA. Without HA being the default
315
+ @server_locator = if params[:ha]
316
+ Java::org.hornetq.api.core.client::HornetQClient.createServerLocatorWithHA(*transport_list)
317
+ #TODO: Support: server_locator.addClusterTopologyListener
318
+ else
319
+ Java::org.hornetq.api.core.client::HornetQClient.createServerLocatorWithoutHA(*transport_list)
320
+ end
321
+
322
+ # If any other options were supplied, apply them to the server locator
205
323
  params.each_pair do |key, val|
206
324
  method = key.to_s+'='
207
- if @connection.respond_to? method
208
- @connection.send method, val
209
- #puts "Debug: #{key} = #{@connection.send key}" if @connection.respond_to? key.to_sym
325
+ if @server_locator.respond_to? method
326
+ @server_locator.send method, val
327
+ HornetQ.logger.trace { "HornetQ ServerLocator setting: #{key} = #{@connection.send key}" } if @server_locator.respond_to? key.to_sym
210
328
  else
211
- HornetQ.logger.warn "Warning: Option:#{key}, with value:#{val} is invalid and being ignored"
329
+ HornetQ.logger.warn "Warning: Option:#{key}, with value:#{val} is invalid and will be ignored"
212
330
  end
213
331
  end
332
+
333
+ @connection = @server_locator.createSessionFactory
334
+ # For handling managed sessions and consumers
335
+ @sessions = []
336
+ @consumers = []
214
337
  end
215
338
 
216
- # Return true if this connection was configured in INVM transport protocol
339
+ # Return true if this connection was configured to use INVM transport protocol
217
340
  def invm?
218
341
  @is_invm
219
342
  end
@@ -370,9 +493,9 @@ module HornetQ
370
493
  end
371
494
  end
372
495
 
373
- # Create a session, start the session, call the supplied block
496
+ # Create a session, start the session, call the supplied block
374
497
  # and once the block completes close the session.
375
- #
498
+ #
376
499
  # See: #session_create for the Parameters
377
500
  #
378
501
  # Returns the result of the block
@@ -417,7 +540,9 @@ module HornetQ
417
540
  def close
418
541
  @sessions.each { |session| session.close }
419
542
  @connection.close if @connection
543
+ @server_locator.close if @server_locator
420
544
  @connection = nil
545
+ @server_locator = nil
421
546
  end
422
547
 
423
548
  # Receive messages in a separate thread when they arrive
@@ -490,12 +615,12 @@ module HornetQ
490
615
  def on_message_statistics
491
616
  @consumers.collect{|consumer| consumer.on_message_statistics}
492
617
  end
493
-
618
+
494
619
  # Start all sessions managed by this connection
495
- #
620
+ #
496
621
  # Sessions created via #create_session are not managed unless
497
622
  # :managed => true was specified when the session was created
498
- #
623
+ #
499
624
  # Session are Only managed when created through the following methods:
500
625
  # Connection#on_message
501
626
  # Connection#create_session And :managed => true
@@ -513,5 +638,5 @@ module HornetQ
513
638
  @sessions.each {|session| session.stop}
514
639
  end
515
640
  end
516
- end
641
+ end
517
642
  end
@@ -284,17 +284,16 @@ module Java::org.hornetq.api.core.client::ClientSession
284
284
  end
285
285
 
286
286
  # To be consistent create Requestor from Session
287
- def create_requestor(request_address)
288
- #Java::org.hornetq.api.core.client::ClientRequestor.new(self, request_address);
289
- HornetQ::Client::RequestorPattern.new(self, request_address)
287
+ def create_requestor(request_address, reply_address=nil, reply_queue=nil)
288
+ HornetQ::Client::RequestorPattern.new(self, request_address, reply_address, reply_queue)
290
289
  end
291
290
 
292
291
  # Creates a RequestorPattern to send a request and to synchronously wait for
293
292
  # the reply, call the supplied block, then close the requestor
294
293
  # Returns the result from the block
295
- def requestor(request_address,&block)
294
+ def requestor(request_address, reply_address=nil, reply_queue=nil, &block)
296
295
  begin
297
- requestor = self.create_requestor(request_address)
296
+ requestor = self.create_requestor(request_address, reply_address, reply_queue)
298
297
  block.call(requestor)
299
298
  ensure
300
299
  requestor.close if requestor
@@ -53,6 +53,12 @@
53
53
  #
54
54
  # long message_id()
55
55
  # Returns the messageID
56
+ # This is an internal message id used by HornetQ itself, it cannot be
57
+ # set by the user.
58
+ # The message is only visible when consuming messages, when producing
59
+ # messages the message_id is Not returned to the caller
60
+ # Use user_id to carry user supplied message id's for correlating
61
+ # responses to requests
56
62
  #
57
63
  # byte priority()
58
64
  # Returns the message priority.
@@ -136,14 +142,22 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
136
142
  # requestor = session.create_requestor('Request Queue')
137
143
  #
138
144
  def reply_to_address=(name)
139
- val = nil
140
- if name.is_a? Java::org.hornetq.api.core::SimpleString
141
- val = name
142
- else
143
- val = Java::org.hornetq.api.core::SimpleString.new(name.to_s)
144
- end
145
-
146
- put_string_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME, val)
145
+ put_string_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME, HornetQ::as_simple_string(name))
146
+ end
147
+
148
+ # Generate a new user_id
149
+ #
150
+ # Sets the user_id to a newly generated id, using a UUID algorithm
151
+ #
152
+ # The user_id is similar to the message_id in other JMS based messaging systems
153
+ # in fact the HornetQ JMS API uses the user_id as the JMS Message ID.
154
+ #
155
+ # The internal message_id is set by the HornetQ Server and is Not returned
156
+ # when sending messages
157
+ #
158
+ # Returns generated user_id
159
+ def generate_user_id
160
+ self.user_id = Java::org.hornetq.utils::UUIDGenerator.instance.generateUUID
147
161
  end
148
162
 
149
163
  # Returns the message type as one of the following symbols
@@ -349,7 +363,7 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
349
363
  :priority => priority,
350
364
  :timestamp => timestamp,
351
365
  :type_sym => type_sym,
352
- :user_id => user_id,
366
+ :user_id => user_id.nil? ? nil : user_id.to_s,
353
367
  }
354
368
  end
355
369
 
@@ -357,5 +371,5 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
357
371
  def inspect
358
372
  "#{self.class.name}:\nBody: #{body.inspect}\nAttributes: #{attributes.inspect}\nProperties: #{properties.inspect}"
359
373
  end
360
-
374
+
361
375
  end
@@ -1,30 +1,111 @@
1
1
  module HornetQ::Client
2
2
 
3
3
  # Implements the Requestor Pattern
4
- # Send a request to a server and wait for a reply
4
+ # Send a request to a server and wait for a reply
5
+ # Parameters
6
+ # * session
7
+ # The session to use processing this request
8
+ # Note: Sessions cannot be shared concurrently by multiple threads
9
+ # * request_address
10
+ # Address to send requests to.
11
+ # It is expected that process listening to requests at this address has
12
+ # implemented the ServerPattern
13
+ # * reply_address
14
+ # If supplied the reply_address must already exist and will be used for
15
+ # receiving responses
16
+ # If not supplied a temporary queue will be created and used by this instance
17
+ # of the RequestorPattern
18
+ # This optional parameter is normally not used
19
+ # * reply_queue
20
+ # If a reply_address is supplied, the reply_queue name can be supplied if it
21
+ # differs from reply_address
5
22
  class RequestorPattern
6
- def initialize(session, request_address)
23
+ def initialize(session, request_address, reply_address=nil, reply_queue=nil)
7
24
  @session = session
8
25
  @producer = session.create_producer(request_address)
9
- reply_queue = "#{request_address}.#{Java::java.util::UUID.randomUUID.toString}"
10
- begin
11
- session.create_temporary_queue(reply_queue, reply_queue)
12
- rescue NativeException => exc
13
- p exc
26
+ if reply_address
27
+ @reply_address = reply_address
28
+ @reply_queue = reply_queue || reply_address
29
+ @destroy_temp_queue = false
30
+ else
31
+ @reply_queue = @reply_address = "#{request_address}.#{Java::java.util::UUID.randomUUID.toString}"
32
+ begin
33
+ session.create_temporary_queue(@reply_address, @reply_queue)
34
+ @destroy_temp_queue = true
35
+ rescue NativeException => exc
36
+ p exc
37
+ end
14
38
  end
15
- @consumer = session.create_consumer(reply_queue)
16
39
  end
17
-
40
+
41
+ # Synchronous Request and wait for reply
42
+ #
43
+ # Returns the message received, or nil if no message was received in the
44
+ # specified timeout.
45
+ #
46
+ # The supplied request_message is updated as follows
47
+ # * The property JMSReplyTo is set to the name of the reply to address
48
+ # * Creates and sets the message user_id if not already set
49
+ # * #TODO: The expiry is set to the message timeout if not already set
50
+ #
51
+ # Note:
52
+ # * The request will only look for a reply message with the same
53
+ # user_id (message id) as the message that was sent. This is critical
54
+ # since a previous receive may have timed out and we do not want
55
+ # to pickup the reponse to an earlier request
56
+ #
57
+ # To receive a message after a timeout, call wait_for_reply with a nil message
58
+ # id to receive any message on the queue
59
+ #
60
+ # Use: submit_request & then wait_for_reply to break it into
61
+ # two separate calls
18
62
  def request(request_message, timeout)
19
- request_message.putStringProperty(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME, @consumer.queue_name);
63
+ #TODO set message expiry to timeout if not already set
64
+ message_id = submit_request(request_message)
65
+ wait_for_reply(message_id, timeout)
66
+ end
67
+
68
+ # Asynchronous Request
69
+ # Use: submit_request & then wait_for_reply to break the request into
70
+ # two separate calls.
71
+ #
72
+ # For example, submit the request now, do some work, then later on
73
+ # in the same thread wait for the reply.
74
+ #
75
+ # The supplied request_message is updated as follows
76
+ # * The property JMSReplyTo is set to the name of the reply to address
77
+ # * Creates and sets the message user_id if not already set
78
+ # * #TODO: The expiry is set to the message timeout if not already set
79
+ #
80
+ # Returns Message id of the message that was sent
81
+ def submit_request(request_message)
82
+ request_message.reply_to_address = @reply_address
83
+ request_message.generate_user_id unless request_message.user_id
20
84
  @producer.send(request_message)
21
- @consumer.receive(timeout)
85
+ request_message.user_id
86
+ end
87
+
88
+ # Asynchronous wait for reply
89
+ #
90
+ # Parameters:
91
+ # user_id: the user defined id to correlate a response for
92
+ #
93
+ # Supply a nil user_id to receive any message from the queue
94
+ #
95
+ # Returns the message received
96
+ #
97
+ # Note: Call submit_request before calling this method
98
+ def wait_for_reply(user_id, timeout)
99
+ # We only want the reply to the supplied message_id, so set filter on message id
100
+ filter = "#{Java::org.hornetq.api.core::FilterConstants::HORNETQ_USERID} = 'ID:#{user_id}'" if user_id
101
+ @session.consumer(:queue_name => @reply_queue, :filter=>filter) do |consumer|
102
+ consumer.receive(timeout)
103
+ end
22
104
  end
23
105
 
24
106
  def close
107
+ @session.delete_queue(@reply_queue) if @destroy_temp_queue
25
108
  @producer.close if @producer
26
- @consumer.close if @consumer
27
- @session.delete_queue(@consumer.queue_name)
28
109
  end
29
110
  end
30
111
 
@@ -35,6 +35,7 @@ module HornetQ::Client
35
35
  reply_message.durable = request_message.durable?
36
36
  # Send request message id back in reply message for correlation purposes
37
37
  reply_message.user_id = request_message.user_id
38
+ #TODO: Also need to include other attributes such as Expiry
38
39
  # Send to Reply to address supplied by the caller
39
40
  @producer.send(request_message.reply_to_address, reply_message)
40
41
  #puts "Sent reply to #{reply_to.to_s}: #{reply_message.inspect}"
Binary file
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: jruby-hornetq
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.3
5
+ version: 0.4.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Reid Morrison
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2011-03-24 00:00:00 -04:00
14
+ date: 2011-04-01 00:00:00 -04:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -59,6 +59,7 @@ files:
59
59
  - examples/advanced/server.rb
60
60
  - examples/client-server/client.rb
61
61
  - examples/client-server/server.rb
62
+ - examples/producer-consumer/browse_all.rb
62
63
  - examples/producer-consumer/consume_all.rb
63
64
  - examples/producer-consumer/consume_on_message.rb
64
65
  - examples/producer-consumer/consumer.rb