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.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +36 -0
- data/HISTORY.md +52 -0
- data/LICENSE.txt +201 -0
- data/README.md +205 -0
- data/Rakefile +30 -0
- data/examples/advanced/session_pool.rb +37 -0
- data/examples/client-server/replier.rb +29 -0
- data/examples/client-server/requestor.rb +40 -0
- data/examples/file-to-q/files_to_q.rb +51 -0
- data/examples/file-to-q/q_to_files.rb +44 -0
- data/examples/invm/invm.rb +44 -0
- data/examples/invm/log4j.properties +58 -0
- data/examples/jms.yml +149 -0
- data/examples/performance/consumer.rb +25 -0
- data/examples/performance/producer.rb +31 -0
- data/examples/producer-consumer/browser.rb +24 -0
- data/examples/producer-consumer/consumer.rb +24 -0
- data/examples/producer-consumer/consumer_async.rb +41 -0
- data/examples/producer-consumer/producer.rb +25 -0
- data/examples/publish-subscribe/publish.rb +24 -0
- data/examples/publish-subscribe/subscribe.rb +31 -0
- data/lib/jms.rb +20 -0
- data/lib/jms/bytes_message.rb +52 -0
- data/lib/jms/connection.rb +529 -0
- data/lib/jms/imports.rb +21 -0
- data/lib/jms/logging.rb +50 -0
- data/lib/jms/map_message.rb +91 -0
- data/lib/jms/message.rb +285 -0
- data/lib/jms/message_consumer.rb +117 -0
- data/lib/jms/message_listener_impl.rb +79 -0
- data/lib/jms/message_producer.rb +59 -0
- data/lib/jms/mq_workaround.rb +70 -0
- data/lib/jms/object_message.rb +26 -0
- data/lib/jms/oracle_a_q_connection_factory.rb +48 -0
- data/lib/jms/queue_browser.rb +28 -0
- data/lib/jms/session.rb +473 -0
- data/lib/jms/session_pool.rb +168 -0
- data/lib/jms/text_message.rb +31 -0
- data/lib/jms/version.rb +3 -0
- data/nbproject/private/private.properties +3 -0
- data/nbproject/private/rake-d.txt +5 -0
- data/parallel_minion.gemspec +21 -0
- data/test/connection_test.rb +160 -0
- data/test/jms.yml +111 -0
- data/test/log4j.properties +32 -0
- data/test/message_test.rb +130 -0
- data/test/session_pool_test.rb +86 -0
- data/test/session_test.rb +140 -0
- 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
|
data/lib/jms.rb
ADDED
@@ -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
|