jruby-hornetq 0.2.0.alpha

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