jruby-hornetq 0.2.0.alpha → 0.2.1.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.
Files changed (46) hide show
  1. data/HISTORY.md +8 -0
  2. data/README.md +88 -5
  3. data/Rakefile +4 -4
  4. data/bin/hornetq_server +26 -55
  5. data/examples/{batch_client.rb → client/advanced/batch_client.rb} +11 -4
  6. data/examples/{client.rb → client/advanced/client.rb} +8 -10
  7. data/examples/{consumer.rb → client/advanced/consumer.rb} +2 -2
  8. data/examples/{hornetq.yml → client/advanced/hornetq.yml} +11 -10
  9. data/examples/{multi_client.rb → client/advanced/multi_client.rb} +6 -3
  10. data/examples/{multi_consumer.rb → client/advanced/multi_consumer.rb} +2 -2
  11. data/examples/{producer.rb → client/advanced/producer.rb} +3 -4
  12. data/examples/{server.rb → client/advanced/server.rb} +6 -9
  13. data/examples/client/client.rb +31 -0
  14. data/examples/client/consumer.rb +22 -0
  15. data/examples/client/invm.rb +38 -0
  16. data/examples/client/producer.rb +21 -0
  17. data/examples/client/server.rb +31 -0
  18. data/examples/server/backup_server.yml +6 -0
  19. data/examples/server/live_server.yml +1 -0
  20. data/examples/server/standalone_server.yml +1 -0
  21. data/lib/data/bindings/hornetq-bindings-1.bindings +0 -0
  22. data/lib/data/bindings/hornetq-bindings-2.bindings +0 -0
  23. data/lib/hornetq.rb +30 -9
  24. data/lib/hornetq/client.rb +19 -0
  25. data/lib/hornetq/{hornet_q_client.rb → client/factory.rb} +85 -88
  26. data/lib/hornetq/{org_hornetq_api_core_client_client_session.rb → client/org_hornetq_api_core_client_client_session.rb} +2 -2
  27. data/lib/hornetq/{org_hornetq_core_client_impl_client_message_impl.rb → client/org_hornetq_core_client_impl_client_message_impl.rb} +62 -2
  28. data/lib/hornetq/{org_hornetq_core_client_impl_client_producer_impl.rb → client/org_hornetq_core_client_impl_client_producer_impl.rb} +0 -0
  29. data/lib/hornetq/{org_hornetq_utils_typed_properties.rb → client/org_hornetq_utils_typed_properties.rb} +0 -0
  30. data/lib/hornetq/{client_requestor.rb → client/requestor.rb} +2 -2
  31. data/lib/hornetq/{client_server.rb → client/server.rb} +2 -2
  32. data/lib/hornetq/{session_pool.rb → client/session_pool.rb} +20 -20
  33. data/lib/hornetq/{lib → java}/hornetq-core-client.jar +0 -0
  34. data/lib/hornetq/{lib → java}/hornetq-core.jar +0 -0
  35. data/lib/hornetq/{lib → java}/netty.jar +0 -0
  36. data/lib/hornetq/org_hornetq_core_server_hornet_q_server.rb +13 -0
  37. data/lib/hornetq/server.rb +12 -0
  38. data/lib/hornetq/server/factory.rb +86 -0
  39. data/lib/hornetq/uri.rb +59 -0
  40. data/test/server_factory_test.rb +184 -0
  41. data/test/uri_test.rb +74 -0
  42. metadata +44 -28
  43. data/examples/backup_server.yml +0 -4
  44. data/examples/live_server.yml +0 -5
  45. data/examples/run_broker.rb +0 -59
  46. data/examples/standalone_server.yml +0 -3
@@ -4,13 +4,13 @@ module Java::org.hornetq.api.core.client::ClientSession
4
4
  # To be consistent create Requestor from Session
5
5
  def create_requestor(request_address)
6
6
  #Java::org.hornetq.api.core.client::ClientRequestor.new(self, request_address);
7
- HornetQClient::ClientRequestor.new(self, request_address)
7
+ HornetQ::Client::Requestor.new(self, request_address)
8
8
  end
9
9
 
10
10
  # Create a server handler for receiving requests and responding with
11
11
  # replies to the supplied address
12
12
  def create_server(input_queue, timeout=0)
13
- HornetQClient::ClientServer.new(self, input_queue, timeout)
13
+ HornetQ::Client::Server.new(self, input_queue, timeout)
14
14
  end
15
15
  end
16
16
 
@@ -18,7 +18,7 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
18
18
  # Set the Reply To Queue Name
19
19
  # When supplied, the consumer of the message is expected to send a response to the
20
20
  # specified queue. However, this is by convention, so no response is guaranteed
21
- # Note: Rather than set this directly, consider creating a ClientRequestor:
21
+ # Note: Rather than set this directly, consider creating a Client::Requestor:
22
22
  # requestor = session.create_requestor('Request Queue')
23
23
  #
24
24
  def reply_to_queue_name=(name)
@@ -42,8 +42,13 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
42
42
  # WARNING: Do not call after setting the body otherwise the send will have
43
43
  # an empty body
44
44
  def body
45
+ # Allow this buffer to be read multiple times
46
+ body_buffer.reset_reader_index
47
+
45
48
  case type
46
49
  when Java::org.hornetq.api.core.Message::BYTES_TYPE #4
50
+ buf = body_buffer
51
+ String.new(buf.read_bytes(buf.readable_bytes))
47
52
  when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
48
53
  when Java::org.hornetq.api.core.Message::MAP_TYPE #5
49
54
  Java::org.hornetq.utils::TypedProperties.new.decode(body_buffer)
@@ -60,10 +65,11 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
60
65
  #
61
66
  # Data is automatically converted based on the message type
62
67
  #
63
- # TODO Support non-string Types
68
+ # DEPRECATED
64
69
  def <<(data)
65
70
  case type
66
71
  when Java::org.hornetq.api.core.Message::BYTES_TYPE #4
72
+ body_buffer.write_bytes(data)
67
73
  when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
68
74
  raise "Cannot use Message#<< when the Message#type has not been set"
69
75
  when Java::org.hornetq.api.core.Message::MAP_TYPE #5
@@ -89,6 +95,60 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
89
95
  end
90
96
  end
91
97
 
98
+ # Set the body of the message
99
+ # The type of the message is automatically set based on the type passed in.
100
+ # The following types are automatically supported in order of priority
101
+ # String => :text
102
+ # Java::org.hornetq.api.core::SimpleString => :text
103
+ # Java::org.hornetq.utils::TypedProperties => :map
104
+ # Hash (actually any object responding to => :map
105
+ def body=(data)
106
+ body_buffer.reset_writer_index
107
+ if data.is_a? String
108
+ # Ruby String
109
+ self.type = Java::org.hornetq.api.core.Message::TEXT_TYPE
110
+ body_buffer.writeNullableSimpleString(Java::org.hornetq.api.core::SimpleString.new(data))
111
+ elsif data.is_a? Java::org.hornetq.api.core::SimpleString
112
+ # SimpleString instance
113
+ self.type = Java::org.hornetq.api.core.Message::TEXT_TYPE
114
+ body_buffer.writeNullableSimpleString(data)
115
+ elsif data.is_a? Java::org.hornetq.utils::TypedProperties
116
+ # TypedProperties
117
+ self.type = Java::org.hornetq.api.core.Message::MAP_TYPE
118
+ data.encode(body_buffer)
119
+ elsif data.responds_to? :each_pair
120
+ # Ruby Hash, or anything that responds to :each_pair
121
+ # TODO What about Hash inside of Hash?
122
+ self.type = Java::org.hornetq.api.core.Message::MAP_TYPE
123
+ properties = Java::org.hornetq.utils::TypedProperties.new
124
+ data.each_pair do |key, val|
125
+ properties[key.to_s] = val
126
+ end
127
+ properties.encode(body_buffer)
128
+ elsif data.responds_to? :to_s
129
+ # Can be converted to a Ruby String
130
+ self.type = Java::org.hornetq.api.core.Message::TEXT_TYPE
131
+ body_buffer.writeNullableSimpleString(Java::org.hornetq.api.core::SimpleString.new(data.to_s))
132
+ else
133
+ # Otherwise Serialize Ruby object
134
+ self.type = Java::org.hornetq.api.core.Message::BYTES_TYPE
135
+ self['encoding', 'jruby']
136
+ # If BYTES type is not working we could use Base64.encode64
137
+ self << Marshal.dump(data)
138
+ self.text = encoded
139
+ end
140
+ end
141
+
142
+ # Serialize JRuby object and base64 encode
143
+ def decode
144
+ if self.get_string_property( 'torquebox_encoding' ) == 'base64'
145
+ serialized = Base64.decode64( self.text )
146
+ Marshal.restore( serialized )
147
+ else
148
+ self.text
149
+ end
150
+ end
151
+
92
152
  # Get a property
93
153
  def [](key)
94
154
  getObjectProperty(key.to_s)
@@ -1,7 +1,7 @@
1
- module HornetQClient
1
+ module HornetQ::Client
2
2
 
3
3
  # Send a request to a server and wait for a reply
4
- class ClientRequestor
4
+ class Requestor
5
5
  def initialize(session, request_address)
6
6
  @session = session
7
7
  @producer = session.create_producer(request_address)
@@ -1,8 +1,8 @@
1
- module HornetQClient
1
+ module HornetQ::Client
2
2
  # Create a Server for receiving requests and replying
3
3
  # to arbitrary queues
4
4
  # Create an instance of this class per thread
5
- class ClientServer
5
+ class Server
6
6
  def initialize(session, request_queue, timeout)
7
7
  @session = session
8
8
  @consumer = session.create_consumer(request_queue)
@@ -1,6 +1,6 @@
1
1
  require 'gene_pool'
2
2
 
3
- module HornetQClient
3
+ module HornetQ::Client
4
4
  # Since a Session can only be used by one thread at a time, we could create
5
5
  # a Session for every thread. That could result in excessive unused Sessions.
6
6
  # An alternative is to create a pool of sessions that can be shared by
@@ -15,11 +15,11 @@ module HornetQClient
15
15
  # end
16
16
  #
17
17
  # Parameters:
18
- # see regular session parameters from: HornetQClient::Factory::create_session
18
+ # see regular session parameters from: HornetQ::Client::Factory::create_session
19
19
  #
20
20
  # Additional parameters for controlling the session pool itself
21
21
  # :pool_name Name of the pool as it shows up in the logger.
22
- # Default: 'HornetQClient::SessionPool'
22
+ # Default: 'HornetQ::Client::SessionPool'
23
23
  # :pool_size Maximum Pool Size. Default: 10
24
24
  # The pool only grows as needed and will never exceed
25
25
  # :pool_size
@@ -35,18 +35,18 @@ module HornetQClient
35
35
  # ....
36
36
  # end
37
37
  class SessionPool
38
- def initialize(factory, parms={})
39
- # Save Session parms since it will be used every time a new session is
38
+ def initialize(factory, params={})
39
+ # Save Session params since it will be used every time a new session is
40
40
  # created in the pool
41
- session_parms = parms.dup
41
+ session_params = params.nil? ? {} : params.dup
42
42
  # TODO Use same logger as HornetQ?
43
43
  # TODO How to shrink unused connections?
44
44
  @pool = GenePool.new(
45
- :name => parms[:pool_name] || self.class.name,
46
- :pool_size => parms[:pool_size] || 10,
47
- :warn_timeout => parms[:pool_warn_timeout] || 5,
48
- :logger => parms[:pool_logger]) do
49
- s = factory.create_session(session_parms)
45
+ :name => session_params[:pool_name] || self.class.name,
46
+ :pool_size => session_params[:pool_size] || 10,
47
+ :warn_timeout => session_params[:pool_warn_timeout] || 5,
48
+ :logger => session_params[:pool_logger]) do
49
+ s = factory.create_session(session_params)
50
50
  # Start the session since it will be used immediately upon creation
51
51
  s.start
52
52
  puts "Creating Session"
@@ -56,7 +56,7 @@ module HornetQClient
56
56
  # Obtain a session from the pool and pass it to the supplied block
57
57
  # The session is automatically returned to the pool once the block completes
58
58
  def session(&block)
59
- @pool.with_connection {|s| block.call(s)}
59
+ @pool.with_connection &block
60
60
  end
61
61
 
62
62
  # Obtain a session from the pool and create a ClientConsumer.
@@ -64,7 +64,7 @@ module HornetQClient
64
64
  # Once the block is complete the consumer is closed and the session is
65
65
  # returned to the pool.
66
66
  #
67
- # See HornetQClient::ClientConsumer for more information on the consumer
67
+ # See HornetQ::Client::ClientConsumer for more information on the consumer
68
68
  # parameters
69
69
  #
70
70
  # Example
@@ -89,12 +89,12 @@ module HornetQClient
89
89
  # Once the block is complete the consumer is closed and the session is
90
90
  # returned to the pool.
91
91
  #
92
- # See HornetQClient::ClientProducer for more information on the producer
92
+ # See HornetQ::Client::ClientProducer for more information on the producer
93
93
  # parameters
94
94
  #
95
95
  # Example
96
96
  # session_pool.producer('MyAddress') do |session, producer|
97
- # message = session.create_message(HornetQClient::Message::TEXT_TYPE,false)
97
+ # message = session.create_message(HornetQ::Client::Message::TEXT_TYPE,false)
98
98
  # message << "#{Time.now}: ### Hello, World ###"
99
99
  # producer.send(message)
100
100
  # end
@@ -110,15 +110,15 @@ module HornetQClient
110
110
  end
111
111
  end
112
112
 
113
- # Obtain a session from the pool and create a ClientRequestor.
113
+ # Obtain a session from the pool and create a Client::Requestor.
114
114
  # Pass both into the supplied block.
115
115
  # Once the block is complete the requestor is closed and the session is
116
116
  # returned to the pool.
117
117
  #
118
- # See HornetQClient::ClientRequestor for more information on the requestor
118
+ # See HornetQ::Client::Requestor for more information on the requestor
119
119
  #
120
120
  # Example
121
- # session_pool.requestor(parms) do |session, requestor|
121
+ # session_pool.requestor(params) do |session, requestor|
122
122
  # ....
123
123
  # end
124
124
  def requestor(address, &block)
@@ -133,12 +133,12 @@ module HornetQClient
133
133
  end
134
134
  end
135
135
 
136
- # Obtain a session from the pool and create a ClientServer.
136
+ # Obtain a session from the pool and create a Client::Server.
137
137
  # Pass both into the supplied block.
138
138
  # Once the block is complete the requestor is closed and the session is
139
139
  # returned to the pool.
140
140
  #
141
- # See HornetQClient::ClientServer for more information on the server
141
+ # See HornetQ::Client::Server for more information on the server
142
142
  #
143
143
  # Example
144
144
  # session_pool.server(queue, timeout) do |session, server|
File without changes
@@ -0,0 +1,13 @@
1
+ # Add methods to Server Interface
2
+ module Java::org.hornetq.core.server::HornetQServer
3
+
4
+ # Shutdown the server when a typical interrupt signal (1,2,15) is caught
5
+ def enable_shutdown_on_signal
6
+ ['HUP', 'INT', 'TERM'].each do |signal_name|
7
+ Signal.trap(signal_name) do
8
+ puts "caught #{signal_name}"
9
+ stop
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module HornetQ
2
+ module Server
3
+ # Only load as needed
4
+ def self.load_requirements
5
+ HornetQ.require_jar 'hornetq-core'
6
+ HornetQ.require_jar 'netty'
7
+ require 'hornetq/org_hornetq_core_server_hornet_q_server'
8
+ end
9
+ end
10
+ end
11
+
12
+ require 'hornetq/server/factory'
@@ -0,0 +1,86 @@
1
+ module HornetQ::Server
2
+
3
+ class Factory
4
+ def self.create_server(parms={})
5
+ HornetQ::Server.load_requirements
6
+
7
+ if parms.kind_of?(String)
8
+ uri = HornetQ::URI.new(parms)
9
+ parms = uri.params
10
+ else
11
+ raise "Missing :uri param in HornetQ::Server.create_server" unless parms[:uri]
12
+ uri = HornetQ::URI.new(parms.delete(:uri))
13
+ # parms override uri params
14
+ parms = uri.params.merge(parms)
15
+ end
16
+ config = Java::org.hornetq.core.config.impl.ConfigurationImpl.new
17
+ data_directory = parms.delete(:data_directory) || HornetQ::DEFAULT_DATA_DIRECTORY
18
+ config.paging_directory = "#{data_directory}/paging"
19
+ config.bindings_directory = "#{data_directory}/bindings"
20
+ config.journal_directory = "#{data_directory}/journal"
21
+ config.large_messages_directory = "#{data_directory}/large-messages"
22
+
23
+ parms.each_pair do |key, val|
24
+ method = key.to_s+'='
25
+ if config.respond_to? method
26
+ config.send method, val
27
+ #puts "Debug: #{key} = #{config.send key}" if config.respond_to? key.to_sym
28
+ else
29
+ puts "Warning: Option:#{key} class=#{key.class} with value:#{val} is invalid and being ignored"
30
+ end
31
+ end
32
+
33
+ if Java::org.hornetq.core.journal.impl.AIOSequentialFileFactory.isSupported
34
+ config.journal_type = Java::org.hornetq.core.server.JournalType::ASYNCIO
35
+ else
36
+ puts("AIO wasn't located on this platform, it will fall back to using pure Java NIO. If your platform is Linux, install LibAIO to enable the AIO journal");
37
+ config.journal_type = Java::org.hornetq.core.server.JournalType::NIO
38
+ end
39
+
40
+ if uri.host == 'invm'
41
+ acceptor = Java::org.hornetq.api.core.TransportConfiguration.new(HornetQ::INVM_ACCEPTOR_CLASS_NAME)
42
+ else
43
+ acceptor = Java::org.hornetq.api.core.TransportConfiguration.new(HornetQ::NETTY_ACCEPTOR_CLASS_NAME, {'host' => uri.host, 'port' => uri.port })
44
+ connector = Java::org.hornetq.api.core.TransportConfiguration.new(HornetQ::NETTY_CONNECTOR_CLASS_NAME, {'host' => uri.host, 'port' => uri.port })
45
+ connector_conf_map = java.util.HashMap.new
46
+ connector_conf_map.put('netty-connector', connector)
47
+ config.connector_configurations = connector_conf_map
48
+ end
49
+ acceptor_conf_set = java.util.HashSet.new
50
+ acceptor_conf_set.add(acceptor)
51
+ config.acceptor_configurations = acceptor_conf_set
52
+
53
+
54
+ if parms[:backup]
55
+ puts "backup"
56
+ config.backup = true
57
+ config.shared_store = false
58
+ elsif uri.backup_host
59
+ puts "live"
60
+ #backup_params.put('reconnectAttempts', -1)
61
+ backup_connector = Java::org.hornetq.api.core.TransportConfiguration.new(HornetQ::NETTY_CONNECTOR_CLASS_NAME, {'host' => uri.backup_host, 'port' => uri.backup_port })
62
+ connector_conf_map.put('backup-connector', backup_connector)
63
+ config.backup_connector_name = 'backup-connector'
64
+ elsif uri.host == 'invm'
65
+ puts 'invm'
66
+ else
67
+ puts 'standalone'
68
+ end
69
+
70
+ return Java::org.hornetq.core.server.HornetQServers.newHornetQServer(config)
71
+ end
72
+
73
+ # Start a new server instance and stop it once the supplied block completes
74
+ def self.start(params={}, &block)
75
+ server = nil
76
+ begin
77
+ server = self.create_server(params)
78
+ server.start
79
+ block.call(server)
80
+ ensure
81
+ server.stop if server
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,59 @@
1
+ module HornetQ
2
+ class URI
3
+ attr_reader :scheme, :host, :port, :backup_host, :backup_port, :path, :params
4
+
5
+ # hornetq://localhost
6
+ # hornetq://localhost,192.168.0.22
7
+ # hornetq://localhost:5445,backupserver:5445/?protocol=netty
8
+ # hornetq://localhost/?protocol=invm
9
+ # hornetq://discoveryserver:5445/?protocol=discovery
10
+
11
+ def initialize(uri)
12
+ raise Exception,"Invalid protocol format: #{uri}" unless uri =~ /(.*):\/\/(.*)/
13
+ @scheme, @host = $1, $2
14
+ raise Exception,"Bad URI(only scheme hornetq:// is supported): #{uri}" unless @scheme == 'hornetq'
15
+ raise 'Mandatory hostname missing in uri' if @host.empty?
16
+ if @host =~ /(.*?)(\/.*)/
17
+ @host, @path = $1, $2
18
+ else
19
+ @path = '/'
20
+ end
21
+ if @host =~ /(.*),(.*)/
22
+ @host, @backup_host = $1, $2
23
+ end
24
+ if @host =~ /(.*):(.*)/
25
+ @host, @port = $1, HornetQ.netty_port($2)
26
+ else
27
+ @port = HornetQ::DEFAULT_NETTY_PORT
28
+ end
29
+ if @backup_host
30
+ if @backup_host =~ /(.*):(.*)/
31
+ @backup_host, @backup_port = $1, HornetQ.netty_port($2)
32
+ else
33
+ @backup_port = HornetQ::DEFAULT_NETTY_PORT
34
+ end
35
+ end
36
+ query = nil
37
+ if @path =~ /(.*)\?(.*)/
38
+ @path, query = $1, $2
39
+ end
40
+
41
+ # Extract settings passed in query
42
+ @params = {}
43
+ if query
44
+ query.split('&').each do |i|
45
+ key, value = i.split('=')
46
+ value = true if value == 'true'
47
+ value = false if value == 'false'
48
+ value = value.to_i if value =~ /^\d+$/
49
+ value = value.to_f if value =~ /^\d+\.\d*$/
50
+ @params[key.to_sym] = value
51
+ end
52
+ end
53
+ end
54
+
55
+ def [](key)
56
+ @params[key]
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,184 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'hornetq'
5
+ require 'fileutils'
6
+
7
+ class MyThread < ::Thread
8
+ def initialize(name, &block)
9
+ super() do
10
+ begin
11
+ yield
12
+ rescue => e
13
+ puts("Thread #{name} died due to exception #{e.message}\n#{e.backtrace.join("\n")}")
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ class ServerFactoryTest < Test::Unit::TestCase
20
+ context 'standalone server' do
21
+ setup do
22
+ @server = nil
23
+ @tmp_data_dir = "/tmp/data_dir/#{$$}"
24
+ @uri = "hornetq://localhost:15445"
25
+ @server_thread = MyThread.new('standalone server') do
26
+ @server = HornetQ::Server::Factory.create_server(:uri => @uri, :data_directory => @tmp_data_dir)
27
+ @server.start
28
+ end
29
+ # Give the server time to startup
30
+ sleep 5
31
+ end
32
+
33
+ teardown do
34
+ @server.stop
35
+ @server_thread.join
36
+ FileUtils.rm_rf(@tmp_data_dir)
37
+ end
38
+
39
+ should 'pass simple messages' do
40
+ count = 10
41
+ queue_name = 'test_queue'
42
+ config = {
43
+ :connector => { :uri => @uri },
44
+ :session => { :username =>'guest', :password => 'guest'}
45
+ }
46
+
47
+ # Create a HornetQ session
48
+ HornetQ::Client::Factory.create_session(config) do |session|
49
+ session.create_queue(queue_name, queue_name, true)
50
+ producer = session.create_producer(queue_name)
51
+ (1..count).each do |i|
52
+ message = session.create_message(HornetQ::Client::Message::TEXT_TYPE,false)
53
+ message.durable = true
54
+ # Set the message body text
55
+ message << "Message ##{i}"
56
+ # Send message to the queue
57
+ producer.send(message)
58
+ end
59
+ end
60
+
61
+ HornetQ::Client::Factory.create_session(config) do |session|
62
+ consumer = session.create_consumer(queue_name)
63
+ session.start
64
+
65
+ i = 0
66
+ while message = consumer.receive(1000)
67
+ i += 1
68
+ message.acknowledge
69
+ assert_equal "Message ##{i}", message.body
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'live and backup server' do
76
+ setup do
77
+ @count = 20
78
+
79
+ @server = nil
80
+ @tmp_data_dir = "/tmp/data_dir/#{$$}"
81
+ @uri = "hornetq://localhost:15445,localhost:15446"
82
+
83
+ @backup_server = nil
84
+ @backup_tmp_data_dir = "/tmp/backup_data_dir/#{$$}"
85
+ @backup_uri = "hornetq://localhost:15446"
86
+
87
+ @backup_server_thread = MyThread.new('backup server') do
88
+ begin
89
+ @backup_server = HornetQ::Server::Factory.create_server(:uri => @backup_uri, :data_directory => @backup_tmp_data_dir, :backup => true)
90
+ @backup_server.start
91
+ rescue Exception => e
92
+ puts "Error in backup server thread: #{e.message}\n#{e.backtrace.join("\n")}"
93
+ end
94
+ end
95
+ # Give the backup server time to startup
96
+ sleep 10
97
+
98
+ @server_thread = MyThread.new('live server') do
99
+ begin
100
+ @server = HornetQ::Server::Factory.create_server(:uri => @uri, :data_directory => @tmp_data_dir)
101
+ @server.start
102
+ rescue Exception => e
103
+ puts "Error in live server thread: #{e.message}\n#{e.backtrace.join("\n")}"
104
+ end
105
+ end
106
+
107
+ # Give the live server time to startup
108
+ sleep 10
109
+
110
+ @queue_name = 'test_queue'
111
+ @config = {
112
+ :connector => {
113
+ :uri => @uri,
114
+ :failover_on_initial_connection => true,
115
+ :failover_on_server_shutdown => true,
116
+ },
117
+ :session => { :username =>'guest', :password => 'guest'}
118
+ }
119
+
120
+ @killer_thread = MyThread.new('killer') do
121
+ sleep 5
122
+ @server.stop
123
+ end
124
+
125
+ @producer_thread = MyThread.new('producer') do
126
+ # Create a HornetQ session
127
+ HornetQ::Client::Factory.create_session(@config) do |session|
128
+ session.create_queue(@queue_name, @queue_name, true)
129
+ producer = session.create_producer(@queue_name)
130
+ (1..@count).each do |i|
131
+ message = session.create_message(HornetQ::Client::Message::TEXT_TYPE,false)
132
+ message.durable = true
133
+ # Set the message body text
134
+ message << "Message ##{i}"
135
+ # Send message to the queue
136
+ begin
137
+ puts "Producing message: #{message.body}"
138
+ producer.send(message)
139
+ sleep 1
140
+ rescue Java::org.hornetq.api.core.HornetQException => e
141
+ puts "Received producer exception: #{e.message} with code=#{e.cause.code}"
142
+ if e.cause.getCode == Java::org.hornetq.api.core.HornetQException::UNBLOCKED
143
+ puts "Retrying the send"
144
+ retry
145
+ end
146
+ rescue Exception => e
147
+ puts "Received producer exception: #{e.message}"
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ teardown do
155
+ @server.stop
156
+ @backup_server.stop
157
+ [ @server_thread, @backup_server_thread, @killer_thread, @producer_thread ].each do |thread|
158
+ thread.join
159
+ end
160
+ FileUtils.rm_rf([@tmp_data_dir, @backup_tmp_data_dir])
161
+ end
162
+
163
+ should 'failover to backup server w/o message loss' do
164
+ # Let the producer create the queue
165
+ sleep 2
166
+ HornetQ::Client::Factory.create_session(@config) do |session|
167
+ consumer = session.create_consumer(@queue_name)
168
+ session.start
169
+
170
+ i = 0
171
+ while message = consumer.receive(1000)
172
+ i += 1
173
+ message.acknowledge
174
+ assert_equal "Message ##{i}", message.body
175
+ puts "Consuming message #{message.body}"
176
+ end
177
+ assert_equal @count, i
178
+ end
179
+
180
+ killer_thread.join
181
+ producer_thread.join
182
+ end
183
+ end
184
+ end