jruby-jms 1.1.0-java

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 (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