jruby-jms 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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