jruby-jms 0.9.0 → 0.10.0

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.
@@ -19,11 +19,99 @@
19
19
  # JMS Provider.
20
20
  #
21
21
  # Interface javax.jms.Session
22
+ #
23
+ # See: http://download.oracle.com/javaee/6/api/javax/jms/Session.html
24
+ #
25
+ # Other methods still directly accessible through this class:
26
+ #
27
+ # create_browser(queue, message_selector)
28
+ # Creates a QueueBrowser object to peek at the messages on the specified queue using a message selector.
29
+ #
30
+ # create_bytes_message()
31
+ # Creates a BytesMessage object
32
+ #
33
+ # create_consumer(destination)
34
+ # Creates a MessageConsumer for the specified destination
35
+ # See: Connection::consumer
36
+ #
37
+ # Example:
38
+ # destination = session.create_destination(:queue_name => "MyQueue")
39
+ # session.create_consumer(destination)
40
+ #
41
+ # create_consumer(destination, message_selector)
42
+ # Creates a MessageConsumer for the specified destination, using a message selector
43
+ #
44
+ # create_consumer(destination, message_selector, boolean NoLocal)
45
+ # Creates MessageConsumer for the specified destination, using a message selector
46
+ #
47
+ # create_durable_subscriber(Topic topic, java.lang.String name)
48
+ # Creates a durable subscriber to the specified topic
49
+ #
50
+ # create_durable_subscriber(Topic topic, java.lang.String name, java.lang.String messageSelector, boolean noLocal)
51
+ # Creates a durable subscriber to the specified topic, using a message selector and specifying whether messages published by its own connection should be delivered to it.
52
+ #
53
+ # create_map_Message()
54
+ # Creates a MapMessage object
55
+ #
56
+ # create_message()
57
+ # Creates a Message object
58
+ #
59
+ # create_object_message()
60
+ # Creates an ObjectMessage object
61
+ #
62
+ # create_object_message(java.io.Serializable object)
63
+ # Creates an initialized ObjectMessage object
64
+ #
65
+ # create_producer(destination)
66
+ # Creates a MessageProducer to send messages to the specified destination
67
+ #
68
+ # create_queue(queue_name)
69
+ # Creates a queue identity given a Queue name
70
+ #
71
+ # create_stream_message()
72
+ # Creates a StreamMessage object
73
+ #
74
+ # create_temporary_queue()
75
+ # Creates a TemporaryQueue object
76
+ #
77
+ # create_temporary_topic()
78
+ # Creates a TemporaryTopic object
79
+ #
80
+ # create_text_message()
81
+ # Creates a TextMessage object
82
+ #
83
+ # create_text_message(text)
84
+ # Creates an initialized TextMessage object
85
+ #
86
+ # create_topic(topic_name)
87
+ # Creates a topic identity given a Topic name
88
+ #
89
+ # acknowledge_mode()
90
+ # Returns the acknowledgement mode of the session
91
+ #
92
+ # message_listener()
93
+ # Returns the session's distinguished message listener (optional).
94
+ #
95
+ # transacted?
96
+ # Indicates whether the session is in transacted mode
97
+ #
98
+ # recover()
99
+ # Stops message delivery in this session, and restarts message delivery with the oldest unacknowledged message
100
+ #
101
+ # rollback()
102
+ # Rolls back any messages done in this transaction and releases any locks currently held
103
+ #
104
+ # message_listener=(MessageListener listener)
105
+ # Sets the session's distinguished message listener (optional)
106
+ #
107
+ # unsubscribe(name)
108
+ # Unsubscribes a durable subscription that has been created by a client
109
+ #
22
110
  module javax.jms::Session
23
111
  # Create a new message instance based on the type of the data being supplied
24
112
  # String (:to_str) => TextMessage
25
113
  # Hash (:each_pair) => MapMessage
26
- # In fact duck typing is used to determine the type. If the class responds
114
+ # Duck typing is used to determine the type. If the class responds
27
115
  # to :to_str then it is considered a String. Similarly if it responds to
28
116
  # :each_pair it is considered to be a Hash
29
117
  def message(data)
@@ -40,42 +128,153 @@ module javax.jms::Session
40
128
  jms_message
41
129
  end
42
130
 
43
- # Does the session support transactions?
44
- # I.e. Can/should commit and rollback be called
45
- def transacted?
46
- self.getAcknowledgeMode == javax.jms.Session::SESSION_TRANSACTED
131
+ # Create the destination based on the parameter supplied
132
+ #
133
+ # The idea behind this method is to allow the decision as to whether
134
+ # one is sending to a topic or destination to be transparent to the code.
135
+ # The supplied parameters can be externalized into say a YAML file
136
+ # so that today it writes to a queue, later it can be changed to write
137
+ # to a topic so that multiple parties can receive the same messages.
138
+ #
139
+ # Note: For Temporary Queues and Topics, remember to delete them when done
140
+ # or just use ::destination instead with a block and it will take care
141
+ # of deleting them for you
142
+ #
143
+ # To create a queue:
144
+ # session.create_destination(:queue_name => 'name of queue')
145
+ #
146
+ # To create a temporary queue:
147
+ # session.create_destination(:queue_name => :temporary)
148
+ #
149
+ # To create a topic:
150
+ # session.create_destination(:topic_name => 'name of queue')
151
+ #
152
+ # To create a temporary topic:
153
+ # session.create_destination(:topic_name => :temporary)
154
+ #
155
+ # Create the destination based on the parameter supplied
156
+ #
157
+ # Parameters:
158
+ # :queue_name => String: Name of the Queue to return
159
+ # Symbol: :temporary => Create temporary queue
160
+ # Mandatory unless :topic_name is supplied
161
+ # Or,
162
+ # :topic_name => String: Name of the Topic to write to or subscribe to
163
+ # Symbol: :temporary => Create temporary topic
164
+ # Mandatory unless :q_name is supplied
165
+ # Or,
166
+ # :destination=> Explicit javaxJms::Destination to use
167
+ #
168
+ # Returns the result of the supplied block
169
+ def create_destination(parms)
170
+ # Allow a Java JMS destination object to be passed in
171
+ return parms[:destination] if parms[:destination] && parms[:destination].java_kind_of?(javax.jms::Destination)
172
+
173
+ # :q_name is deprecated
174
+ queue_name = parms[:queue_name] || parms[:q_name]
175
+ topic_name = parms[:topic_name]
176
+ raise "Missing mandatory parameter :q_name or :topic_name to Session::producer, Session::consumer, or Session::browser" unless queue_name || topic_name
177
+
178
+ if queue_name
179
+ queue_name == :temporary ? create_temporary_queue : create_queue(queue_name)
180
+ else
181
+ topic_name == :temporary ? create_temporary_topic : create_topic(topic_name)
182
+ end
183
+ end
184
+
185
+ # Create a queue or topic to send or receive messages from
186
+ #
187
+ # A block must be supplied so that if it is a temporary topic or queue
188
+ # it will be deleted after the proc is complete
189
+ #
190
+ # To create a queue:
191
+ # session.destination(:queue_name => 'name of queue')
192
+ #
193
+ # To create a temporary queue:
194
+ # session.destination(:queue_name => :temporary)
195
+ #
196
+ # To create a topic:
197
+ # session.destination(:topic_name => 'name of queue')
198
+ #
199
+ # To create a temporary topic:
200
+ # session.destination(:topic_name => :temporary)
201
+ #
202
+ # Create the destination based on the parameter supplied
203
+ #
204
+ # Parameters:
205
+ # :queue_name => String: Name of the Queue to return
206
+ # Symbol: :temporary => Create temporary queue
207
+ # Mandatory unless :topic_name is supplied
208
+ # Or,
209
+ # :topic_name => String: Name of the Topic to write to or subscribe to
210
+ # Symbol: :temporary => Create temporary topic
211
+ # Mandatory unless :q_name is supplied
212
+ # Or,
213
+ # :destination=> Explicit javaxJms::Destination to use
214
+ #
215
+ # Returns the result of the supplied block
216
+ def destination(parms={}, &block)
217
+ raise "Missing mandatory Block when calling JMS::Session#destination" unless block
218
+ dest = nil
219
+ begin
220
+ dest = create_destination(parms)
221
+ block.call(dest)
222
+ ensure
223
+ # Delete Temporary Queue / Topic
224
+ dest.delete if dest && dest.respond_to?(:delete)
225
+ end
226
+ end
227
+
228
+ # Return the queue matching the queue name supplied
229
+ # Call the Proc if supplied
230
+ def queue(queue_name, &block)
231
+ q = create_queue(queue_name)
232
+ block.call(q) if block
233
+ q
47
234
  end
48
235
 
49
- # Create and open a queue to put or get from. Once the supplied Proc is complete
50
- # the queue is automatically closed. If no Proc is supplied then the queue must
51
- # be explicitly closed by the caller.
52
- def destination(parms={}, &proc)
53
- parms[:jms_session] = self
54
- q = JMS::Destination.new(parms)
55
- q.open(parms)
56
- if proc
236
+ # Return a temporary queue
237
+ # The temporary queue is deleted once the block completes
238
+ # If no block is supplied then it should be deleted by the caller
239
+ # when no longer needed
240
+ def temporary_queue(&block)
241
+ q = create_temporary_queue
242
+ if block
57
243
  begin
58
- proc.call(q)
244
+ block.call(q)
59
245
  ensure
60
- q.close
246
+ # Delete Temporary queue on completion of block
247
+ q.delete if q
61
248
  q = nil
62
249
  end
63
250
  end
64
251
  q
65
252
  end
66
253
 
67
- # Return the queue matching the queue name supplied
254
+ # Return the topic matching the topic name supplied
68
255
  # Call the Proc if supplied
69
- def queue(q_name, &proc)
70
- q = create_queue(q_name)
71
- if proc
256
+ def topic(topic_name, &proc)
257
+ t = create_topic(topic_name)
258
+ proc.call(t) if proc
259
+ t
260
+ end
261
+
262
+ # Return a temporary topic
263
+ # The temporary topic is deleted once the block completes
264
+ # If no block is supplied then it should be deleted by the caller
265
+ # when no longer needed
266
+ def temporary_topic(&block)
267
+ t = create_temporary_topic
268
+ if block
72
269
  begin
73
- proc.call(q)
270
+ block.call(t)
74
271
  ensure
75
- q = nil
272
+ # Delete Temporary topic on completion of block
273
+ t.delete if t
274
+ t = nil
76
275
  end
77
276
  end
78
- q
277
+ t
79
278
  end
80
279
 
81
280
  # Return a producer for the queue name supplied
@@ -240,32 +439,6 @@ module javax.jms::Session
240
439
  def browse(parms={}, &proc)
241
440
  self.browser(parms) {|b| b.each(parms, &proc)}
242
441
  end
243
-
244
- private
245
- # Create the destination based on the parameter supplied
246
- #
247
- # Parameters:
248
- # :q_name => String: Name of the Queue to return
249
- # Symbol: :temporary => Create temporary queue
250
- # Mandatory unless :topic_name is supplied
251
- # Or,
252
- # :topic_name => String: Name of the Topic to write to or subscribe to
253
- # Symbol: :temporary => Create temporary topic
254
- # Mandatory unless :q_name is supplied
255
- # Or,
256
- # :destination=> Explicit javaxJms::Destination to use
257
- def create_destination(parms)
258
- return parms[:destination] if parms[:destination] && parms[:destination].kind_of?(javaxJms::Destination)
259
- q_name = parms[:q_name]
260
- topic_name = parms[:topic_name]
261
- raise "Missing mandatory parameter :q_name or :topic_name to Session::producer, Session::consumer, or Session::browser" unless q_name || topic_name
262
-
263
- if q_name
264
- q_name == :temporary ? create_temporary_queue : create_queue(q_name)
265
- else
266
- topic_name == :temporary ? create_temporary_topic : create_topic(topic_name)
267
- end
268
- end
269
442
  end
270
443
 
271
444
  # Workaround for IBM MQ JMS implementation that implements an undocumented consume method
data/lib/jms/logger.rb ADDED
@@ -0,0 +1,4 @@
1
+ # Add trace method to Ruby Logger class
2
+ class Logger
3
+ alias :trace :debug
4
+ end
@@ -0,0 +1,29 @@
1
+ # Add HornetQ logging capabilities
2
+ module JMS
3
+
4
+ # Returns the logger being used by jruby-jms
5
+ def self.logger
6
+ @logger ||= (rails_logger || default_logger)
7
+ end
8
+
9
+ # Replace the logger for jruby-jms
10
+ def self.logger=(logger)
11
+ @logger = logger
12
+ end
13
+
14
+ private
15
+ def self.rails_logger
16
+ (defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
17
+ (defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
18
+ end
19
+
20
+ # By default we use the standard Ruby Logger
21
+ def self.default_logger
22
+ require 'logger'
23
+ require 'jms/logger'
24
+ l = Logger.new(STDOUT)
25
+ l.level = Logger::INFO
26
+ l
27
+ end
28
+
29
+ end
@@ -0,0 +1,79 @@
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 JMS
18
+
19
+ private
20
+ # For internal use only by JMS::Connection
21
+ class MessageListener
22
+ include javax.jms::MessageListener
23
+
24
+ # Parameters:
25
+ # :statistics Capture statistics on how many messages have been read
26
+ # true : This method will capture statistics on the number of messages received
27
+ # and the time it took to process them.
28
+ # The timer starts when the listener instance is created and finishes when either the last message was received,
29
+ # or when Destination::statistics is called. In this case MessageConsumer::statistics
30
+ # can be called several times during processing without affecting the end time.
31
+ # Also, the start time and message count is not reset until MessageConsumer::each
32
+ # is called again with :statistics => true
33
+ #
34
+ # The statistics gathered are returned when :statistics => true and :async => false
35
+ def initialize(parms={}, &proc)
36
+ @proc = proc
37
+
38
+ if parms[:statistics]
39
+ @message_count = 0
40
+ @start_time = Time.now
41
+ end
42
+ end
43
+
44
+ # Method called for every message received on the queue
45
+ # Per the JMS specification, this method will be called sequentially for each message on the queue.
46
+ # This method will not be called again until its prior invocation has completed.
47
+ # Must be onMessage() since on_message() does not work for interface methods that must be implemented
48
+ def onMessage(message)
49
+ begin
50
+ if @message_count
51
+ @message_count += 1
52
+ @last_time = Time.now
53
+ end
54
+ @proc.call message
55
+ rescue SyntaxError, NameError => boom
56
+ JMS::logger.error "Unhandled Exception processing JMS Message. Doesn't compile: " + boom
57
+ JMS::logger.error "Ignoring poison message:\n#{message.inspect}"
58
+ JMS::logger.error boom.backtrace.join("\n")
59
+ rescue StandardError => bang
60
+ JMS::logger.error "Unhandled Exception processing JMS Message. Doesn't compile: " + bang
61
+ JMS::logger.error "Ignoring poison message:\n#{message.inspect}"
62
+ JMS::logger.error boom.backtrace.join("\n")
63
+ rescue => exc
64
+ JMS::logger.error "Unhandled Exception processing JMS Message. Exception occurred:\n#{exc}"
65
+ JMS::logger.error "Ignoring poison message:\n#{message.inspect}"
66
+ JMS::logger.error exc.backtrace.join("\n")
67
+ end
68
+ end
69
+
70
+ # Return Statistics gathered for this listener
71
+ def statistics
72
+ raise "First call MessageConsumer::on_message with :statistics=>true before calling MessageConsumer::statistics()" unless @message_count
73
+ duration =(@last_time || Time.now) - @start_time
74
+ {:messages => @message_count,
75
+ :duration => duration,
76
+ :messages_per_second => (@message_count/duration).to_i}
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,155 @@
1
+ # Allow examples to be run in-place without requiring a gem install
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'shoulda'
7
+ require 'jms'
8
+ require 'yaml'
9
+
10
+ class JMSTest < Test::Unit::TestCase
11
+ context 'JMS Connection' do
12
+ # Load configuration from jms.yml
13
+ setup do
14
+ # To change the JMS provider, edit jms.yml and change :default
15
+
16
+ # Load Connection parameters from configuration file
17
+ cfg = YAML.load_file(File.join(File.dirname(__FILE__), 'jms.yml'))
18
+ jms_provider = cfg['default']
19
+ @config = cfg[jms_provider]
20
+ raise "JMS Provider option:#{jms_provider} not found in jms.yml file" unless @config
21
+ end
22
+
23
+ should 'Create Connection to the Broker/Server' do
24
+ connection = JMS::Connection.new(@config)
25
+ JMS::logger.info connection.to_s
26
+ assert_not_nil connection
27
+ connection.close
28
+ end
29
+
30
+ should 'Create and start Connection to the Broker/Server with block' do
31
+ JMS::Connection.start(@config) do |connection|
32
+ assert_not_nil connection
33
+ end
34
+ end
35
+
36
+ should 'Create and start Connection to the Broker/Server with block and start one session' do
37
+ JMS::Connection.session(@config) do |session|
38
+ assert_not_nil session
39
+ end
40
+ end
41
+
42
+ should 'Start and stop connection' do
43
+ connection = JMS::Connection.new(@config)
44
+ assert_not_nil connection
45
+ assert_nil connection.start
46
+
47
+ assert_nil connection.stop
48
+ assert_nil connection.close
49
+ end
50
+
51
+ should 'Create a session from the connection' do
52
+ connection = JMS::Connection.new(@config)
53
+
54
+ session_parms = {
55
+ :transacted => true,
56
+ :options => javax.jms.Session::AUTO_ACKNOWLEDGE
57
+ }
58
+
59
+ session = connection.create_session
60
+ assert_not_nil session
61
+ assert_equal session.transacted?, false
62
+ assert_nil session.close
63
+
64
+ assert_nil connection.stop
65
+ assert_nil connection.close
66
+ end
67
+
68
+ should 'Create a session with a block' do
69
+ connection = JMS::Connection.new(@config)
70
+
71
+ connection.session do |session|
72
+ assert_not_nil session
73
+ assert_equal session.transacted?, false
74
+ end
75
+
76
+ assert_nil connection.stop
77
+ assert_nil connection.close
78
+ end
79
+
80
+ should 'create a session without a block and throw exception' do
81
+ connection = JMS::Connection.new(@config)
82
+
83
+ assert_raise(RuntimeError) { connection.session }
84
+
85
+ assert_nil connection.stop
86
+ assert_nil connection.close
87
+ end
88
+
89
+ should 'Create a session from the connection with parms' do
90
+ connection = JMS::Connection.new(@config)
91
+
92
+ session_parms = {
93
+ :transacted => true,
94
+ :options => javax.jms.Session::AUTO_ACKNOWLEDGE
95
+ }
96
+
97
+ session = connection.create_session(session_parms)
98
+ assert_not_nil session
99
+ assert_equal session.transacted?, true
100
+ # When session is transacted, options are ignore, so ack mode must be transacted
101
+ assert_equal session.acknowledge_mode, javax.jms.Session::SESSION_TRANSACTED
102
+ assert_nil session.close
103
+
104
+ assert_nil connection.stop
105
+ assert_nil connection.close
106
+ end
107
+
108
+ should 'Create a session from the connection with block and parms' do
109
+ JMS::Connection.start(@config) do |connection|
110
+
111
+ session_parms = {
112
+ :transacted => true,
113
+ :options => javax.jms.Session::CLIENT_ACKNOWLEDGE
114
+ }
115
+
116
+ connection.session(session_parms) do |session|
117
+ assert_not_nil session
118
+ assert_equal session.transacted?, true
119
+ # When session is transacted, options are ignore, so ack mode must be transacted
120
+ assert_equal session.acknowledge_mode, javax.jms.Session::SESSION_TRANSACTED
121
+ end
122
+ end
123
+ end
124
+
125
+ should 'Create a session from the connection with block and parms opposite test' do
126
+ JMS::Connection.start(@config) do |connection|
127
+
128
+ session_parms = {
129
+ :transacted => false,
130
+ :options => javax.jms.Session::AUTO_ACKNOWLEDGE
131
+ }
132
+
133
+ connection.session(session_parms) do |session|
134
+ assert_not_nil session
135
+ assert_equal session.transacted?, false
136
+ assert_equal session.acknowledge_mode, javax.jms.Session::AUTO_ACKNOWLEDGE
137
+ end
138
+ end
139
+ end
140
+
141
+ context 'JMS Connection additional capabilities' do
142
+
143
+ should 'start an on_message handler' do
144
+ JMS::Connection.start(@config) do |connection|
145
+ value = nil
146
+ connection.on_message(:transacted => true, :q_name => :temporary) do |message|
147
+ value = "received"
148
+ end
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+ end
155
+ end