nebulous_stomp 1.1.5

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.
@@ -0,0 +1,254 @@
1
+ # coding: UTF-8
2
+
3
+ require 'nebulous'
4
+
5
+ require_relative 'stomp_handler'
6
+ require_relative 'redis_handler'
7
+ require_relative 'message'
8
+
9
+
10
+ module Nebulous
11
+
12
+
13
+ ##
14
+ # Class to handle a request which returns a Message
15
+ #
16
+ # Note that this has changed since 0.1.0. The principal difference is we
17
+ # return a Nebulous::Message; the NebResponse class no longer exists.
18
+ #
19
+ class NebRequest
20
+
21
+
22
+ # The target name as set up by call to Nebulous::add_target
23
+ attr_reader :target
24
+
25
+ # The 'verb' part of the message
26
+ attr_reader :verb
27
+
28
+ # The 'parameters' part of the message
29
+ attr_reader :params
30
+
31
+ # The 'description' part of the message
32
+ attr_reader :desc
33
+
34
+ # The 'replyID' header to use in this message
35
+ attr_reader :replyID
36
+
37
+ # Message timeout in seconds
38
+ attr_reader :mTimeout
39
+
40
+ # Cache timeout (fade and forget) in seconds
41
+ attr_reader :cTimeout
42
+
43
+ # The message
44
+ attr_reader :message
45
+
46
+ # The STOMP queue to send the request to
47
+ attr_reader :requestQ
48
+
49
+ # The STOMP queue to listen for responses on
50
+ attr_reader :responseQ
51
+
52
+
53
+ ##
54
+ # :call-seq:
55
+ # NebRequest.new(target, verb)
56
+ # NebRequest.new(target, verb, params)
57
+ # NebRequest.new(target, verb, params, desc)
58
+ #
59
+ # Create a new request. Raises Nebulous::NebulousError if anything goes
60
+ # wrong.
61
+ #
62
+ # Parameters:
63
+ # target [Symbol] the target name to send the request to
64
+ # verb [String] the 'verb' part of the message
65
+ # params [String] the 'parameters' part of the message
66
+ # desc [String] the 'description' part of the message
67
+ # stompHandler ONLY FOR TESTING
68
+ # redisHandler ONLY FOR TESTING
69
+ #
70
+ def initialize( target,
71
+ verb,
72
+ params=nil,
73
+ desc=nil,
74
+ stompHandler=nil,
75
+ redisHandler=nil )
76
+
77
+ Nebulous.logger.debug(__FILE__) {"New NebRequest for verb #{verb}"}
78
+
79
+ @target = target.to_s
80
+ @verb = verb.to_s
81
+ @params = params.nil? ? nil : params.to_s
82
+ @desc = desc.nil? ? nil : desc.to_s
83
+ @stomp_handler = stompHandler
84
+ @redis_handler = redisHandler
85
+ @requestQ = nil
86
+ @responseQ = nil
87
+ @message = nil
88
+ @replyID = nil
89
+ @mTimeout = 0
90
+ @cTimeout = 0
91
+
92
+ @redis_handler ||= RedisHandler.new( Param.get(:redisConnectHash) )
93
+ @stomp_handler ||= StompHandler.new( Param.get(:stompConnectHash) )
94
+
95
+ neb_connect if nebulous_on?
96
+ end
97
+
98
+
99
+ ##
100
+ # :call-seq:
101
+ # request.send_no_cache -> (Message)
102
+ # request.send_no_cache(mTimeout) -> (Message)
103
+ #
104
+ # Send a request and return the response, without using the cache.
105
+ #
106
+ # Parameters:
107
+ # mTimeout [Fixnum] Message timout in seconds - defaults to @mTimeout
108
+ #
109
+ # Raises NebulousTimeout or NebulousError as necessary.
110
+ #
111
+ # Note that this routine completely ignores Redis. It doesn't just not
112
+ # check the cache; it also doesn't update it.
113
+ #
114
+ def send_no_cache(mTimeout=@mTimeout)
115
+ return nil unless nebulous_on?
116
+
117
+ # If we've lost the connection then reconnect but *keep replyID*
118
+ @stomp_handler.stomp_connect unless @stomp_handler.connected?
119
+ @replyID = @stomp_handler.calc_reply_id if @replyID.nil?
120
+
121
+ neb_qna(mTimeout)
122
+
123
+ ensure
124
+ @stomp_handler.stomp_disconnect if @stomp_handler
125
+ end
126
+
127
+
128
+ ##
129
+ # ::call-seq::
130
+ # request.send -> (Message)
131
+ # request.send(mTimeout) -> (Message)
132
+ # request.send(mTimeout,cTimeout) -> (Message)
133
+ #
134
+ # As send_nocache, but without not using the cache :)
135
+ #
136
+ # Parameters:
137
+ # mTimeout [Fixnum] Message timout in seconds - defaults to @mTimeout
138
+ # cTimeout [Fixnum] Cache timout in seconds - defaults to @cTimeout
139
+ #
140
+ # Raises NebulousTimeout, NebulousError as necessary.
141
+ #
142
+ # We use Redis for the cache. This is possibly like using a sledgehammer
143
+ # to crack a nut, but it certainly makes things very simple.
144
+ #
145
+ def send(mTimeout=@mTimeout, cTimeout=@cTimeout)
146
+ return nil unless nebulous_on?
147
+ return send_no_cache(mTimeout) unless redis_on?
148
+
149
+ @redis_handler.connect unless @redis_handler.connected?
150
+
151
+ found = @redis_handler.get(@message.protocol_json)
152
+ return Message.from_cache(found) unless found.nil?
153
+
154
+ # No answer in Redis -- ask Nebulous
155
+ nebMess = send_no_cache(mTimeout)
156
+ @redis_handler.set(@message.protocol_json, nebMess.to_cache, ex: cTimeout)
157
+
158
+ nebMess
159
+
160
+ ensure
161
+ @redis_handler.quit if @redis_handler
162
+ end
163
+
164
+
165
+ ##
166
+ # :call-seq:
167
+ # request.clear_cache -> self
168
+ #
169
+ # Clear the cache of responses to this request - just this request.
170
+ #
171
+ def clear_cache
172
+ return self unless redis_on?
173
+ @redis_handler.connect unless @redis_handler.connected?
174
+ @redis_handler.del(@message.protocol_json)
175
+
176
+ self
177
+
178
+ ensure
179
+ @redis_handler.quit if @redis_handler
180
+ end
181
+
182
+
183
+ ##
184
+ # :call-seq:
185
+ # request.redis_on? -> (boolean)
186
+ #
187
+ # Return true if Redis is turned on in the *config*
188
+ #
189
+ # (If you want to know if we are conected to Redis, try
190
+ # `@redis_handler.connected?`)
191
+ #
192
+ def redis_on?
193
+ @redis_handler && @redis_handler.redis_on?
194
+ end
195
+
196
+
197
+ ##
198
+ # :call-seq:
199
+ # request.nebulous_on? -> (boolean)
200
+ #
201
+ # Return true if Nebulous is turned on in the *config*
202
+ #
203
+ def nebulous_on?
204
+ @stomp_handler && @stomp_handler.nebulous_on?
205
+ end
206
+
207
+
208
+ private
209
+
210
+
211
+ ##
212
+ # Connect to STOMP etc and do initial setup
213
+ # Called automatically by initialize, if Nebulous is 'on' in the config.
214
+ #
215
+ def neb_connect
216
+ targetHash = Param.get_target(@target)
217
+ raise NebulousError, "Unknown target #{target}" if targetHash.nil?
218
+
219
+ @cTimeout = Param.get(:cacheTimeout)
220
+ @mTimeout = targetHash[:messageTimeout] || Param.get(:messageTimeout)
221
+ @requestQ = targetHash[:sendQueue]
222
+ @responseQ = targetHash[:receiveQueue]
223
+ @message = Message.from_parts(@responseQ, nil, verb, params, desc)
224
+
225
+ @stomp_handler.stomp_connect
226
+ @replyID = @stomp_handler.calc_reply_id
227
+
228
+ self
229
+ end
230
+
231
+
232
+ ##
233
+ # Send a message via STOMP and wait for a response
234
+ #
235
+ # Note: this used to return a Stomp::Message, but now it returns a
236
+ # Nebulous::Message.
237
+ #
238
+ def neb_qna(mTimeout)
239
+ @stomp_handler.send_message(@requestQ, @message)
240
+
241
+ response = nil
242
+ @stomp_handler.listen_with_timeout(@responseQ, mTimeout) do |msg|
243
+ response = msg
244
+ end
245
+
246
+ response
247
+ end
248
+
249
+
250
+ end # of NebRequest
251
+
252
+
253
+ end
254
+
@@ -0,0 +1,39 @@
1
+ # coding: UTF-8
2
+
3
+ require 'nebulous'
4
+
5
+ require_relative 'nebrequest'
6
+ require_relative 'stomp_handler_null'
7
+ require_relative 'redis_handler_null'
8
+
9
+
10
+ module Nebulous
11
+
12
+
13
+ ##
14
+ # Class to fake a NebRequest
15
+ #
16
+ class NebRequestNull < NebRequest
17
+
18
+ def initialize( target, verb, params=nil, desc=nil )
19
+ sh = StompHandlerNull.new( Param.get(:stompConnectHash) )
20
+ rh = RedisHandlerNull.new( Param.get(:redisConnectHash) )
21
+ super(target, verb, params, desc, sh, rh)
22
+ end
23
+
24
+
25
+ def insert_fake_stomp(message)
26
+ @stomp_handler.insert_fake(message)
27
+ end
28
+
29
+
30
+ def insert_fake_redis(key, value)
31
+ @redis_handler.insert_fake(key, value)
32
+ end
33
+
34
+
35
+ end
36
+
37
+
38
+ end
39
+
@@ -0,0 +1,139 @@
1
+ # coding: UTF-8
2
+
3
+
4
+ module Nebulous
5
+
6
+
7
+ ##
8
+ # 'Singleton' 'object' that stores parameters.
9
+ #
10
+ module Param
11
+ extend self
12
+
13
+
14
+ # Default parameters hash
15
+ ParamDefaults = { stompConnectHash: {},
16
+ redisConnectHash: {},
17
+ messageTimeout: 10,
18
+ cacheTimeout: 120,
19
+ logger: nil,
20
+ targets: {} }
21
+
22
+ # Default hash for each target
23
+ TargetDefaults = { sendQueue: nil,
24
+ receiveQueue: nil,
25
+ messageTimeout: nil }
26
+
27
+
28
+ ##
29
+ # Set the initial parameter string. This also has the effect of resetting
30
+ # everything.
31
+ #
32
+ # Parameters default to Param::ParamDefaults. keys passed in parameter p to
33
+ # ovveride those defaults must match, or a NebulousError will result.
34
+ #
35
+ # This method is only called by Nebulous::init().
36
+ #
37
+ def set(p={})
38
+ raise NebulousError, "Invalid initialisation hash" unless p.kind_of?(Hash)
39
+
40
+ validate(ParamDefaults, p, "Unknown initialisation hash")
41
+
42
+ @params = ParamDefaults.merge(p)
43
+ end
44
+
45
+
46
+ ##
47
+ # Add a Nebulous target. Raises NebulousError if anything looks screwy.
48
+ #
49
+ # Parameters:
50
+ # n -- target name
51
+ # t -- hash, which must follow the pattern set by Param::TargetDefaults
52
+ #
53
+ # Used only by Nebulous::init
54
+ #
55
+ def add_target(n, t)
56
+ raise NebulousError, "Invalid target hash" unless t.kind_of?(Hash)
57
+
58
+ validate(TargetDefaults, t, "Unknown target hash")
59
+
60
+ raise NebulousError, "Config Problem - Target missing 'send'" \
61
+ if t[:sendQueue].nil?
62
+
63
+ raise NebulousError, "Config Problem - Target missing 'receive'" \
64
+ if t[:receiveQueue].nil?
65
+
66
+ @params ||= ParamDefaults
67
+ @params[:targets][n.to_sym] = TargetDefaults.merge(t)
68
+ end
69
+
70
+
71
+
72
+ ##
73
+ # Set a logger instance
74
+ #
75
+ def set_logger(lg)
76
+ raise NebulousError unless lg.kind_of?(Logger) || lg.nil?
77
+ @logger = lg
78
+ end
79
+
80
+
81
+ ##
82
+ # Get the logger instance
83
+ #
84
+ def get_logger; @logger; end
85
+
86
+
87
+ ##
88
+ # Get the whole parameter hash. Probably only useful for testing.
89
+ #
90
+ def get_all()
91
+ @params
92
+ end
93
+
94
+
95
+ ##
96
+ # Get a the value of the parameter with the key p.
97
+ #
98
+ def get(p)
99
+ @params ||= ParamDefaults
100
+ @params[p.to_sym]
101
+ end
102
+
103
+
104
+ ##
105
+ # Given a target name, return the corresponding target hash
106
+ #
107
+ def get_target(name)
108
+ t = Param.get(:targets)
109
+ x = (t && t.kind_of?(Hash)) ? t[name.to_sym] : nil
110
+
111
+ raise NebulousError, "Config problem - unknown target #{name}" if x.nil?
112
+ return x
113
+ end
114
+
115
+
116
+ ##
117
+ # Raise an exception if a hash has any keys not found in an exemplar
118
+ #
119
+ # (Private method, only called within Param)
120
+ #
121
+ def validate(exemplar, hash, message)
122
+ hash.each_key do |k|
123
+ raise NebulousError, "#{message} key '#{k}'" unless exemplar.include?(k)
124
+ end
125
+ end
126
+
127
+
128
+ ##
129
+ # reset all parameters -- probably only useful for testing
130
+ #
131
+ def reset
132
+ @params = nil
133
+ @logger = nil
134
+ end
135
+
136
+
137
+ end
138
+ end
139
+
@@ -0,0 +1,103 @@
1
+ # coding: UTF-8
2
+
3
+ require 'redis'
4
+
5
+
6
+ module Nebulous
7
+
8
+
9
+ ##
10
+ # A class to deal with talking to Redis via the Redis gem
11
+ #
12
+ class RedisHandler
13
+
14
+ # This is most likely useless to anything except spec/redis_handler_spec --
15
+ # at least, I hope so...
16
+ attr_reader :redis
17
+
18
+
19
+ ##
20
+ # Initialise an instance of the handler by passing it the connection hash
21
+ #
22
+ # We use the optional testRedis parameter to mock connections to Redis, for
23
+ # testing. It's probably of no use to anyone else.
24
+ #
25
+ def initialize(connectHash, testRedis=nil)
26
+ @redis_hash = connectHash ? connectHash.dup : nil
27
+ @test_redis = testRedis
28
+ @redis = nil
29
+ end
30
+
31
+
32
+ ##
33
+ # Connect to the Redis key/value store. Raise Nebulous error if connection
34
+ # fails.
35
+ #
36
+ def connect
37
+ @redis = @test_redis || Redis.new(@redis_hash.dup)
38
+
39
+ @redis.client.connect
40
+ raise ConnectionError, "Redis Connection failed" unless @redis.connected?
41
+
42
+ self
43
+
44
+ rescue => err
45
+ raise ConnectionError, err.to_s
46
+ end
47
+
48
+
49
+ ##
50
+ # :call-seq:
51
+ # handler.connected? -> (boolean)
52
+ #
53
+ # Call @redis.quit if appropriate; raise nothing if we are not connected etc
54
+ #
55
+ def quit
56
+ @redis.quit if connected?
57
+ @redis = nil
58
+ self
59
+ end
60
+
61
+
62
+ ##
63
+ # return whether we are connected to Redis.
64
+ #
65
+ def connected?
66
+ @redis && @redis.connected?
67
+ end
68
+
69
+
70
+ ##
71
+ # :call-seq:
72
+ # handler.redis_on? -> (boolean)
73
+ #
74
+ # Return whether the Redis is turned "on" in the connect hash, the config
75
+ # file.
76
+ #
77
+ # The rest of nebulous should just let RedisHandler worry about this
78
+ # detail.
79
+ #
80
+ def redis_on?
81
+ @redis_hash && !@redis_hash.empty?
82
+ end
83
+
84
+
85
+
86
+ ##
87
+ # Cover all the other methods on @redis that we are basically forwarding to
88
+ # it. I could use Forwardable here -- except that would not allow us to
89
+ # raise Nebulous::ConnectionError if @redis.nil?
90
+ #
91
+ def method_missing(meth, *args)
92
+ super unless [:set,:get,:del].include?(meth)
93
+
94
+ raise ConnectionError, "Redis not connected, sent #{meth}" \
95
+ unless connected?
96
+
97
+ @redis.__send__(meth, *args)
98
+ end
99
+
100
+ end
101
+
102
+
103
+ end
@@ -0,0 +1,61 @@
1
+ # coding: UTF-8
2
+
3
+ require 'redis'
4
+ require 'json'
5
+
6
+ require_relative 'redis_handler'
7
+
8
+
9
+ module Nebulous
10
+
11
+
12
+ ##
13
+ # Behaves just like RedisHandler, except, does nothing and expects no
14
+ # connection to Redis.
15
+ #
16
+ # This is hopefully useful for testing -- if only for testing of Nebulous.
17
+ #
18
+ class RedisHandlerNull < RedisHandler
19
+
20
+ attr_reader :fake_pair
21
+
22
+
23
+ def initialize(connectHash={})
24
+ super
25
+ @fake_pair = {}
26
+ end
27
+
28
+
29
+ def insert_fake(key, value)
30
+ @fake_pair = { key => value }
31
+ end
32
+
33
+
34
+ def connect
35
+ @redis = true
36
+ self
37
+ end
38
+
39
+
40
+ def quit
41
+ @redis = nil
42
+ self
43
+ end
44
+
45
+
46
+ def connected?
47
+ @fake_pair != {}
48
+ end
49
+
50
+
51
+ def set(key, value, hash=nil); insert_fake(key, value); end
52
+
53
+ def del(key); @fake_pair = {}; end
54
+
55
+ def get(key); @fake_pair.values.first; end
56
+
57
+
58
+ end
59
+
60
+
61
+ end