rabbitmq-jruby-client 0.1.7
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.
- data/MIT-LICENSE +22 -0
- data/README +55 -0
- data/lib/commons-cli-1.1.jar +0 -0
- data/lib/commons-io-1.2.jar +0 -0
- data/lib/junit.jar +0 -0
- data/lib/rabbitmq-client.jar +0 -0
- data/lib/rabbitmq_client.rb +189 -0
- data/spec/rabbitmq_client_spec.rb +125 -0
- metadata +62 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2009 Jerry Luk <jerryluk@gmail.com>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
|
4
|
+
obtaining a copy of this software and associated documentation
|
|
5
|
+
files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use,
|
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the
|
|
9
|
+
Software is furnished to do so, subject to the following
|
|
10
|
+
conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
RabbitMQ JRuby Client
|
|
2
|
+
=====================
|
|
3
|
+
rabbitmq-jruby-client allows you to use RabbitMQ Client from JRuby using the official Java RabbitMQ client from Rabbit Technologies
|
|
4
|
+
See more at http://www.rabbitmq.com
|
|
5
|
+
|
|
6
|
+
Getting Started
|
|
7
|
+
===============
|
|
8
|
+
1. Make sure you can install RubyGem from GitHub (you only have to do this once): jruby -S gem sources -a http://gems.github.com
|
|
9
|
+
2. Install and start RabbitMQ (see below)
|
|
10
|
+
3. Install JRuby RabbitMQ Client: jruby -S gem install jerryluk-rabbitmq-jruby-client
|
|
11
|
+
|
|
12
|
+
Example Usage
|
|
13
|
+
=============
|
|
14
|
+
gem 'jerryluk-rabbitmq-jruby-client'
|
|
15
|
+
require 'rabbitmq_client'
|
|
16
|
+
|
|
17
|
+
# Initializes the new client and connect to the server
|
|
18
|
+
client = RabbitMQClient.new
|
|
19
|
+
|
|
20
|
+
# Initializes a new queue
|
|
21
|
+
queue = client.queue('queue_name')
|
|
22
|
+
|
|
23
|
+
# Initializes a new exchange
|
|
24
|
+
exchange = client.exchange('exchange_name')
|
|
25
|
+
|
|
26
|
+
# Connects queue with the exchange
|
|
27
|
+
queue.bind(exchange)
|
|
28
|
+
|
|
29
|
+
# Publish a message to the queue
|
|
30
|
+
queue.publish('message body')
|
|
31
|
+
|
|
32
|
+
# Retrieve a message from the queue
|
|
33
|
+
message = queue.retrieve
|
|
34
|
+
|
|
35
|
+
# Subscribe to a queue with callback. The callback will be run in a new thread for each new message.
|
|
36
|
+
queue.subscribe do |message|
|
|
37
|
+
# do something with message
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Subscribe to a queue in a loop. No new thread will be created.
|
|
41
|
+
queue.loop_subscribe do |message|
|
|
42
|
+
# do something with message
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Installing RabbitMQ on OS X
|
|
47
|
+
===========================
|
|
48
|
+
1. Install MacPorts
|
|
49
|
+
2. sudo port install erlang
|
|
50
|
+
3. wget http://www.rabbitmq.com/releases/rabbitmq-server/v1.5.3/rabbitmq-server-generic-unix-1.5.3.tar.gz
|
|
51
|
+
4. tar zxvf rabbitmq-server-1.5.3.tar.gz
|
|
52
|
+
5. sudo mv rabbitmq_server-1.5.3 /opt/local/lib/erlang/lib
|
|
53
|
+
|
|
54
|
+
To run RabbitMQ
|
|
55
|
+
6. sudo /opt/local/lib/erlang/lib/rabbitmq_server-1.5.3/sbin/rabbitmq-server
|
|
Binary file
|
|
Binary file
|
data/lib/junit.jar
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
require 'java'
|
|
2
|
+
require File.dirname(__FILE__) + '/junit.jar'
|
|
3
|
+
require File.dirname(__FILE__) + '/commons-cli-1.1.jar'
|
|
4
|
+
require File.dirname(__FILE__) + '/commons-io-1.2.jar'
|
|
5
|
+
require File.dirname(__FILE__) + '/rabbitmq-client.jar'
|
|
6
|
+
|
|
7
|
+
class RabbitMQClient
|
|
8
|
+
include ObjectSpace
|
|
9
|
+
include_class('com.rabbitmq.client.Connection')
|
|
10
|
+
include_class('com.rabbitmq.client.ConnectionParameters')
|
|
11
|
+
include_class('com.rabbitmq.client.ConnectionFactory')
|
|
12
|
+
include_class('com.rabbitmq.client.Channel')
|
|
13
|
+
include_class('com.rabbitmq.client.Consumer')
|
|
14
|
+
include_class('com.rabbitmq.client.DefaultConsumer')
|
|
15
|
+
include_class('com.rabbitmq.client.QueueingConsumer')
|
|
16
|
+
include_class('com.rabbitmq.client.MessageProperties')
|
|
17
|
+
include_class('java.lang.InterruptedException')
|
|
18
|
+
include_class('java.lang.String') { |package, name| "J#{name}" }
|
|
19
|
+
|
|
20
|
+
class RabbitMQClientError < StandardError;end
|
|
21
|
+
|
|
22
|
+
class QueueConsumer < DefaultConsumer
|
|
23
|
+
def initialize(channel, block)
|
|
24
|
+
@channel = channel
|
|
25
|
+
@block = block
|
|
26
|
+
super(channel)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def handleDelivery(consumer_tag, envelope, properties, body)
|
|
30
|
+
delivery_tag = envelope.get_delivery_tag
|
|
31
|
+
message_body = Marshal.load(String.from_java_bytes(body))
|
|
32
|
+
# TODO: Do we need to do something with properties?
|
|
33
|
+
@block.call message_body
|
|
34
|
+
@channel.basic_ack(delivery_tag, false)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class Queue
|
|
39
|
+
def initialize(name, channel, durable=false)
|
|
40
|
+
@name = name
|
|
41
|
+
@durable = durable
|
|
42
|
+
@channel = channel
|
|
43
|
+
@channel.queue_declare(name, durable)
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def bind(exchange, routing_key='')
|
|
48
|
+
@routing_key = routing_key
|
|
49
|
+
@exchange = exchange
|
|
50
|
+
raise RabbitMQClientError, "queue and exchange has different durable property" unless @durable == exchange.durable
|
|
51
|
+
@channel.queue_bind(@name, @exchange.name, @routing_key)
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Set props for different type of message. Currently they are:
|
|
56
|
+
# RabbitMQClient::MessageProperties::MINIMAL_BASIC
|
|
57
|
+
# RabbitMQClient::MessageProperties::MINIMAL_PERSISTENT_BASIC
|
|
58
|
+
# RabbitMQClient::MessageProperties::BASIC
|
|
59
|
+
# RabbitMQClient::MessageProperties::PERSISTENT_BASIC
|
|
60
|
+
# RabbitMQClient::MessageProperties::TEXT_PLAIN
|
|
61
|
+
# RabbitMQClient::MessageProperties::PERSISTENT_TEXT_PLAIN
|
|
62
|
+
def publish(message_body, props=nil)
|
|
63
|
+
auto_bind
|
|
64
|
+
message_body_byte = Marshal.dump(message_body).to_java_bytes
|
|
65
|
+
@channel.basic_publish(@exchange.name, @routing_key, props, message_body_byte)
|
|
66
|
+
message_body
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def persistent_publish(message_body, props=MessageProperties::PERSISTENT_TEXT_PLAIN)
|
|
70
|
+
raise RabbitMQClientError, "can only publish persistent message to durable queue" unless @durable
|
|
71
|
+
publish(message_body, props)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def retrieve
|
|
75
|
+
auto_bind
|
|
76
|
+
message_body = nil
|
|
77
|
+
no_ack = false
|
|
78
|
+
response = @channel.basic_get(@name, no_ack)
|
|
79
|
+
if response
|
|
80
|
+
props = response.get_props
|
|
81
|
+
message_body = Marshal.load(String.from_java_bytes(response.get_body))
|
|
82
|
+
delivery_tag = response.get_envelope.get_delivery_tag
|
|
83
|
+
@channel.basic_ack(delivery_tag, false)
|
|
84
|
+
end
|
|
85
|
+
message_body
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def subscribe(&block)
|
|
89
|
+
no_ack = false
|
|
90
|
+
@channel.basic_consume(@name, no_ack, QueueConsumer.new(@channel, block))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def loop_subscribe(&block)
|
|
94
|
+
no_ack = false
|
|
95
|
+
consumer = QueueingConsumer.new(@channel)
|
|
96
|
+
@channel.basic_consume(@name, no_ack, consumer)
|
|
97
|
+
loop do
|
|
98
|
+
begin
|
|
99
|
+
delivery = consumer.next_delivery
|
|
100
|
+
message_body = Marshal.load(String.from_java_bytes(delivery.get_body))
|
|
101
|
+
block.call message_body
|
|
102
|
+
@channel.basic_ack(delivery.get_envelope.get_delivery_tag, false)
|
|
103
|
+
rescue InterruptedException => ie
|
|
104
|
+
next
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
protected
|
|
110
|
+
def auto_bind
|
|
111
|
+
unless @exchange
|
|
112
|
+
exchange = Exchange.new("#{@name}_exchange", 'fanout', @channel, @durable)
|
|
113
|
+
self.bind(exchange)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class Exchange
|
|
119
|
+
attr_reader :name
|
|
120
|
+
attr_reader :durable
|
|
121
|
+
|
|
122
|
+
def initialize(name, type, channel, durable=false)
|
|
123
|
+
@name = name
|
|
124
|
+
@type = type
|
|
125
|
+
@durable = durable
|
|
126
|
+
@channel = channel
|
|
127
|
+
@channel.exchange_declare(@name, type.to_s, durable)
|
|
128
|
+
self
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Class Methods
|
|
133
|
+
class << self
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
attr_reader :channel
|
|
137
|
+
attr_reader :connection
|
|
138
|
+
|
|
139
|
+
# Instance Methods
|
|
140
|
+
def initialize(options={})
|
|
141
|
+
# server address
|
|
142
|
+
@host = options[:host] || '127.0.0.1'
|
|
143
|
+
@port = options[:port] || 5672
|
|
144
|
+
|
|
145
|
+
# login details
|
|
146
|
+
@username = options[:username] || 'guest'
|
|
147
|
+
@password = options[:password] || 'guest'
|
|
148
|
+
@vhost = options[:vhost] || '/'
|
|
149
|
+
|
|
150
|
+
# queues and exchanges
|
|
151
|
+
@queues = {}
|
|
152
|
+
@exchanges = {}
|
|
153
|
+
|
|
154
|
+
connect unless options[:no_auto_connect]
|
|
155
|
+
# Disconnect before the object is destroyed
|
|
156
|
+
define_finalizer(self, lambda {|id| self.disconnect if self.connected? })
|
|
157
|
+
self
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def connect
|
|
161
|
+
params = ConnectionParameters.new
|
|
162
|
+
params.set_username(@username)
|
|
163
|
+
params.set_password(@password)
|
|
164
|
+
params.set_virtual_host(@vhost)
|
|
165
|
+
params.set_requested_heartbeat(0)
|
|
166
|
+
conn_factory = ConnectionFactory.new(params)
|
|
167
|
+
@connection = conn_factory.new_connection(@host, @port)
|
|
168
|
+
@channel = @connection.create_channel
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def disconnect
|
|
172
|
+
@channel.close
|
|
173
|
+
@connection.close
|
|
174
|
+
@connection = nil
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def connected?
|
|
178
|
+
@connection != nil
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def queue(name, durable=false)
|
|
182
|
+
@queues[name] ||= Queue.new(name, @channel, durable)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def exchange(name, type='fanout', durable=false)
|
|
186
|
+
@exchanges[name] ||= Exchange.new(name, type, @channel, durable)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'spec'
|
|
3
|
+
require File.dirname(__FILE__) + '/../lib/rabbitmq_client'
|
|
4
|
+
|
|
5
|
+
describe RabbitMQClient do
|
|
6
|
+
before(:each) do
|
|
7
|
+
@client = RabbitMQClient.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after(:each) do
|
|
11
|
+
@client.disconnect
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should able to create a connection" do
|
|
15
|
+
@client.connection.should_not be_nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should able to create a channel" do
|
|
19
|
+
@client.channel.should_not be_nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should be able to create a new exchange" do
|
|
23
|
+
exchange = @client.exchange('test_exchange', 'direct')
|
|
24
|
+
exchange.should_not be_nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe Queue, "Basic non-persistent queue" do
|
|
28
|
+
before(:each) do
|
|
29
|
+
@queue = @client.queue('test_queue')
|
|
30
|
+
@exchange = @client.exchange('test_exchange', 'direct')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should able to create a queue" do
|
|
34
|
+
@queue.should_not be_nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should able to bind to an exchange" do
|
|
38
|
+
@queue.bind(@exchange).should_not be_nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should able to publish and retrieve a message" do
|
|
42
|
+
@queue.bind(@exchange)
|
|
43
|
+
@queue.publish('Hello World')
|
|
44
|
+
@queue.retrieve.should == 'Hello World'
|
|
45
|
+
@queue.publish('人大')
|
|
46
|
+
@queue.retrieve.should == '人大'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should able to subscribe with a callback function" do
|
|
50
|
+
a = 0
|
|
51
|
+
@queue.bind(@exchange)
|
|
52
|
+
@queue.subscribe do |v|
|
|
53
|
+
a += v.to_i
|
|
54
|
+
end
|
|
55
|
+
@queue.publish("1")
|
|
56
|
+
@queue.publish("2")
|
|
57
|
+
sleep 1
|
|
58
|
+
a.should == 3
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should able to subscribe to a queue using loop_subscribe" do
|
|
62
|
+
a = 0
|
|
63
|
+
@queue.bind(@exchange)
|
|
64
|
+
Thread.new do
|
|
65
|
+
begin
|
|
66
|
+
timeout(1) do
|
|
67
|
+
@queue.loop_subscribe do |v|
|
|
68
|
+
a += v.to_i
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
rescue Timeout::Error => e
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
@queue.publish("1")
|
|
75
|
+
@queue.publish("2")
|
|
76
|
+
sleep 2
|
|
77
|
+
a.should == 3
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should raise an exception if binding a persistent queue with non-persistent exchange and vice versa" do
|
|
81
|
+
persistent_queue = @client.queue('test_queue1', true)
|
|
82
|
+
persistent_exchange = @client.exchange('test_exchange1', 'fanout', true)
|
|
83
|
+
lambda { persistent_queue.bind(@exchange) }.should raise_error(RabbitMQClient::RabbitMQClientError)
|
|
84
|
+
lambda { @queue.bind(persistent_exchange) }.should raise_error(RabbitMQClient::RabbitMQClientError)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should raise an exception if publish a persistent message on non-duration queue" do
|
|
88
|
+
@queue.bind(@exchange)
|
|
89
|
+
lambda { @queue.persistent_publish('Hello') }.should raise_error(RabbitMQClient::RabbitMQClientError)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe Queue, "Basic persistent queue" do
|
|
94
|
+
before(:each) do
|
|
95
|
+
@queue = @client.queue('test_durable_queue', true)
|
|
96
|
+
@exchange = @client.exchange('test_durable_exchange', 'fanout', true)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should able to create a queue" do
|
|
100
|
+
@queue.should_not be_nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should able to bind to an exchange" do
|
|
104
|
+
@queue.bind(@exchange).should_not be_nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should able to publish and retrieve a message" do
|
|
108
|
+
@queue.bind(@exchange)
|
|
109
|
+
@queue.persistent_publish('Hello World')
|
|
110
|
+
@queue.retrieve.should == 'Hello World'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "should able to subscribe with a callback function" do
|
|
114
|
+
a = 0
|
|
115
|
+
@queue.bind(@exchange)
|
|
116
|
+
@queue.subscribe do |v|
|
|
117
|
+
a += v.to_i
|
|
118
|
+
end
|
|
119
|
+
@queue.persistent_publish("1")
|
|
120
|
+
@queue.persistent_publish("2")
|
|
121
|
+
sleep 1
|
|
122
|
+
a.should == 3
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rabbitmq-jruby-client
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.7
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jerry Luk
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-02-09 00:00:00 -08:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: A RabbitMQ client for JRuby
|
|
17
|
+
email: jerryluk@gmail.com
|
|
18
|
+
executables: []
|
|
19
|
+
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
|
|
24
|
+
files:
|
|
25
|
+
- README
|
|
26
|
+
- MIT-LICENSE
|
|
27
|
+
- lib/commons-cli-1.1.jar
|
|
28
|
+
- lib/commons-io-1.2.jar
|
|
29
|
+
- lib/rabbitmq-client.jar
|
|
30
|
+
- lib/junit.jar
|
|
31
|
+
- lib/rabbitmq_client.rb
|
|
32
|
+
- spec/rabbitmq_client_spec.rb
|
|
33
|
+
has_rdoc: true
|
|
34
|
+
homepage: http://www.linkedin.com/in/jerryluk
|
|
35
|
+
licenses: []
|
|
36
|
+
|
|
37
|
+
post_install_message:
|
|
38
|
+
rdoc_options: []
|
|
39
|
+
|
|
40
|
+
require_paths:
|
|
41
|
+
- lib
|
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: "0"
|
|
47
|
+
version:
|
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: "0"
|
|
53
|
+
version:
|
|
54
|
+
requirements: []
|
|
55
|
+
|
|
56
|
+
rubyforge_project:
|
|
57
|
+
rubygems_version: 1.3.5
|
|
58
|
+
signing_key:
|
|
59
|
+
specification_version: 3
|
|
60
|
+
summary: A RabbitMQ client for JRuby
|
|
61
|
+
test_files:
|
|
62
|
+
- spec/rabbitmq_client_spec.rb
|