jruby-jms 1.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -0
  3. data/Gemfile.lock +36 -0
  4. data/HISTORY.md +52 -0
  5. data/LICENSE.txt +201 -0
  6. data/README.md +205 -0
  7. data/Rakefile +30 -0
  8. data/examples/advanced/session_pool.rb +37 -0
  9. data/examples/client-server/replier.rb +29 -0
  10. data/examples/client-server/requestor.rb +40 -0
  11. data/examples/file-to-q/files_to_q.rb +51 -0
  12. data/examples/file-to-q/q_to_files.rb +44 -0
  13. data/examples/invm/invm.rb +44 -0
  14. data/examples/invm/log4j.properties +58 -0
  15. data/examples/jms.yml +149 -0
  16. data/examples/performance/consumer.rb +25 -0
  17. data/examples/performance/producer.rb +31 -0
  18. data/examples/producer-consumer/browser.rb +24 -0
  19. data/examples/producer-consumer/consumer.rb +24 -0
  20. data/examples/producer-consumer/consumer_async.rb +41 -0
  21. data/examples/producer-consumer/producer.rb +25 -0
  22. data/examples/publish-subscribe/publish.rb +24 -0
  23. data/examples/publish-subscribe/subscribe.rb +31 -0
  24. data/lib/jms.rb +20 -0
  25. data/lib/jms/bytes_message.rb +52 -0
  26. data/lib/jms/connection.rb +529 -0
  27. data/lib/jms/imports.rb +21 -0
  28. data/lib/jms/logging.rb +50 -0
  29. data/lib/jms/map_message.rb +91 -0
  30. data/lib/jms/message.rb +285 -0
  31. data/lib/jms/message_consumer.rb +117 -0
  32. data/lib/jms/message_listener_impl.rb +79 -0
  33. data/lib/jms/message_producer.rb +59 -0
  34. data/lib/jms/mq_workaround.rb +70 -0
  35. data/lib/jms/object_message.rb +26 -0
  36. data/lib/jms/oracle_a_q_connection_factory.rb +48 -0
  37. data/lib/jms/queue_browser.rb +28 -0
  38. data/lib/jms/session.rb +473 -0
  39. data/lib/jms/session_pool.rb +168 -0
  40. data/lib/jms/text_message.rb +31 -0
  41. data/lib/jms/version.rb +3 -0
  42. data/nbproject/private/private.properties +3 -0
  43. data/nbproject/private/rake-d.txt +5 -0
  44. data/parallel_minion.gemspec +21 -0
  45. data/test/connection_test.rb +160 -0
  46. data/test/jms.yml +111 -0
  47. data/test/log4j.properties +32 -0
  48. data/test/message_test.rb +130 -0
  49. data/test/session_pool_test.rb +86 -0
  50. data/test/session_test.rb +140 -0
  51. metadata +113 -0
@@ -0,0 +1,25 @@
1
+ #
2
+ # Sample Consumer:
3
+ # Retrieve all messages from a 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 'yaml'
11
+ require 'jms'
12
+
13
+ jms_provider = ARGV[0] || 'activemq'
14
+
15
+ # Load Connection parameters from configuration file
16
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
17
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
18
+
19
+ JMS::Connection.session(config) do |session|
20
+ stats = session.consume(:queue_name => 'ExampleQueue', :statistics => true) do |message|
21
+ # Do nothing in this example with each message
22
+ end
23
+
24
+ JMS::logger.info "Consumed #{stats[:messages]} messages. Average #{stats[:ms_per_msg]}ms per message"
25
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # Sample Producer:
3
+ # Write multiple messages to 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 'yaml'
11
+ require 'jms'
12
+ require 'benchmark'
13
+
14
+ jms_provider = ARGV[0] || 'activemq'
15
+ count = (ARGV[1] || 10).to_i
16
+
17
+ # Load Connection parameters from configuration file
18
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
19
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
20
+
21
+ JMS::Connection.session(config) do |session|
22
+ duration = Benchmark.realtime do
23
+ session.producer(:queue_name => 'ExampleQueue') do |producer|
24
+ count.times do |i|
25
+ producer.send(session.message("Hello Producer #{i}"))
26
+ end
27
+ end
28
+ end
29
+
30
+ JMS::logger.info "Produced #{count} messages. Average #{duration*1000/count}ms per message"
31
+ end
@@ -0,0 +1,24 @@
1
+ #
2
+ # Sample Browsing Consumer:
3
+ # Browse all messages on a queue without removing them
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 'jms'
11
+ require 'yaml'
12
+
13
+ jms_provider = ARGV[0] || 'activemq'
14
+
15
+ # Load Connection parameters from configuration file
16
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
17
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
18
+
19
+ # Consume all available messages on the queue
20
+ JMS::Connection.session(config) do |session|
21
+ session.browse(:queue_name => 'ExampleQueue', :timeout=>1000) do |message|
22
+ JMS::logger.info message.inspect
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ #
2
+ # Sample Consumer:
3
+ # Retrieve all messages from a 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 'jms'
11
+ require 'yaml'
12
+
13
+ jms_provider = ARGV[0] || 'activemq'
14
+
15
+ # Load Connection parameters from configuration file
16
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
17
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
18
+
19
+ # Consume all available messages on the queue
20
+ JMS::Connection.session(config) do |session|
21
+ session.consume(:queue_name => 'ExampleQueue', :timeout=>1000) do |message|
22
+ JMS::logger.info message.inspect
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ #
2
+ # Sample Asynchronous Consumer:
3
+ # Retrieve all messages from the queue in a separate thread
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 'jms'
11
+ require 'yaml'
12
+
13
+ jms_provider = ARGV[0] || 'activemq'
14
+
15
+ # Load Connection parameters from configuration file
16
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
17
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
18
+
19
+ continue = true
20
+
21
+ trap("INT") {
22
+ JMS::logger.info "CTRL + C"
23
+ continue = false
24
+ }
25
+
26
+ # Consume all available messages on the queue
27
+ JMS::Connection.start(config) do |connection|
28
+
29
+ # Define Asynchronous code block to be called every time a message is received
30
+ connection.on_message(:queue_name => 'ExampleQueue') do |message|
31
+ JMS::logger.info message.inspect
32
+ end
33
+
34
+ # Since the on_message handler above is in a separate thread the thread needs
35
+ # to do some other work. For this example it will just sleep for 10 seconds
36
+ while(continue)
37
+ sleep 10
38
+ end
39
+
40
+ JMS::logger.info "closing ..."
41
+ end
@@ -0,0 +1,25 @@
1
+ #
2
+ # Sample Producer:
3
+ # Write messages to 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 'yaml'
11
+ require 'jms'
12
+
13
+ jms_provider = ARGV[0] || 'activemq'
14
+
15
+ # Load Connection parameters from configuration file
16
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
17
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
18
+
19
+ JMS::Connection.session(config) do |session|
20
+ session.producer(:queue_name => 'ExampleQueue') do |producer|
21
+ producer.delivery_mode_sym = :non_persistent
22
+ producer.send(session.message("Hello World: #{Time.now}"))
23
+ JMS::logger.info "Successfully sent one message to queue ExampleQueue"
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ #
2
+ # Sample Publisher:
3
+ # Write messages to a topic
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 'yaml'
11
+ require 'jms'
12
+
13
+ jms_provider = ARGV[0] || 'activemq'
14
+
15
+ # Load Connection parameters from configuration file
16
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
17
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
18
+
19
+ JMS::Connection.session(config) do |session|
20
+ session.producer(:topic_name => 'SampleTopic') do |producer|
21
+ producer.send(session.message("Hello World: #{Time.now}"))
22
+ JMS::logger.info "Successfully published one message to topic SampleTopic"
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # Sample Topic Subscriber:
3
+ # Retrieve all messages from a topic using a non-durable subscription
4
+ #
5
+ # Try starting multiple copies of this Consumer. All active instances should
6
+ # receive the same messages
7
+ #
8
+ # Since the topic subscription is non-durable, it will only receive new messages.
9
+ # Any messages sent prior to the instance starting will not be received.
10
+ # Also, any messages sent after the instance has stopped will not be received
11
+ # when the instance is re-started, only new messages sent after it started will
12
+ # be received.
13
+
14
+ # Allow examples to be run in-place without requiring a gem install
15
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
16
+
17
+ require 'rubygems'
18
+ require 'jms'
19
+ require 'yaml'
20
+
21
+ jms_provider = ARGV[0] || 'activemq'
22
+
23
+ # Load Connection parameters from configuration file
24
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'jms.yml'))[jms_provider]
25
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless config
26
+
27
+ JMS::Connection.session(config) do |session|
28
+ session.consume(:topic_name => 'SampleTopic', :timeout=>30000) do |message|
29
+ JMS::logger.info message.inspect
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ ################################################################################
2
+ # Copyright 2008, 2009, 2010, 2011 J. Reid Morrison
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ################################################################################
16
+
17
+ require 'java'
18
+ require 'jms/version'
19
+ require 'jms/logging'
20
+ require 'jms/connection'
@@ -0,0 +1,52 @@
1
+ ################################################################################
2
+ # Copyright 2008, 2009, 2010, 2011 J. Reid Morrison
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ################################################################################
16
+
17
+ #Interface javax.jms.BytesMessage
18
+ module JMS::BytesMessage
19
+ def data
20
+ # Puts the message body in read-only mode and repositions the stream of
21
+ # bytes to the beginning
22
+ self.reset
23
+
24
+ available = self.body_length
25
+
26
+ return nil if available == 0
27
+
28
+ result = ""
29
+ bytes_size = 1024
30
+ bytes = Java::byte[bytes_size].new
31
+
32
+ while (n = available < bytes_size ? available : bytes_size) > 0
33
+ self.read_bytes(bytes, n)
34
+ if n == bytes_size
35
+ result << String.from_java_bytes(bytes)
36
+ else
37
+ result << String.from_java_bytes(bytes)[0..n-1]
38
+ end
39
+ available -= n
40
+ end
41
+ result
42
+ end
43
+
44
+ def data=(val)
45
+ self.write_bytes(val.respond_to?(:to_java_bytes) ? val.to_java_bytes : val)
46
+ end
47
+
48
+ def to_s
49
+ data
50
+ end
51
+
52
+ end
@@ -0,0 +1,529 @@
1
+ ################################################################################
2
+ # Copyright 2008, 2009, 2010, 2011 J. Reid Morrison
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ################################################################################
16
+
17
+ # Module: Java Messaging System (JMS) Interface
18
+ module JMS
19
+ # Every JMS session must have at least one Connection instance
20
+ # A Connection instance represents a connection between this client application
21
+ # and the JMS Provider (server/queue manager/broker).
22
+ # A connection is distinct from a Session, in that multiple Sessions can share a
23
+ # single connection. Also, unit of work control (commit/rollback) is performed
24
+ # at the Session level.
25
+ #
26
+ # Since many JRuby applications will only have one connection and one session
27
+ # several convenience methods have been added to support creating both the
28
+ # Session and Connection objects automatically.
29
+ #
30
+ # For Example, to read all messages from a queue and then terminate:
31
+ # require 'rubygems'
32
+ # require 'jms'
33
+ #
34
+ # JMS::Connection.create_session(
35
+ # :factory => 'org.apache.activemq.ActiveMQConnectionFactory',
36
+ # :broker_url => 'tcp://localhost:61616',
37
+ # :require_jars => [
38
+ # '~/Applications/apache-activemq-5.5.0/activemq-all-5.5.0.jar',
39
+ # '~/Applications/apache-activemq-5.5.0/lib/optional/slf4j-log4j12-1.5.11.jar',
40
+ # '~/Applications/apache-activemq-5.5.0/lib/optional/log4j-1.2.14.jar',
41
+ # ]
42
+ # ) do |session|
43
+ # session.consumer(:queue_name=>'TEST') do |consumer|
44
+ # if message = consumer.receive_no_wait
45
+ # puts "Data Received: #{message.data}"
46
+ # else
47
+ # puts 'No message available'
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # The above code creates a Connection and then a Session. Once the block completes
53
+ # the session is closed and the Connection disconnected.
54
+ #
55
+ # See: http://download.oracle.com/javaee/6/api/javax/jms/Connection.html
56
+ #
57
+ class Connection
58
+ # Create a connection to the JMS provider, start the connection,
59
+ # call the supplied code block, then close the connection upon completion
60
+ #
61
+ # Returns the result of the supplied block
62
+ def self.start(params = {}, &proc)
63
+ raise "Missing mandatory Block when calling JMS::Connection.start" unless proc
64
+ connection = Connection.new(params)
65
+ connection.start
66
+ begin
67
+ proc.call(connection)
68
+ ensure
69
+ connection.close
70
+ end
71
+ end
72
+
73
+ # Connect to a JMS Broker, create and start the session,
74
+ # then call the code block passing in the session.
75
+ # Both the Session and Connection are closed on termination of the block
76
+ #
77
+ # Shortcut convenience method to both connect to the broker and create a session
78
+ # Useful when only a single session is required in the current thread
79
+ #
80
+ # Note: It is important that each thread have its own session to support transactions
81
+ # This method will also start the session immediately so that any
82
+ # consumers using this session will start immediately
83
+ def self.session(params = {}, &proc)
84
+ self.start(params) do |connection|
85
+ connection.session(params, &proc)
86
+ end
87
+ end
88
+
89
+ # Load the required jar files for this JMS Provider and
90
+ # load JRuby extensions for those classes
91
+ #
92
+ # Rather than copying the JMS jar files into the JRuby lib, load them
93
+ # on demand. JRuby JMS extensions are only loaded once the jar files have been
94
+ # loaded.
95
+ #
96
+ # Can be called multiple times if required, although it would not be performant
97
+ # to do so regularly.
98
+ #
99
+ # Parameter: jar_list is an Array of the path and filenames to jar files
100
+ # to load for this JMS Provider
101
+ #
102
+ # Returns nil
103
+ #
104
+ # TODO make this a class method
105
+ def fetch_dependencies(jar_list)
106
+ jar_list.each do |jar|
107
+ JMS::logger.debug "Loading Jar File:#{jar}"
108
+ begin
109
+ require jar
110
+ rescue Exception => exc
111
+ JMS::logger.error "Failed to Load Jar File:#{jar}. #{exc.to_s}"
112
+ end
113
+ end if jar_list
114
+
115
+ require 'jms/mq_workaround'
116
+ require 'jms/imports'
117
+ require 'jms/message_listener_impl'
118
+ require 'jms/message'
119
+ require 'jms/text_message'
120
+ require 'jms/map_message'
121
+ require 'jms/bytes_message'
122
+ require 'jms/object_message'
123
+ require 'jms/session'
124
+ require 'jms/message_consumer'
125
+ require 'jms/message_producer'
126
+ require 'jms/queue_browser'
127
+ end
128
+
129
+ # Create a connection to the JMS provider
130
+ #
131
+ # Note: Connection::start must be called before any consumers will be
132
+ # able to receive messages
133
+ #
134
+ # In JMS we need to start by obtaining the JMS Factory class that is supplied
135
+ # by the JMS Vendor.
136
+ #
137
+ # There are 3 ways to establish a connection to a JMS Provider
138
+ # 1. Supply the name of the JMS Providers Factory Class
139
+ # 2. Supply an instance of the JMS Provider class itself
140
+ # 3. Use a JNDI lookup to return the JMS Provider Factory class
141
+ # Parameters:
142
+ # :factory => String: Name of JMS Provider Factory class
143
+ # => Class: JMS Provider Factory class itself
144
+ #
145
+ # :jndi_name => String: Name of JNDI entry at which the Factory can be found
146
+ # :jndi_context => Mandatory if jndi lookup is being used, contains details
147
+ # on how to connect to JNDI server etc.
148
+ #
149
+ # :require_jars => An optional array of Jar file names to load for the specified
150
+ # JMS provider. By using this option it is not necessary
151
+ # to put all the JMS Provider specific jar files into the
152
+ # environment variable CLASSPATH prior to starting JRuby
153
+ #
154
+ # :username => Username to connect to JMS provider with
155
+ # :password => Password to use when to connecting to the JMS provider
156
+ # Note: :password is ignored if :username is not supplied
157
+ #
158
+ # :factory and :jndi_name are mutually exclusive, both cannot be supplied at the
159
+ # same time. :factory takes precedence over :jndi_name
160
+ #
161
+ # JMS Provider specific properties can be set if the JMS Factory itself
162
+ # has setters for those properties.
163
+ #
164
+ # For some known examples, see: [Example jms.yml](https://github.com/reidmorrison/jruby-jms/blob/master/examples/jms.yml)
165
+ #
166
+ def initialize(params = {})
167
+ # Used by ::on_message
168
+ @sessions = []
169
+ @consumers = []
170
+
171
+ options = params.dup
172
+
173
+ # Load Jar files on demand so that they do not need to be in the CLASSPATH
174
+ # of JRuby lib directory
175
+ fetch_dependencies(options.delete(:require_jars))
176
+
177
+ connection_factory = nil
178
+ factory = options.delete(:factory)
179
+ if factory
180
+ # If factory check if oracle is needed.
181
+ if (factory.include? 'AQjmsFactory')
182
+ require 'jms/oracle_a_q_connection_factory'
183
+ end
184
+
185
+ # If factory is a string, then it is the name of a class, not the class itself
186
+ factory = eval(factory) if factory.respond_to? :to_str
187
+ connection_factory = factory.new
188
+ elsif jndi_name = options[:jndi_name]
189
+ raise "Missing mandatory parameter :jndi_context missing in call to Connection::connect" unless jndi_context = options[:jndi_context]
190
+ if jndi_context['java.naming.factory.initial'].include? 'AQjmsInitialContextFactory'
191
+ require 'jms/oracle_a_q_connection_factory'
192
+ end
193
+
194
+ jndi = javax.naming.InitialContext.new(java.util.Hashtable.new(jndi_context))
195
+ begin
196
+ connection_factory = jndi.lookup jndi_name
197
+ ensure
198
+ jndi.close
199
+ end
200
+ else
201
+ raise "Missing mandatory parameter :factory or :jndi_name missing in call to Connection::connect"
202
+ end
203
+ options.delete(:jndi_name)
204
+ options.delete(:jndi_context)
205
+
206
+ JMS::logger.debug "Using Factory: #{connection_factory.java_class}" if connection_factory.respond_to? :java_class
207
+ options.each_pair do |key, val|
208
+ next if [:username, :password].include?(key)
209
+
210
+ method = key.to_s+'='
211
+ if connection_factory.respond_to? method
212
+ connection_factory.send method, val
213
+ JMS::logger.debug " #{key} = #{connection_factory.send key.to_sym}" if connection_factory.respond_to? key.to_sym
214
+ else
215
+ JMS::logger.warn "#{connection_factory.java_class} does not understand option: :#{key}=#{val}, ignoring :#{key}" if connection_factory.respond_to? :java_class
216
+ end
217
+ end
218
+
219
+ # Check for username and password
220
+ if options[:username]
221
+ @jms_connection = connection_factory.create_connection(options[:username], options[:password])
222
+ else
223
+ @jms_connection = connection_factory.create_connection
224
+ end
225
+ end
226
+
227
+ # Start (or restart) delivery of incoming messages over this connection.
228
+ # By default no messages are delivered until this method is called explicitly
229
+ # Delivery of messages to any asynchronous Destination::each() call will only
230
+ # start after Connection::start is called, or Connection.start is used
231
+ def start
232
+ @jms_connection.start
233
+ end
234
+
235
+ # Temporarily stop delivery of incoming messages on this connection
236
+ # Useful during a hot code update or other changes that need to be completed
237
+ # without any new messages being processed
238
+ # Call start() to resume receiving messages
239
+ def stop
240
+ @jms_connection.stop
241
+ end
242
+
243
+ # Create a session over this connection.
244
+ # It is recommended to create separate sessions for each thread
245
+ # If a block of code is passed in, it will be called and then the session is automatically
246
+ # closed on completion of the code block
247
+ #
248
+ # Parameters:
249
+ # :transacted => true or false
250
+ # Determines whether transactions are supported within this session.
251
+ # I.e. Whether commit or rollback can be called
252
+ # Default: false
253
+ # Note: :options below are ignored if this value is set to :true
254
+ #
255
+ # :options => any of the JMS::Session constants:
256
+ # Note: :options are ignored if :transacted => true
257
+ # JMS::Session::AUTO_ACKNOWLEDGE
258
+ # With this acknowledgment mode, the session automatically acknowledges
259
+ # a client's receipt of a message either when the session has successfully
260
+ # returned from a call to receive or when the message listener the session has
261
+ # called to process the message successfully returns.
262
+ # JMS::Session::CLIENT_ACKNOWLEDGE
263
+ # With this acknowledgment mode, the client acknowledges a consumed
264
+ # message by calling the message's acknowledge method.
265
+ # JMS::Session::DUPS_OK_ACKNOWLEDGE
266
+ # This acknowledgment mode instructs the session to lazily acknowledge
267
+ # the delivery of messages.
268
+ # JMS::Session::SESSION_TRANSACTED
269
+ # This value is returned from the method getAcknowledgeMode if the
270
+ # session is transacted.
271
+ # Default: JMS::Session::AUTO_ACKNOWLEDGE
272
+ #
273
+ def session(params={}, &proc)
274
+ raise "Missing mandatory Block when calling JMS::Connection#session" unless proc
275
+ session = self.create_session(params)
276
+ begin
277
+ proc.call(session)
278
+ ensure
279
+ session.close
280
+ end
281
+ end
282
+
283
+ # Create a session over this connection.
284
+ # It is recommended to create separate sessions for each thread
285
+ #
286
+ # Note: Remember to call close on the returned session when it is no longer
287
+ # needed. Rather use JMS::Connection#session with a block whenever
288
+ # possible
289
+ #
290
+ # Parameters:
291
+ # :transacted => true or false
292
+ # Determines whether transactions are supported within this session.
293
+ # I.e. Whether commit or rollback can be called
294
+ # Default: false
295
+ # Note: :options below are ignored if this value is set to :true
296
+ #
297
+ # :options => any of the JMS::Session constants:
298
+ # Note: :options are ignored if :transacted => true
299
+ # JMS::Session::AUTO_ACKNOWLEDGE
300
+ # With this acknowledgment mode, the session automatically acknowledges
301
+ # a client's receipt of a message either when the session has successfully
302
+ # returned from a call to receive or when the message listener the session has
303
+ # called to process the message successfully returns.
304
+ # JMS::Session::CLIENT_ACKNOWLEDGE
305
+ # With this acknowledgment mode, the client acknowledges a consumed
306
+ # message by calling the message's acknowledge method.
307
+ # JMS::Session::DUPS_OK_ACKNOWLEDGE
308
+ # This acknowledgment mode instructs the session to lazily acknowledge
309
+ # the delivery of messages.
310
+ # JMS::Session::SESSION_TRANSACTED
311
+ # This value is returned from the method getAcknowledgeMode if the
312
+ # session is transacted.
313
+ # Default: JMS::Session::AUTO_ACKNOWLEDGE
314
+ #
315
+ def create_session(params={})
316
+ transacted = params[:transacted] || false
317
+ options = params[:options] || JMS::Session::AUTO_ACKNOWLEDGE
318
+ @jms_connection.create_session(transacted, options)
319
+ end
320
+
321
+ # Close connection with the JMS Provider
322
+ # First close any consumers or sessions that are active as a result of JMS::Connection::on_message
323
+ def close
324
+ @consumers.each {|consumer| consumer.close } if @consumers
325
+ @consumers = []
326
+
327
+ @sessions.each {|session| session.close} if @sessions
328
+ @session=[]
329
+
330
+ @jms_connection.close if @jms_connection
331
+ end
332
+
333
+ # Gets the client identifier for this connection.
334
+ def client_id
335
+ @jms_connection.getClientID
336
+ end
337
+
338
+ # Sets the client identifier for this connection.
339
+ def client_id=(client_id)
340
+ @jms_connection.setClientID(client_id)
341
+ end
342
+
343
+ # Returns the ExceptionListener object for this connection
344
+ # Returned class implements interface JMS::ExceptionListener
345
+ def exception_listener
346
+ @jms_connection.getExceptionListener
347
+ end
348
+
349
+ # Sets an exception listener for this connection
350
+ # See ::on_exception to set a Ruby Listener
351
+ # Returns: nil
352
+ def exception_listener=(listener)
353
+ @jms_connection.setExceptionListener(listener)
354
+ end
355
+
356
+ # Whenever an exception occurs the supplied block is called
357
+ # This is important when Connection::on_message has been used, since
358
+ # failures to the connection would be lost otherwise
359
+ #
360
+ # For details on the supplied parameter when the block is called,
361
+ # see: http://download.oracle.com/javaee/6/api/javax/jms/JMSException.html
362
+ #
363
+ # Example:
364
+ # connection.on_exception do |jms_exception|
365
+ # puts "JMS Exception has occurred: #{jms_exception}"
366
+ # end
367
+ #
368
+ # Returns: nil
369
+ def on_exception(&block)
370
+ @jms_connection.setExceptionListener(block)
371
+ end
372
+
373
+ # Gets the metadata for this connection
374
+ # see: http://download.oracle.com/javaee/6/api/javax/jms/ConnectionMetaData.html
375
+ def meta_data
376
+ @jms_connection.getMetaData
377
+ end
378
+
379
+ # Return a string describing the JMS provider and version
380
+ def to_s
381
+ md = @jms_connection.getMetaData
382
+ "JMS::Connection provider: #{md.getJMSProviderName} v#{md.getProviderVersion}, JMS v#{md.getJMSVersion}"
383
+ end
384
+
385
+ # Receive messages in a separate thread when they arrive
386
+ #
387
+ # Allows messages to be received Asynchronously in a separate thread.
388
+ # This method will return to the caller before messages are processed.
389
+ # It is then the callers responsibility to keep the program active so that messages
390
+ # can then be processed.
391
+ #
392
+ # Session Parameters:
393
+ # :transacted => true or false
394
+ # Determines whether transactions are supported within this session.
395
+ # I.e. Whether commit or rollback can be called
396
+ # Default: false
397
+ # Note: :options below are ignored if this value is set to :true
398
+ #
399
+ # :options => any of the JMS::Session constants:
400
+ # Note: :options are ignored if :transacted => true
401
+ # JMS::Session::AUTO_ACKNOWLEDGE
402
+ # With this acknowledgment mode, the session automatically acknowledges
403
+ # a client's receipt of a message either when the session has successfully
404
+ # returned from a call to receive or when the message listener the session has
405
+ # called to process the message successfully returns.
406
+ # JMS::Session::CLIENT_ACKNOWLEDGE
407
+ # With this acknowledgment mode, the client acknowledges a consumed
408
+ # message by calling the message's acknowledge method.
409
+ # JMS::Session::DUPS_OK_ACKNOWLEDGE
410
+ # This acknowledgment mode instructs the session to lazily acknowledge
411
+ # the delivery of messages.
412
+ # JMS::Session::SESSION_TRANSACTED
413
+ # This value is returned from the method getAcknowledgeMode if the
414
+ # session is transacted.
415
+ # Default: JMS::Session::AUTO_ACKNOWLEDGE
416
+ #
417
+ # :session_count : Number of sessions to create, each with their own consumer which
418
+ # in turn will call the supplied code block.
419
+ # Note: The supplied block must be thread safe since it will be called
420
+ # by several threads at the same time.
421
+ # I.e. Don't change instance variables etc. without the
422
+ # necessary semaphores etc.
423
+ # Default: 1
424
+ #
425
+ # Consumer Parameters:
426
+ # :queue_name => String: Name of the Queue to return
427
+ # Symbol: :temporary => Create temporary queue
428
+ # Mandatory unless :topic_name is supplied
429
+ # Or,
430
+ # :topic_name => String: Name of the Topic to write to or subscribe to
431
+ # Symbol: :temporary => Create temporary topic
432
+ # Mandatory unless :queue_name is supplied
433
+ # Or,
434
+ # :destination=> Explicit javaxJms::Destination to use
435
+ #
436
+ # :selector => Filter which messages should be returned from the queue
437
+ # Default: All messages
438
+ #
439
+ # :no_local => Determine whether messages published by its own connection
440
+ # should be delivered to the supplied block
441
+ # Default: false
442
+ #
443
+ # :statistics Capture statistics on how many messages have been read
444
+ # true : This method will capture statistics on the number of messages received
445
+ # and the time it took to process them.
446
+ # The timer starts when each() is called and finishes when either the last message was received,
447
+ # or when Destination::statistics is called. In this case MessageConsumer::statistics
448
+ # can be called several times during processing without affecting the end time.
449
+ # Also, the start time and message count is not reset until MessageConsumer::each
450
+ # is called again with :statistics => true
451
+ #
452
+ # Usage: For transacted sessions the block supplied must return either true or false:
453
+ # true => The session is committed
454
+ # false => The session is rolled back
455
+ # Any Exception => The session is rolled back
456
+ #
457
+ # Note: Separately invoke Connection#on_exception so that connection failures can be handled
458
+ # since on_message will Not be called if the connection is lost
459
+ #
460
+ def on_message(params, &block)
461
+ raise "JMS::Connection must be connected prior to calling JMS::Connection::on_message" unless @sessions && @consumers
462
+
463
+ consumer_count = params[:session_count] || 1
464
+ consumer_count.times do
465
+ session = self.create_session(params)
466
+ consumer = session.consumer(params)
467
+ if session.transacted?
468
+ consumer.on_message(params) do |message|
469
+ begin
470
+ block.call(message) ? session.commit : session.rollback
471
+ rescue => exc
472
+ session.rollback
473
+ throw exc
474
+ end
475
+ end
476
+ else
477
+ consumer.on_message(params, &block)
478
+ end
479
+ @consumers << consumer
480
+ @sessions << session
481
+ end
482
+ end
483
+
484
+ # Return the statistics for every active Connection#on_message consumer
485
+ # in an Array
486
+ #
487
+ # For details on the contents of each element in the array, see: Consumer#on_message_statistics
488
+ def on_message_statistics
489
+ @consumers.collect { |consumer| consumer.on_message_statistics }
490
+ end
491
+
492
+ # Since a Session can only be used by one thread at a time, we could create
493
+ # a Session for every thread. That could result in excessive unused Sessions.
494
+ # An alternative is to create a pool of sessions that can be shared by
495
+ # multiple threads.
496
+ #
497
+ # Each thread can request a session and then return it once it is no longer
498
+ # needed by that thread. The only way to get a session is to pass a block so that
499
+ # the Session is automatically returned to the pool upon completion of the block.
500
+ #
501
+ # Parameters:
502
+ # see regular session parameters from: JMS::Connection#initialize
503
+ #
504
+ # Additional parameters for controlling the session pool itself
505
+ # :pool_name Name of the pool as it shows up in the logger.
506
+ # Default: 'JMS::SessionPool'
507
+ # :pool_size Maximum Pool Size. Default: 10
508
+ # The pool only grows as needed and will never exceed
509
+ # :pool_size
510
+ # :pool_warn_timeout Number of seconds to wait before logging a warning when a
511
+ # session in the pool is not available. Measured in seconds
512
+ # Default: 5.0
513
+ # :pool_logger Supply a logger that responds to #debug, #info, #warn and #debug?
514
+ # For example: Rails.logger
515
+ # Default: None
516
+ # Example:
517
+ # session_pool = connection.create_session_pool(config)
518
+ #
519
+ # session_pool.session do |session|
520
+ # producer.send(session.message("Hello World"))
521
+ # end
522
+ def create_session_pool(params={})
523
+ require 'jms/session_pool' unless defined? JMS::SessionPool
524
+ JMS::SessionPool.new(self, params)
525
+ end
526
+
527
+ end
528
+
529
+ end