stapfen 2.2.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/.gitignore +19 -0
- data/CHANGES.md +16 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +22 -0
- data/README.md +106 -0
- data/Rakefile +10 -0
- data/examples/simple.rb +44 -0
- data/lib/stapfen.rb +25 -0
- data/lib/stapfen/client.rb +8 -0
- data/lib/stapfen/client/jms.rb +118 -0
- data/lib/stapfen/client/kafka.rb +76 -0
- data/lib/stapfen/client/stomp.rb +35 -0
- data/lib/stapfen/destination.rb +59 -0
- data/lib/stapfen/logger.rb +48 -0
- data/lib/stapfen/message.rb +58 -0
- data/lib/stapfen/version.rb +3 -0
- data/lib/stapfen/worker.rb +265 -0
- data/spec/client/jms_spec.rb +199 -0
- data/spec/client/kafka_spec.rb +54 -0
- data/spec/client/stomp_spec.rb +5 -0
- data/spec/client_spec.rb +5 -0
- data/spec/destination_spec.rb +71 -0
- data/spec/logger_spec.rb +41 -0
- data/spec/message_spec.rb +96 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/worker_spec.rb +275 -0
- data/stapfen.gemspec +24 -0
- metadata +93 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if RUBY_PLATFORM == 'java'
|
4
|
+
require 'stapfen/client/jms'
|
5
|
+
|
6
|
+
describe Stapfen::Client::JMS, :java => true do
|
7
|
+
let(:config) { {} }
|
8
|
+
subject(:client) { described_class.new(config) }
|
9
|
+
|
10
|
+
it { should respond_to :connect }
|
11
|
+
|
12
|
+
describe '#can_unreceive?' do
|
13
|
+
subject { client.can_unreceive? }
|
14
|
+
|
15
|
+
it { should be true }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#unreceive' do
|
19
|
+
let(:body) { 'Some body in string form' }
|
20
|
+
let(:message) { double('Message', :destination => orig_destination, :data => body, :getStringProperty => nil) }
|
21
|
+
let(:max_redeliveries) { 2 }
|
22
|
+
let(:dlq) { '/queue/some_queue/dlq' }
|
23
|
+
let(:unreceive_headers) do
|
24
|
+
{ :dead_letter_queue => dlq, :max_redeliveries => max_redeliveries }
|
25
|
+
end
|
26
|
+
let(:orig_destination) { '/queue/some_queue' }
|
27
|
+
|
28
|
+
subject(:unreceive!) { client.unreceive(message, unreceive_headers) }
|
29
|
+
|
30
|
+
context 'with no unreceive[:max_redeliveries] or unreceive[:dead_letter_queue]' do
|
31
|
+
let(:unreceive_headers) { Hash.new }
|
32
|
+
|
33
|
+
it 'should not resend the message' do
|
34
|
+
client.should_not_receive(:publish)
|
35
|
+
|
36
|
+
unreceive!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'On a message with no retry_count in the headers' do
|
41
|
+
before :each do
|
42
|
+
message.stub(:getStringProperty).with('retry_count').and_return(nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should publish it to the same destination with a retry_count of 1' do
|
46
|
+
client.should_receive(:publish) do |dest, the_body, the_headers|
|
47
|
+
expect(dest).to eql orig_destination
|
48
|
+
expect(the_body).to eql body
|
49
|
+
expect(the_headers).to eql({'retry_count' => '1'})
|
50
|
+
end
|
51
|
+
|
52
|
+
unreceive!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'On a message with a retry_count in the headers' do
|
57
|
+
before :each do
|
58
|
+
message.stub(:getStringProperty).with('retry_count').and_return(retry_count)
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
context 'that is less than max_redeliveries' do
|
63
|
+
let(:retry_count) { max_redeliveries - 1 }
|
64
|
+
|
65
|
+
it 'should publish it to the same destination with a retry_count increased by one' do
|
66
|
+
client.should_receive(:publish) do |dest, the_body, the_headers|
|
67
|
+
expect(dest).to eql orig_destination
|
68
|
+
expect(the_body).to eql body
|
69
|
+
|
70
|
+
|
71
|
+
expect(the_headers).to eql({'retry_count' => (retry_count + 1).to_s})
|
72
|
+
end
|
73
|
+
|
74
|
+
unreceive!
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# This is the 'last' attempt to redeliver, so don't send it again
|
79
|
+
context 'that is equal to max_redeliveries' do
|
80
|
+
let(:retry_count) { max_redeliveries }
|
81
|
+
|
82
|
+
it 'should publish it to the DLQ with no retry_count' do
|
83
|
+
client.should_receive(:publish) do |dest, the_body, the_headers|
|
84
|
+
expect(the_body).to eql body
|
85
|
+
expect(dest).to eql dlq
|
86
|
+
|
87
|
+
expect(the_headers).to eql({:original_destination => orig_destination})
|
88
|
+
end
|
89
|
+
|
90
|
+
unreceive!
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'that is greater than max_redeliveries' do
|
95
|
+
let(:retry_count) { max_redeliveries + 1 }
|
96
|
+
|
97
|
+
it 'should publish it to the DLQ with no retry_count' do
|
98
|
+
client.should_receive(:publish) do |dest, the_body, the_headers|
|
99
|
+
expect(the_body).to eql body
|
100
|
+
expect(dest).to eql dlq
|
101
|
+
|
102
|
+
expect(the_headers).to eql({:original_destination => orig_destination})
|
103
|
+
end
|
104
|
+
|
105
|
+
unreceive!
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#connect' do
|
112
|
+
subject(:connection) { client.connect }
|
113
|
+
let(:jms_conn) { double('JMS::Connection') }
|
114
|
+
|
115
|
+
before :each do
|
116
|
+
::JMS::Connection.should_receive(:new).and_return(jms_conn)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should start the connection' do
|
120
|
+
jms_conn.should_receive(:start)
|
121
|
+
expect(connection).to eql(jms_conn)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '#session' do
|
126
|
+
let(:session) { double('JMS::Session') }
|
127
|
+
let(:connection) { double('JMS::Connection') }
|
128
|
+
|
129
|
+
before :each do
|
130
|
+
client.stub(:connection => connection)
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'without a session already' do
|
134
|
+
it 'should create a new session' do
|
135
|
+
connection.should_receive(:create_session).and_return(session)
|
136
|
+
expect(client.session).to eql(session)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'with an existing session' do
|
141
|
+
it 'should return that existing session' do
|
142
|
+
connection.should_receive(:create_session).once.and_return(session)
|
143
|
+
3.times do
|
144
|
+
expect(client.session).to eql(session)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#publish' do
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#closed?' do
|
154
|
+
subject(:result) { client.closed? }
|
155
|
+
|
156
|
+
context 'if a connection exists' do
|
157
|
+
before :each do
|
158
|
+
client.stub(:connection).and_return(double('JMS::Connection'))
|
159
|
+
end
|
160
|
+
|
161
|
+
it { should be false }
|
162
|
+
end
|
163
|
+
|
164
|
+
context 'without a connection' do
|
165
|
+
it { should be true }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#close' do
|
170
|
+
subject(:result) { client.close }
|
171
|
+
let(:connection) { double('JMS::Connection') }
|
172
|
+
|
173
|
+
before :each do
|
174
|
+
client.instance_variable_set(:@connection, connection)
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'without an existing session' do
|
178
|
+
it 'should close the client' do
|
179
|
+
connection.should_receive(:close)
|
180
|
+
expect(result).to be true
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'with an existing session' do
|
185
|
+
let(:session) { double('JMS::Session') }
|
186
|
+
|
187
|
+
before :each do
|
188
|
+
client.instance_variable_set(:@session, session)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should close the client and session' do
|
192
|
+
session.should_receive(:close)
|
193
|
+
connection.should_receive(:close)
|
194
|
+
expect(result).to be true
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'stapfen/client/kafka'
|
4
|
+
|
5
|
+
describe Stapfen::Client::Kafka, :java => true do
|
6
|
+
let(:config) { { :topic => 'test', :groupId => 'groupId', :zookeepers => 'foo' } }
|
7
|
+
let(:consumer) { double('Hermann::Consumer') }
|
8
|
+
|
9
|
+
subject(:client) { described_class.new(config) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(Hermann::Consumer).to receive(:new) { consumer }
|
13
|
+
end
|
14
|
+
|
15
|
+
it { should respond_to :connect }
|
16
|
+
|
17
|
+
describe '#can_unreceive?' do
|
18
|
+
subject { client.can_unreceive? }
|
19
|
+
it { should be false }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#close' do
|
23
|
+
subject(:result) { client.close }
|
24
|
+
|
25
|
+
context 'with a connection' do
|
26
|
+
it 'should close the client' do
|
27
|
+
allow(consumer).to receive(:shutdown)
|
28
|
+
expect(result).to be true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
context 'without a connection' do
|
32
|
+
it 'returns false' do
|
33
|
+
expect(Hermann::Consumer).to receive(:new) { nil }
|
34
|
+
expect(consumer).to_not receive(:shutdown)
|
35
|
+
expect(result).to be false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#subscribe' do
|
41
|
+
let(:topic) { 'topic' }
|
42
|
+
let(:destination) { double('Destination') }
|
43
|
+
let(:msg) { 'foo' }
|
44
|
+
it 'yields to the block and passes in consumed message' do
|
45
|
+
allow(destination).to receive(:as_kafka) { topic }
|
46
|
+
allow(Stapfen::Destination).to receive(:from_string) { destination }
|
47
|
+
allow(consumer).to receive(:consume).with(topic).and_yield(msg)
|
48
|
+
|
49
|
+
expect{ |b|
|
50
|
+
client.subscribe(destination, nil, &b)
|
51
|
+
}.to yield_with_args(msg)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stapfen/destination'
|
3
|
+
|
4
|
+
describe Stapfen::Destination do
|
5
|
+
it { should respond_to :name }
|
6
|
+
it { should respond_to :type }
|
7
|
+
|
8
|
+
describe '#as_kafka' do
|
9
|
+
let(:name) { 'topic' }
|
10
|
+
subject(:destination) do
|
11
|
+
d = described_class.new
|
12
|
+
d.type = :topic
|
13
|
+
d.name = name
|
14
|
+
d.as_kafka
|
15
|
+
end
|
16
|
+
|
17
|
+
it { should be_instance_of String }
|
18
|
+
it { should eql "topic" }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#as_jms' do
|
22
|
+
let(:name) { 'rspec/dlq' }
|
23
|
+
subject(:destination) do
|
24
|
+
d = described_class.new
|
25
|
+
d.type = :queue
|
26
|
+
d.name = name
|
27
|
+
d.as_jms
|
28
|
+
end
|
29
|
+
|
30
|
+
it { should be_instance_of String }
|
31
|
+
it { should eql "queue://#{name}" }
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#as_stomp' do
|
35
|
+
let(:name) { 'rspec/dlq' }
|
36
|
+
|
37
|
+
subject(:destination) do
|
38
|
+
d = described_class.new
|
39
|
+
d.type = :queue
|
40
|
+
d.name = name
|
41
|
+
d.as_stomp
|
42
|
+
end
|
43
|
+
|
44
|
+
it { should be_instance_of String }
|
45
|
+
it { should eql "/queue/#{name}" }
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'class methods' do
|
49
|
+
describe '#from_string' do
|
50
|
+
subject(:destination) { described_class.from_string(name) }
|
51
|
+
|
52
|
+
context 'a simple queue' do
|
53
|
+
let(:name) { '/queue/rspec' }
|
54
|
+
its(:name) { should eql 'rspec' }
|
55
|
+
its(:type) { should eql :queue }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'a queue with slashes' do
|
59
|
+
let(:name) { '/queue/rspec/dlq' }
|
60
|
+
its(:name) { should eql 'rspec/dlq' }
|
61
|
+
its(:type) { should eql :queue }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'a complex topic' do
|
65
|
+
let(:name) { '/topic/rspec/dlq' }
|
66
|
+
its(:name) { should eql 'rspec/dlq' }
|
67
|
+
its(:type) { should eql :topic }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/logger_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Stapfen::Logger do
|
4
|
+
let(:mixin) do
|
5
|
+
Class.new do
|
6
|
+
include Stapfen::Logger
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
subject(:logger) { mixin.new }
|
11
|
+
|
12
|
+
context 'instance methods' do
|
13
|
+
it { should respond_to :info }
|
14
|
+
it { should respond_to :debug }
|
15
|
+
it { should respond_to :warn }
|
16
|
+
it { should respond_to :error }
|
17
|
+
|
18
|
+
context 'without an initialized logger' do
|
19
|
+
before :each do
|
20
|
+
logger.class.stub(:logger)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should discard info messages' do
|
24
|
+
expect(logger.info('rspec')).to be false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with an initialized logger' do
|
29
|
+
let(:plogger) { double('RSpec Logger') }
|
30
|
+
|
31
|
+
before :each do
|
32
|
+
logger.class.stub(:logger).and_return(lambda { plogger })
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should pass info messages along' do
|
36
|
+
plogger.should_receive(:info)
|
37
|
+
expect(logger.info('rspec')).to be true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stapfen/message'
|
3
|
+
|
4
|
+
if RUBY_PLATFORM == 'java'
|
5
|
+
require 'java'
|
6
|
+
require File.expand_path('./activemq-all-5.8.0.jar')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Stapfen::Message do
|
10
|
+
context 'accessors' do
|
11
|
+
it { should respond_to :message_id }
|
12
|
+
it { should respond_to :body }
|
13
|
+
it { should respond_to :original }
|
14
|
+
it { should respond_to :destination }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#initialize' do
|
18
|
+
it 'should accept :body' do
|
19
|
+
body = 'hello'
|
20
|
+
m = described_class.new(:body => body)
|
21
|
+
expect(m.body).to eql(body)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'class methods' do
|
26
|
+
let(:body) { 'hello stapfen' }
|
27
|
+
let(:destination) { '/queue/rspec' }
|
28
|
+
let(:message_id) { rand.to_s }
|
29
|
+
|
30
|
+
describe '#from_stomp' do
|
31
|
+
subject(:result) { described_class.from_stomp(message) }
|
32
|
+
|
33
|
+
context 'when passed something other than a Stomp::Message' do
|
34
|
+
let(:message) { 'hello' }
|
35
|
+
|
36
|
+
it 'should raise an error' do
|
37
|
+
expect { result }.to raise_error(Stapfen::InvalidMessageError)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when passed a Stomp::Message' do
|
42
|
+
let(:message) do
|
43
|
+
m = Stomp::Message.new('') # empty frame
|
44
|
+
m.body = body
|
45
|
+
headers = {'destination' => destination,
|
46
|
+
'message-id' => message_id}
|
47
|
+
m.headers = headers
|
48
|
+
m
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should create an instance' do
|
52
|
+
expect(result).to be_instance_of Stapfen::Message
|
53
|
+
end
|
54
|
+
|
55
|
+
its(:body) { should eql body }
|
56
|
+
its(:destination) { should eql destination }
|
57
|
+
its(:message_id) { should eql message_id }
|
58
|
+
its(:original) { should be message }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#from_jms', :java => true do
|
63
|
+
subject(:result) { described_class.from_jms(message) }
|
64
|
+
|
65
|
+
context 'when passed something other than a JMS message' do
|
66
|
+
let(:message) { 'hello' }
|
67
|
+
|
68
|
+
it 'should raise an error' do
|
69
|
+
expect { result }.to raise_error(Stapfen::InvalidMessageError)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when passed an ActiveMQBytesMessage' do
|
74
|
+
let(:destination) { 'queue://rspec' }
|
75
|
+
|
76
|
+
let(:message) do
|
77
|
+
m = Java::OrgApacheActivemqCommand::ActiveMQBytesMessage.new
|
78
|
+
m.stub(:jms_destination => double('ActiveMQDestination mock', :getQualifiedName => destination))
|
79
|
+
m.stub(:jms_message_id => message_id)
|
80
|
+
m.stub(:data => body)
|
81
|
+
m
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should create an instance' do
|
85
|
+
expect(result).to be_instance_of Stapfen::Message
|
86
|
+
end
|
87
|
+
|
88
|
+
its(:body) { should eql body }
|
89
|
+
its(:destination) { should eql destination }
|
90
|
+
its(:message_id) { should eql message_id }
|
91
|
+
its(:original) { should be message }
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|