jruby-hornetq 0.3.3 → 0.4.0

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