jruby-hornetq 0.2.0.alpha

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.
@@ -0,0 +1,388 @@
1
+ require 'uri'
2
+
3
+ module HornetQClient
4
+
5
+ # Import Message Constants
6
+ import Java::org.hornetq.api.core.Message
7
+
8
+ # Netty Class name
9
+ NETTY_CLASS_NAME ='org.hornetq.core.remoting.impl.netty.NettyConnectorFactory'
10
+ INVM_CLASS_NAME = 'org.hornetq.core.remoting.impl.invm.InVMAcceptorFactory'
11
+ DEFAULT_NETTY_PORT = 5445
12
+
13
+ class Factory
14
+ # Create a new Factory from which sessions can be created
15
+ #
16
+ # Parameters:
17
+ # * a Hash consisting of one or more of the named parameters
18
+ # * Summary of parameters and their default values
19
+ # HornetQClient::Factory.new(
20
+ # :uri => 'hornetq://localhost',
21
+ # :ack_batch_size => ,
22
+ # :auto_group => ,
23
+ # :block_on_acknowledge => ,
24
+ # :block_on_durable_send => ,
25
+ # :block_on_non_durable_send => ,
26
+ # :cache_large_messages_client => ,
27
+ # :call_timeout => ,
28
+ # :client_failure_check_period => ,
29
+ # :confirmation_window_size => ,
30
+ # :connection_load_balancing_policy_class_name => ,
31
+ # :connection_ttl => ,
32
+ # :consumer_max_rate => ,
33
+ # :consumer_window_size => ,
34
+ # :discovery_address => ,
35
+ # :discovery_initial_wait_timeout => ,
36
+ # :discovery_port => ,
37
+ # :discovery_refresh_timeout => ,
38
+ # :failover_on_initial_connection => true,
39
+ # :failover_on_server_shutdown => true,
40
+ # :group_id => ,
41
+ # :initial_message_packet_size => ,
42
+ # :java_object => ,
43
+ # :local_bind_address => ,
44
+ # :max_retry_interval => ,
45
+ # :min_large_message_size => ,
46
+ # :pre_acknowledge => ,
47
+ # :producer_max_rate => ,
48
+ # :producer_window_size => ,
49
+ # :reconnect_attempts => 1,
50
+ # :retry_interval => ,
51
+ # :retry_interval_multiplier => ,
52
+ # :scheduled_thread_pool_max_size => ,
53
+ # :static_connectors => ,
54
+ # :thread_pool_max_size => ,
55
+ # :use_global_pools =>
56
+ # )
57
+ #
58
+ # Mandatory Parameters
59
+ # * :uri
60
+ # * The hornetq uri as to which server to connect with and which
61
+ # transport protocol to use. Format:
62
+ # hornetq://server:port,backupserver:port/?protocol=[netty|discover]
63
+ # * To use the default netty transport
64
+ # hornetq://server:port
65
+ # * To use the default netty transport and specify a backup server
66
+ # hornetq://server:port,backupserver:port
67
+ # * To use auto-discovery
68
+ # hornetq://server:port/?protocol=discovery
69
+ # * To use HornetQ within the current JVM
70
+ # hornetq://invm
71
+ #
72
+ # Optional Parameters
73
+ # * :ack_batch_size
74
+ # * :auto_group
75
+ # * :block_on_acknowledge
76
+ # * :block_on_durable_send
77
+ # * :block_on_non_durable_send
78
+ # * :cache_large_messages_client
79
+ # * :call_timeout
80
+ # * :client_failure_check_period
81
+ # * :confirmation_window_size
82
+ # * :connection_load_balancing_policy_class_name
83
+ # * :connection_ttl
84
+ # * :consumer_max_rate
85
+ # * :consumer_window_size
86
+ # * :discovery_address
87
+ # * :discovery_initial_wait_timeout
88
+ # * :discovery_port
89
+ # * :discovery_refresh_timeout
90
+ # * :failover_on_initial_connection
91
+ # * :failover_on_server_shutdown
92
+ # * :group_id
93
+ # * :initial_message_packet_size
94
+ # * :java_object
95
+ # * :local_bind_address
96
+ # * :max_retry_interval
97
+ # * :min_large_message_size
98
+ # * :pre_acknowledge
99
+ # * :producer_max_rate
100
+ # * :producer_window_size
101
+ # * :reconnect_attempts
102
+ # * :retry_interval
103
+ # * :retry_interval_multiplier
104
+ # * :scheduled_thread_pool_max_size
105
+ # * :static_connectors
106
+ # * :thread_pool_max_size
107
+ # * :use_global_pools
108
+
109
+ def initialize(parms={})
110
+ raise "Missing :uri under :connector in config" unless uri = parms[:uri]
111
+ # TODO: Support :uri as an array for cluster configurations
112
+
113
+ scheme, userinfo, host, port, registry, path, opaque, query, fragment = URI.split(uri)
114
+ raise InvalidURIError,"bad URI(only scheme hornetq:// is supported): #{uri}" unless scheme == 'hornetq'
115
+ backup_host = backup_port = nil
116
+
117
+ # Check for multiple server names
118
+ if registry
119
+ host, backup_host = registry.split(',')
120
+ host, port = host.split(':')
121
+ backup_host, backup_port = backup_host.split(':')
122
+ end
123
+
124
+ # Extract settings passed in query
125
+ settings = {}
126
+ if query
127
+ query.split(';').each do |i|
128
+ key, value = i.split('=')
129
+ settings[key] = value
130
+ end
131
+ end
132
+
133
+ # Determine transport protocol
134
+ factory = nil
135
+ # In-VM Transport has no fail-over or additional parameters
136
+ if host == 'invm'
137
+ transport = Java::org.hornetq.api.core.TransportConfiguration.new(INVM_CLASS_NAME)
138
+ factory = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(transport)
139
+ elsif settings[:protocol]
140
+ # Auto-Discovery just has a host name and port
141
+ if settings[:protocol] == 'discovery'
142
+ factory = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(host, port)
143
+ elsif settings[:protocol] != 'netty'
144
+ raise "Unknown HornetQ protocol:#{settings[:protocol]}"
145
+ end
146
+ end
147
+
148
+ # Unless already created, then the factory will use the netty protocol
149
+ unless factory
150
+ # Primary Transport
151
+ raise "Mandatory hostname missing in :uri" unless host
152
+ port ||= DEFAULT_NETTY_PORT
153
+ transport = Java::org.hornetq.api.core.TransportConfiguration.new(NETTY_CLASS_NAME, {'host' => host, 'port' => Java::java.lang.Integer.new(port)})
154
+
155
+ # Check for backup server connection information
156
+ if backup_host
157
+ backup_port ||= DEFAULT_NETTY_PORT
158
+ backup_transport = Java::org.hornetq.api.core.TransportConfiguration.new(NETTY_CLASS_NAME, {'host' => backup_host, 'port' => Java::java.lang.Integer.new(backup_port)})
159
+ factory = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(transport, backup_transport)
160
+ else
161
+ factory = Java::org.hornetq.api.core.client.HornetQClient.create_client_session_factory(transport)
162
+ end
163
+ end
164
+
165
+ # If any other options were supplied, apply them to the created Factory instance
166
+ parms.each_pair do |key, val|
167
+ next if key == :uri
168
+ method = key.to_s+'='
169
+ if factory.respond_to? method
170
+ factory.send method, val
171
+ #puts "Debug: #{key} = #{factory.send key}" if factory.respond_to? key.to_sym
172
+ else
173
+ puts "Warning: Option:#{key}, with value:#{val} is invalid and being ignored"
174
+ end
175
+ end
176
+
177
+ @factory = factory
178
+ end
179
+
180
+ # Create a new HornetQ session
181
+ #
182
+ # If a block is passed in the block will be passed the session as a parameter
183
+ # and this method will return the result of the block. The session is
184
+ # always closed once the proc completes
185
+ #
186
+ # If no block is passed, a new session is returned and it is the responsibility
187
+ # of the caller to close the session
188
+ #
189
+ # Note:
190
+ # * The returned session MUST be closed once complete
191
+ # factory = HornetQClient::Factory.new(:uri => 'hornetq://localhost/')
192
+ # session = factory.create_session
193
+ # ...
194
+ # session.close
195
+ # factory.close
196
+ # * It is recommended to rather call HornetQClient::Factory.create_session
197
+ # so that all resouces are closed automatically
198
+ # HornetQClient::Factory.create_session(:uri => 'hornetq://localhost/') do |session|
199
+ # ...
200
+ # end
201
+ #
202
+ # Returns:
203
+ # * A new HornetQ ClientSession
204
+ # * See org.hornetq.api.core.client.ClientSession for documentation on returned object
205
+ #
206
+ # Throws:
207
+ # * NativeException
208
+ # * ...
209
+ #
210
+ # Example:
211
+ # require 'hornetq'
212
+ #
213
+ # factory = nil
214
+ # begin
215
+ # factory = HornetQClient::Factory.new(:uri => 'hornetq://localhost/')
216
+ # factory.create_session do |session|
217
+ #
218
+ # # Create a new queue
219
+ # session.create_queue('Example', 'Example', true)
220
+ #
221
+ # # Create a producer to send messages
222
+ # producer = session.create_producer('Example')
223
+ #
224
+ # # Create a Text Message
225
+ # message = session.create_message(HornetQClient::Message::TEXT_TYPE,true)
226
+ # message << 'Hello World'
227
+ #
228
+ # # Send the message
229
+ # producer.send(message)
230
+ # end
231
+ # ensure
232
+ # factory.close if factory
233
+ # end
234
+ #
235
+ # Example:
236
+ # require 'hornetq'
237
+ #
238
+ # factory = nil
239
+ # session = nil
240
+ # begin
241
+ # factory = HornetQClient::Factory.new(:uri => 'hornetq://localhost/')
242
+ # session = factory.create_session
243
+ #
244
+ # # Create a new queue
245
+ # session.create_queue('Example', 'Example', true)
246
+ #
247
+ # # Create a producer to send messages
248
+ # producer = session.create_producer('Example')
249
+ #
250
+ # # Create a Text Message
251
+ # message = session.create_message(HornetQClient::Message::TEXT_TYPE,true)
252
+ # message.body_buffer.write_string('Hello World')
253
+ #
254
+ # # Send the message
255
+ # producer.send(message)
256
+ # ensure
257
+ # session.close if session
258
+ # factory.close if factory
259
+ # end
260
+ #
261
+ # Parameters:
262
+ # * a Hash consisting of one or more of the named parameters
263
+ # * Summary of parameters and their default values
264
+ # factory.create_session(
265
+ # :username => 'my_username', # Default is no authentication
266
+ # :password => 'password', # Default is no authentication
267
+ # :xa => false,
268
+ # :auto_commit_sends => true,
269
+ # :auto_commit_acks => true,
270
+ # :pre_acknowledge => false,
271
+ # :ack_batch_size => 1
272
+ # )
273
+ #
274
+ # Mandatory Parameters
275
+ # * None
276
+ #
277
+ # Optional Parameters
278
+ # * :username
279
+ # * The user name. To create an authenticated session
280
+ #
281
+ # * :password
282
+ # * The user password. To create an authenticated session
283
+ #
284
+ # * :xa
285
+ # * Whether the session supports XA transaction semantics or not
286
+ #
287
+ # * :auto_commit_sends
288
+ # * true: automatically commit message sends
289
+ # * false: commit manually
290
+ #
291
+ # * :auto_commit_acks
292
+ # * true: automatically commit message acknowledgement
293
+ # * false: commit manually
294
+ #
295
+ # * :pre_acknowledge
296
+ # * true: to pre-acknowledge messages on the server
297
+ # * false: to let the client acknowledge the messages
298
+ # * Note: It is possible to pre-acknowledge messages on the server so that the
299
+ # client can avoid additional network trip to the server to acknowledge
300
+ # messages. While this increases performance, this does not guarantee
301
+ # delivery (as messages can be lost after being pre-acknowledged on the
302
+ # server). Use with caution if your application design permits it.
303
+ #
304
+ # * :ack_batch_size
305
+ # * the batch size of the acknowledgements
306
+ #
307
+ def create_session(parms={}, &proc)
308
+ raise "HornetQClient Factory Already Closed" unless @factory
309
+ if proc
310
+ session = nil
311
+ result = nil
312
+ begin
313
+ #session = @factory.create_session(true, true)
314
+ session = @factory.create_session(
315
+ parms[:username],
316
+ parms[:password],
317
+ parms[:xa] || false,
318
+ parms[:auto_commit_sends].nil? ? true : parms[:auto_commit_sends],
319
+ parms[:auto_commit_acks].nil? ? true : parms[:auto_commit_acks],
320
+ parms[:pre_acknowledge] || false,
321
+ parms[:ack_batch_size] || 1)
322
+ result = proc.call(session)
323
+ ensure
324
+ session.close if session
325
+ end
326
+ result
327
+ else
328
+ @factory.create_session(
329
+ parms[:username],
330
+ parms[:password],
331
+ parms[:xa] || false,
332
+ parms[:auto_commit_sends].nil? ? true : parms[:auto_commit_sends],
333
+ parms[:auto_commit_acks].nil? ? true : parms[:auto_commit_acks],
334
+ parms[:pre_acknowledge] || false,
335
+ parms[:ack_batch_size] || 1)
336
+ end
337
+ end
338
+
339
+ # Create a Session pool
340
+ def create_session_pool(parms={})
341
+ SessionPool.new(self, parms)
342
+ end
343
+
344
+ # Close Factory connections
345
+ def close
346
+ @factory.close if @factory
347
+ @factory = nil
348
+ end
349
+
350
+ # Create a new Factory and Session
351
+ #
352
+ # Creates a new factory and session, then passes the session to the supplied
353
+ # block. Upon completion the session and factory are both closed
354
+ # See Factory::initialize and Factory::create_session for the list
355
+ # of parameters
356
+ def self.create_session(parms={},&proc)
357
+ raise "Missing mandatory code block" unless proc
358
+ factory = nil
359
+ session = nil
360
+ begin
361
+ factory = self.new(parms[:connector] || {})
362
+ session = factory.create_session(parms[:session] || {}, &proc)
363
+ ensure
364
+ factory.close if factory
365
+ end
366
+ end
367
+
368
+ # Call the supplied code block after creating a factory instance
369
+ # See initialize for the parameter list
370
+ # The factory is closed before returning
371
+ #
372
+ # Returns the result of the code block
373
+ def self.create_factory(parms={}, &proc)
374
+ raise "Missing mandatory code block" unless proc
375
+ factory = nil
376
+ result = nil
377
+ begin
378
+ factory=self.new(parms)
379
+ result = proc.call(factory)
380
+ ensure
381
+ factory.close
382
+ end
383
+ result
384
+ end
385
+
386
+ end
387
+
388
+ end
Binary file
Binary file
@@ -0,0 +1,16 @@
1
+ # Add methods to Session Interface
2
+ module Java::org.hornetq.api.core.client::ClientSession
3
+
4
+ # To be consistent create Requestor from Session
5
+ def create_requestor(request_address)
6
+ #Java::org.hornetq.api.core.client::ClientRequestor.new(self, request_address);
7
+ HornetQClient::ClientRequestor.new(self, request_address)
8
+ end
9
+
10
+ # Create a server handler for receiving requests and responding with
11
+ # replies to the supplied address
12
+ def create_server(input_queue, timeout=0)
13
+ HornetQClient::ClientServer.new(self, input_queue, timeout)
14
+ end
15
+ end
16
+
@@ -0,0 +1,166 @@
1
+
2
+ # Cannot add to the interface Java::org.hornetq.api.core::Message because these
3
+ # methods access instance variables in the Java object
4
+ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
5
+ # Attributes
6
+ # attr_accessor :address, :type, :durable, :expiration, :priority, :timestamp, :user_id
7
+
8
+ # Is this a request message for which a reply is expected?
9
+ def request?
10
+ contains_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME)
11
+ end
12
+
13
+ # Return the Reply To Queue Name as a string
14
+ def reply_to_queue_name
15
+ get_string_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME)
16
+ end
17
+
18
+ # Set the Reply To Queue Name
19
+ # When supplied, the consumer of the message is expected to send a response to the
20
+ # specified queue. However, this is by convention, so no response is guaranteed
21
+ # Note: Rather than set this directly, consider creating a ClientRequestor:
22
+ # requestor = session.create_requestor('Request Queue')
23
+ #
24
+ def reply_to_queue_name=(name)
25
+ val = nil
26
+ if name.is_a? Java::org.hornetq.api.core::SimpleString
27
+ val = name
28
+ else
29
+ val = Java::org.hornetq.api.core::SimpleString.new(name.to_s)
30
+ end
31
+
32
+ put_string_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME, val)
33
+ end
34
+
35
+ # Return the size of the encoded message
36
+ # attr_reader :encode_size
37
+
38
+ # Return the body for this message
39
+ # TODO: Do remaining message Types
40
+ #
41
+ # WARNING: This method can only be called ONCE!
42
+ # WARNING: Do not call after setting the body otherwise the send will have
43
+ # an empty body
44
+ def body
45
+ case type
46
+ when Java::org.hornetq.api.core.Message::BYTES_TYPE #4
47
+ when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
48
+ when Java::org.hornetq.api.core.Message::MAP_TYPE #5
49
+ Java::org.hornetq.utils::TypedProperties.new.decode(body_buffer)
50
+ when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
51
+ when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
52
+ when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
53
+ body_buffer.read_nullable_simple_string.to_string
54
+ else
55
+ raise "Unknown Message Type, use Message#body_buffer instead"
56
+ end
57
+ end
58
+
59
+ # Write data into the message body
60
+ #
61
+ # Data is automatically converted based on the message type
62
+ #
63
+ # TODO Support non-string Types
64
+ def <<(data)
65
+ case type
66
+ when Java::org.hornetq.api.core.Message::BYTES_TYPE #4
67
+ when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
68
+ raise "Cannot use Message#<< when the Message#type has not been set"
69
+ when Java::org.hornetq.api.core.Message::MAP_TYPE #5
70
+ if data.class == Java::org.hornetq.utils::TypedProperties
71
+ body_buffer.reset_writer_index
72
+ data.encode(body_buffer)
73
+ elsif data.responds_to? :each_pair
74
+ properties = Java::org.hornetq.utils::TypedProperties.new
75
+ properties.from_hash(data)
76
+ body_buffer.reset_writer_index
77
+ properties.encode(body_buffer)
78
+ end
79
+ when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
80
+ when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
81
+ when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
82
+ if data.class == Java::org.hornetq.api.core::SimpleString
83
+ body_buffer.writeNullableSimpleString(data)
84
+ else
85
+ body_buffer.writeNullableSimpleString(Java::org.hornetq.api.core::SimpleString.new(data.to_s))
86
+ end
87
+ else
88
+ raise "Unknown Message Type, use Message#body_buffer instead"
89
+ end
90
+ end
91
+
92
+ # Get a property
93
+ def [](key)
94
+ getObjectProperty(key.to_s)
95
+ end
96
+
97
+ # Set a property
98
+ # TODO: Does it need proper translation, otherwise it will be a Ruby object
99
+ def []=(key,value)
100
+ putObjectProperty(key, value)
101
+ end
102
+
103
+ # Does this message include the supplied property?
104
+ def include?(key)
105
+ # Ensure a Ruby true is returned
106
+ property_exists(key.to_s) == true
107
+ end
108
+
109
+ # call-seq:
110
+ # body_buffer
111
+ #
112
+ # Return the message body as a HornetQBuffer
113
+ #
114
+
115
+ # call-seq:
116
+ # to_map
117
+ #
118
+ # Return the Message as a Map
119
+ #
120
+
121
+ # call-seq:
122
+ # remove_property(key)
123
+ #
124
+ # Remove a property
125
+
126
+ # call-seq:
127
+ # contains_property(key)
128
+ #
129
+ # Returns true if this message contains a property with the given key
130
+ # TODO: Symbols?
131
+
132
+ # Return TypedProperties
133
+ def getProperties
134
+ properties
135
+ end
136
+
137
+ # Iterate over all the properties
138
+ #
139
+ def properties_each_pair(&proc)
140
+ enum = getPropertyNames
141
+ while enum.has_more_elements
142
+ key = enum.next_element
143
+ proc.call key, getObjectProperty(key)
144
+ end
145
+ end
146
+
147
+ # Return all message Attributes as a hash
148
+ def attributes
149
+ {
150
+ :address => address.nil? ? '' : address.to_string,
151
+ :type => type,
152
+ :durable => durable,
153
+ :expiration => expiration,
154
+ :priority => priority,
155
+ :timestamp => timestamp,
156
+ :user_id => user_id,
157
+ :encode_size => encode_size
158
+ }
159
+ end
160
+
161
+ # Does not include the body since it can only read once
162
+ def inspect
163
+ "#{self.class.name}:\nAttributes: #{attributes.inspect}\nProperties: #{properties.inspect}"
164
+ end
165
+
166
+ end
@@ -0,0 +1,5 @@
1
+ # TODO Support send(String)
2
+ # TODO Support send(:data => string, :durable=>true, :address=>'MyAddress')
3
+ #
4
+ class Java::org.hornetq.core.client.impl::ClientProducerImpl
5
+ end
@@ -0,0 +1,60 @@
1
+ # Used by HornetQ to move around HashMap messages
2
+ # Ruby methods added to make it behave like a Ruby Hash
3
+ class Java::org.hornetq.utils::TypedProperties
4
+ # Get a property
5
+ def [](key)
6
+ value = getProperty(key)
7
+ (value.class == Java::org.hornetq.api.core::SimpleString) ? value.to_s : value
8
+ end
9
+
10
+ # Set a property
11
+ # Currently supports Long, Double, Boolean
12
+ # TODO: Not supported Byte, Bytes, Short, Int, FLoat, Char
13
+ def []=(key,val)
14
+ case
15
+ when val.class == Fixnum # 1
16
+ putLongProperty(key,val)
17
+ when val.class == Float #1.1
18
+ putDoubleProperty(key,val)
19
+ when val.class == Bignum # 11111111111111111
20
+ putLongProperty(key,val)
21
+ when (val.class == TrueClass) || (val.class == FalseClass)
22
+ putBooleanProperty(key,val)
23
+ when val.class == NilClass
24
+ setSimpleStringProperty(key,null)
25
+ when val.class == Java::org.hornetq.api.core::SimpleString
26
+ setSimpleStringProperty(key,val)
27
+ else
28
+ putSimpleStringProperty(key,val.to_s)
29
+ end
30
+ end
31
+
32
+ # Iterate through each key,value pair
33
+ def each_pair(&proc)
34
+ it = property_names.iterator
35
+ while it.has_next
36
+ key = it.next
37
+ proc.call(key.to_string, self[key])
38
+ end
39
+ end
40
+
41
+ # Convert Properties to a Ruby Hash
42
+ def to_h
43
+ h = {}
44
+ each_pair do |key, value|
45
+ h[key] = value
46
+ end
47
+ h
48
+ end
49
+
50
+ # Write Hash values into this TyedProperties instance
51
+ def from_h(hash)
52
+ hash.each_pair do |key,value|
53
+ self[key] = value
54
+ end
55
+ end
56
+
57
+ def inspect
58
+ "#{self.class.name}: #{to_h.inspect}"
59
+ end
60
+ end