rightscale-nanite 0.4.1 → 0.4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/nanite.rb +71 -0
- data/lib/nanite/actor.rb +60 -0
- data/lib/nanite/actor_registry.rb +24 -0
- data/lib/nanite/admin.rb +153 -0
- data/lib/nanite/agent.rb +250 -0
- data/lib/nanite/amqp.rb +47 -0
- data/lib/nanite/cluster.rb +203 -0
- data/lib/nanite/config.rb +102 -0
- data/lib/nanite/console.rb +39 -0
- data/lib/nanite/daemonize.rb +13 -0
- data/lib/nanite/dispatcher.rb +90 -0
- data/lib/nanite/identity.rb +16 -0
- data/lib/nanite/job.rb +104 -0
- data/lib/nanite/local_state.rb +34 -0
- data/lib/nanite/log.rb +64 -0
- data/lib/nanite/log/formatter.rb +39 -0
- data/lib/nanite/mapper.rb +277 -0
- data/lib/nanite/mapper_proxy.rb +56 -0
- data/lib/nanite/packets.rb +231 -0
- data/lib/nanite/pid_file.rb +52 -0
- data/lib/nanite/reaper.rb +38 -0
- data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
- data/lib/nanite/security/certificate.rb +55 -0
- data/lib/nanite/security/certificate_cache.rb +66 -0
- data/lib/nanite/security/distinguished_name.rb +34 -0
- data/lib/nanite/security/encrypted_document.rb +46 -0
- data/lib/nanite/security/rsa_key_pair.rb +53 -0
- data/lib/nanite/security/secure_serializer.rb +67 -0
- data/lib/nanite/security/signature.rb +40 -0
- data/lib/nanite/security/static_certificate_store.rb +35 -0
- data/lib/nanite/security_provider.rb +47 -0
- data/lib/nanite/serializer.rb +52 -0
- data/lib/nanite/state.rb +164 -0
- data/lib/nanite/streaming.rb +125 -0
- data/lib/nanite/util.rb +51 -0
- data/spec/actor_registry_spec.rb +62 -0
- data/spec/actor_spec.rb +59 -0
- data/spec/agent_spec.rb +235 -0
- data/spec/cached_certificate_store_proxy_spec.rb +34 -0
- data/spec/certificate_cache_spec.rb +49 -0
- data/spec/certificate_spec.rb +27 -0
- data/spec/cluster_spec.rb +300 -0
- data/spec/dispatcher_spec.rb +136 -0
- data/spec/distinguished_name_spec.rb +24 -0
- data/spec/encrypted_document_spec.rb +21 -0
- data/spec/job_spec.rb +219 -0
- data/spec/local_state_spec.rb +112 -0
- data/spec/packet_spec.rb +218 -0
- data/spec/rsa_key_pair_spec.rb +33 -0
- data/spec/secure_serializer_spec.rb +41 -0
- data/spec/serializer_spec.rb +107 -0
- data/spec/signature_spec.rb +30 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/static_certificate_store_spec.rb +30 -0
- data/spec/util_spec.rb +63 -0
- metadata +62 -1
data/lib/nanite/amqp.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
class MQ
|
2
|
+
class Queue
|
3
|
+
# Asks the broker to redeliver all unacknowledged messages on a
|
4
|
+
# specifieid channel. Zero or more messages may be redelivered.
|
5
|
+
#
|
6
|
+
# * requeue (default false)
|
7
|
+
# If this parameter is false, the message will be redelivered to the original recipient.
|
8
|
+
# If this flag is true, the server will attempt to requeue the message, potentially then
|
9
|
+
# delivering it to an alternative subscriber.
|
10
|
+
#
|
11
|
+
def recover requeue = false
|
12
|
+
@mq.callback{
|
13
|
+
@mq.send Protocol::Basic::Recover.new({ :requeue => requeue })
|
14
|
+
}
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# monkey patch to the amqp gem that adds :no_declare => true option for new
|
21
|
+
# Exchange objects. This allows us to send messeages to exchanges that are
|
22
|
+
# declared by the mappers and that we have no configuration priviledges on.
|
23
|
+
# temporary until we get this into amqp proper
|
24
|
+
MQ::Exchange.class_eval do
|
25
|
+
def initialize mq, type, name, opts = {}
|
26
|
+
@mq = mq
|
27
|
+
@type, @name, @opts = type, name, opts
|
28
|
+
@mq.exchanges[@name = name] ||= self
|
29
|
+
@key = opts[:key]
|
30
|
+
|
31
|
+
@mq.callback{
|
32
|
+
@mq.send AMQP::Protocol::Exchange::Declare.new({ :exchange => name,
|
33
|
+
:type => type,
|
34
|
+
:nowait => true }.merge(opts))
|
35
|
+
} unless name == "amq.#{type}" or name == '' or opts[:no_declare]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Nanite
|
40
|
+
module AMQPHelper
|
41
|
+
def start_amqp(options)
|
42
|
+
connection = AMQP.connect(:user => options[:user], :pass => options[:pass], :vhost => options[:vhost],
|
43
|
+
:host => options[:host], :port => (options[:port] || ::AMQP::PORT).to_i, :insist => options[:insist] || false)
|
44
|
+
MQ.new(connection)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module Nanite
|
2
|
+
class Cluster
|
3
|
+
attr_reader :agent_timeout, :nanites, :reaper, :serializer, :identity, :amq, :redis, :mapper
|
4
|
+
|
5
|
+
def initialize(amq, agent_timeout, identity, serializer, mapper, redis=nil)
|
6
|
+
@amq = amq
|
7
|
+
@agent_timeout = agent_timeout
|
8
|
+
@identity = identity
|
9
|
+
@serializer = serializer
|
10
|
+
@mapper = mapper
|
11
|
+
@redis = redis
|
12
|
+
@security = SecurityProvider.get
|
13
|
+
if redis
|
14
|
+
Nanite::Log.info("using redis for state storage")
|
15
|
+
require 'nanite/state'
|
16
|
+
@nanites = ::Nanite::State.new(redis)
|
17
|
+
else
|
18
|
+
require 'nanite/local_state'
|
19
|
+
@nanites = Nanite::LocalState.new
|
20
|
+
end
|
21
|
+
@reaper = Reaper.new(agent_timeout)
|
22
|
+
setup_queues
|
23
|
+
end
|
24
|
+
|
25
|
+
# determine which nanites should receive the given request
|
26
|
+
def targets_for(request)
|
27
|
+
return [request.target] if request.target
|
28
|
+
__send__(request.selector, request.type, request.tags).collect {|name, state| name }
|
29
|
+
end
|
30
|
+
|
31
|
+
# adds nanite to nanites map: key is nanite's identity
|
32
|
+
# and value is a services/status pair implemented
|
33
|
+
# as a hash
|
34
|
+
def register(reg)
|
35
|
+
case reg
|
36
|
+
when Register
|
37
|
+
if @security.authorize_registration(reg)
|
38
|
+
nanites[reg.identity] = { :services => reg.services, :status => reg.status, :tags => reg.tags }
|
39
|
+
reaper.timeout(reg.identity, agent_timeout + 1) { nanites.delete(reg.identity) }
|
40
|
+
Nanite::Log.info("registered: #{reg.identity}, #{nanites[reg.identity].inspect}")
|
41
|
+
else
|
42
|
+
Nanite::Log.warning("registration of #{reg.inspect} not authorized")
|
43
|
+
end
|
44
|
+
when UnRegister
|
45
|
+
nanites.delete(reg.identity)
|
46
|
+
Nanite::Log.info("un-registering: #{reg.identity}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def route(request, targets)
|
51
|
+
EM.next_tick { targets.map { |target| publish(request, target) } }
|
52
|
+
end
|
53
|
+
|
54
|
+
def publish(request, target)
|
55
|
+
# We need to initialize the 'target' field of the request object so that the serializer has
|
56
|
+
# access to it.
|
57
|
+
begin
|
58
|
+
old_target = request.target
|
59
|
+
request.target = target unless target == 'mapper-offline'
|
60
|
+
amq.queue(target).publish(serializer.dump(request), :persistent => request.persistent)
|
61
|
+
ensure
|
62
|
+
request.target = old_target
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
# updates nanite information (last ping timestamps, status)
|
69
|
+
# when heartbeat message is received
|
70
|
+
def handle_ping(ping)
|
71
|
+
if nanite = nanites[ping.identity]
|
72
|
+
nanite[:status] = ping.status
|
73
|
+
reaper.reset_with_autoregister_hack(ping.identity, agent_timeout + 1) { nanites.delete(ping.identity) }
|
74
|
+
else
|
75
|
+
amq.queue(ping.identity).publish(serializer.dump(Advertise.new))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# forward request coming from agent
|
80
|
+
def handle_request(request)
|
81
|
+
if @security.authorize_request(request)
|
82
|
+
result = Result.new(request.token, request.from, nil, mapper.identity)
|
83
|
+
intm_handler = lambda do |res|
|
84
|
+
result.results = res
|
85
|
+
forward_response(result, request.persistent)
|
86
|
+
end
|
87
|
+
ok = mapper.send_request(request, :intermediate_handler => intm_handler) do |res|
|
88
|
+
result.results = res
|
89
|
+
forward_response(result, request.persistent)
|
90
|
+
end
|
91
|
+
if ok == false
|
92
|
+
forward_response(result, request.persistent)
|
93
|
+
end
|
94
|
+
else
|
95
|
+
Nanite::Log.warning("request #{request.inspect} not authorized")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# forward response back to agent that originally made the request
|
100
|
+
def forward_response(res, persistent)
|
101
|
+
amq.queue(res.to).publish(serializer.dump(res), :persistent => persistent)
|
102
|
+
end
|
103
|
+
|
104
|
+
# returns least loaded nanite that provides given service
|
105
|
+
def least_loaded(service, tags=[])
|
106
|
+
candidates = nanites_providing(service,tags)
|
107
|
+
return [] if candidates.empty?
|
108
|
+
|
109
|
+
[candidates.min { |a,b| a[1][:status] <=> b[1][:status] }]
|
110
|
+
end
|
111
|
+
|
112
|
+
# returns all nanites that provide given service
|
113
|
+
def all(service, tags=[])
|
114
|
+
nanites_providing(service,tags)
|
115
|
+
end
|
116
|
+
|
117
|
+
# returns a random nanite
|
118
|
+
def random(service, tags=[])
|
119
|
+
candidates = nanites_providing(service,tags)
|
120
|
+
return [] if candidates.empty?
|
121
|
+
|
122
|
+
[candidates[rand(candidates.size)]]
|
123
|
+
end
|
124
|
+
|
125
|
+
# selects next nanite that provides given service
|
126
|
+
# using round robin rotation
|
127
|
+
def rr(service, tags=[])
|
128
|
+
@last ||= {}
|
129
|
+
@last[service] ||= 0
|
130
|
+
candidates = nanites_providing(service,tags)
|
131
|
+
return [] if candidates.empty?
|
132
|
+
@last[service] = 0 if @last[service] >= candidates.size
|
133
|
+
candidate = candidates[@last[service]]
|
134
|
+
@last[service] += 1
|
135
|
+
[candidate]
|
136
|
+
end
|
137
|
+
|
138
|
+
# returns all nanites that provide the given service
|
139
|
+
def nanites_providing(service, *tags)
|
140
|
+
nanites.nanites_for(service, *tags)
|
141
|
+
end
|
142
|
+
|
143
|
+
def setup_queues
|
144
|
+
setup_heartbeat_queue
|
145
|
+
setup_registration_queue
|
146
|
+
setup_request_queue
|
147
|
+
end
|
148
|
+
|
149
|
+
def setup_heartbeat_queue
|
150
|
+
handler = lambda do |ping|
|
151
|
+
begin
|
152
|
+
ping = serializer.load(ping)
|
153
|
+
Nanite::Log.debug("got heartbeat from #{ping.identity}") if ping.respond_to?(:identity)
|
154
|
+
handle_ping(ping)
|
155
|
+
rescue Exception => e
|
156
|
+
Nanite::Log.error("Error handling heartbeat: #{e.message}")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
hb_fanout = amq.fanout('heartbeat', :durable => true)
|
160
|
+
if @redis
|
161
|
+
amq.queue("heartbeat").bind(hb_fanout).subscribe &handler
|
162
|
+
else
|
163
|
+
amq.queue("heartbeat-#{identity}", :exclusive => true).bind(hb_fanout).subscribe &handler
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def setup_registration_queue
|
168
|
+
handler = lambda do |msg|
|
169
|
+
begin
|
170
|
+
msg = serializer.load(msg)
|
171
|
+
Nanite::Log.debug("got registration from #{msg.identity}")
|
172
|
+
register(msg)
|
173
|
+
rescue Exception => e
|
174
|
+
Nanite::Log.error("Error handling registration: #{e.message}")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
reg_fanout = amq.fanout('registration', :durable => true)
|
178
|
+
if @redis
|
179
|
+
amq.queue("registration").bind(reg_fanout).subscribe &handler
|
180
|
+
else
|
181
|
+
amq.queue("registration-#{identity}", :exclusive => true).bind(reg_fanout).subscribe &handler
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def setup_request_queue
|
186
|
+
handler = lambda do |msg|
|
187
|
+
begin
|
188
|
+
msg = serializer.load(msg)
|
189
|
+
Nanite::Log.debug("got request from #{msg.from} of type #{msg.type}")
|
190
|
+
handle_request(msg)
|
191
|
+
rescue Exception => e
|
192
|
+
Nanite::Log.error("Error handling request: #{e.message}")
|
193
|
+
end
|
194
|
+
end
|
195
|
+
req_fanout = amq.fanout('request', :durable => true)
|
196
|
+
if @redis
|
197
|
+
amq.queue("request").bind(req_fanout).subscribe &handler
|
198
|
+
else
|
199
|
+
amq.queue("request-#{identity}", :exclusive => true).bind(req_fanout).subscribe &handler
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Nanite
|
2
|
+
|
3
|
+
COMMON_DEFAULT_OPTIONS = {:pass => 'testing', :vhost => '/nanite', :secure => false, :host => '0.0.0.0',
|
4
|
+
:log_level => :info, :format => :marshal, :daemonize => false, :console => false, :root => Dir.pwd}
|
5
|
+
|
6
|
+
module CommonConfig
|
7
|
+
def setup_mapper_options(opts, options)
|
8
|
+
setup_common_options(opts, options, 'mapper')
|
9
|
+
|
10
|
+
opts.on("-a", "--agent-timeout TIMEOUT", "How long to wait before an agent is considered to be offline and thus removed from the list of available agents.") do |timeout|
|
11
|
+
options[:agent_timeout] = timeout
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on("-r", "--offline-redelivery-frequency", "The frequency in seconds that messages stored in the offline queue will be retrieved for attempted redelivery to the nanites. Default is 10 seconds.") do |frequency|
|
15
|
+
options[:offline_redelivery_frequency] = frequency
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on("--persistent", "Instructs the AMQP broker to save messages to persistent storage so that they aren't lost when the broker is restarted. Can be overriden on a per-message basis using the request and push methods.") do
|
19
|
+
options[:persistent] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("--offline-failsafe", "Store messages in an offline queue when all the nanites are offline. Messages will be redelivered when nanites come online. Can be overriden on a per-message basis using the request methods.") do
|
23
|
+
options[:offline_failsafe] = true
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("--redis HOST_PORT", "Use redis as the agent state storage in the mapper: --redis 127.0.0.1:6379; missing host and/or port will be filled with defaults if colon is present") do |redis|
|
27
|
+
redishost, redisport = redis.split(':')
|
28
|
+
redishost = '127.0.0.1' if (redishost.nil? || redishost.empty?)
|
29
|
+
redisport = '6379' if (redishost.nil? || redishost.empty?)
|
30
|
+
redis = "#{redishost}:#{redisport}"
|
31
|
+
options[:redis] = redis
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def setup_common_options(opts, options, type)
|
37
|
+
opts.version = Nanite::VERSION
|
38
|
+
|
39
|
+
opts.on("-i", "--irb-console", "Start #{type} in irb console mode.") do |console|
|
40
|
+
options[:console] = 'irb'
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("-u", "--user USER", "Specify the rabbitmq username.") do |user|
|
44
|
+
options[:user] = user
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("-h", "--host HOST", "Specify the rabbitmq hostname.") do |host|
|
48
|
+
options[:host] = host
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("-P", "--port PORT", "Specify the rabbitmq PORT, default 5672.") do |port|
|
52
|
+
options[:port] = port
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on("-p", "--pass PASSWORD", "Specify the rabbitmq password") do |pass|
|
56
|
+
options[:pass] = pass
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on("-t", "--token IDENITY", "Specify the #{type} identity.") do |ident|
|
60
|
+
options[:identity] = ident
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on("-v", "--vhost VHOST", "Specify the rabbitmq vhost") do |vhost|
|
64
|
+
options[:vhost] = vhost
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on("-s", "--secure", "Use Security features of rabbitmq to restrict nanites to themselves") do
|
68
|
+
options[:secure] = true
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on("-f", "--format FORMAT", "The serialization type to use for transfering data. Can be marshal, json or yaml. Default is marshal") do |fmt|
|
72
|
+
options[:format] = fmt.to_sym
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on("-d", "--daemonize", "Run #{type} as a daemon") do |d|
|
76
|
+
options[:daemonize] = true
|
77
|
+
end
|
78
|
+
|
79
|
+
opts.on("--pid-dir PATH", "Specify the pid path, only used with daemonize") do |dir|
|
80
|
+
options[:pid_dir] = dir
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.on("-l", "--log-level LEVEL", "Specify the log level (fatal, error, warn, info, debug). Default is info") do |level|
|
84
|
+
options[:log_level] = level
|
85
|
+
end
|
86
|
+
|
87
|
+
opts.on("--log-dir PATH", "Specify the log path") do |dir|
|
88
|
+
options[:log_dir] = dir
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on("--tag TAG", "Specify a tag. Can issue multiple times.") do |tag|
|
92
|
+
options[:tag] ||= []
|
93
|
+
options[:tag] << tag
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on("--version", "Show the nanite version number") do |res|
|
97
|
+
puts "Nanite Version #{opts.version}"
|
98
|
+
exit
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Nanite
|
2
|
+
module ConsoleHelper
|
3
|
+
def self.included(base)
|
4
|
+
@@base = base
|
5
|
+
end
|
6
|
+
|
7
|
+
def start_console
|
8
|
+
puts "Starting #{@@base.name.split(":").last.downcase} console (#{self.identity}) (Nanite #{Nanite::VERSION})"
|
9
|
+
Thread.new do
|
10
|
+
Console.start(self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Console
|
16
|
+
class << self; attr_accessor :instance; end
|
17
|
+
|
18
|
+
def self.start(binding)
|
19
|
+
require 'irb'
|
20
|
+
old_args = ARGV.dup
|
21
|
+
ARGV.replace ["--simple-prompt"]
|
22
|
+
|
23
|
+
IRB.setup(nil)
|
24
|
+
self.instance = IRB::Irb.new(IRB::WorkSpace.new(binding))
|
25
|
+
|
26
|
+
@CONF = IRB.instance_variable_get(:@CONF)
|
27
|
+
@CONF[:IRB_RC].call self.instance.context if @CONF[:IRB_RC]
|
28
|
+
@CONF[:MAIN_CONTEXT] = self.instance.context
|
29
|
+
|
30
|
+
catch(:IRB_EXIT) { self.instance.eval_input }
|
31
|
+
ensure
|
32
|
+
ARGV.replace old_args
|
33
|
+
# Clean up tty settings in some evil, evil cases
|
34
|
+
begin; catch(:IRB_EXIT) { irb_exit }; rescue Exception; end
|
35
|
+
# Make nanite exit when irb does
|
36
|
+
EM.stop if EM.reactor_running?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Nanite
|
2
|
+
class Dispatcher
|
3
|
+
attr_reader :registry, :serializer, :identity, :amq, :options
|
4
|
+
attr_accessor :evmclass
|
5
|
+
|
6
|
+
def initialize(amq, registry, serializer, identity, options)
|
7
|
+
@amq = amq
|
8
|
+
@registry = registry
|
9
|
+
@serializer = serializer
|
10
|
+
@identity = identity
|
11
|
+
@options = options
|
12
|
+
@evmclass = EM
|
13
|
+
end
|
14
|
+
|
15
|
+
def dispatch(deliverable)
|
16
|
+
prefix, meth = deliverable.type.split('/')[1..-1]
|
17
|
+
meth ||= :index
|
18
|
+
actor = registry.actor_for(prefix)
|
19
|
+
|
20
|
+
operation = lambda do
|
21
|
+
begin
|
22
|
+
intermediate_results_proc = lambda { |*args| self.handle_intermediate_results(actor, meth, deliverable, *args) }
|
23
|
+
args = [ deliverable.payload ]
|
24
|
+
args.push(deliverable) if actor.method(meth).arity == 2
|
25
|
+
actor.send(meth, *args, &intermediate_results_proc)
|
26
|
+
rescue Exception => e
|
27
|
+
handle_exception(actor, meth, deliverable, e)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
callback = lambda do |r|
|
32
|
+
if deliverable.kind_of?(Request)
|
33
|
+
r = Result.new(deliverable.token, deliverable.reply_to, r, identity)
|
34
|
+
amq.queue(deliverable.reply_to, :no_declare => options[:secure]).publish(serializer.dump(r))
|
35
|
+
end
|
36
|
+
r # For unit tests
|
37
|
+
end
|
38
|
+
|
39
|
+
if @options[:single_threaded]
|
40
|
+
@evmclass.next_tick { callback.call(operation.call) }
|
41
|
+
else
|
42
|
+
@evmclass.defer(operation, callback)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def handle_intermediate_results(actor, meth, deliverable, *args)
|
49
|
+
messagekey = case args.size
|
50
|
+
when 1
|
51
|
+
'defaultkey'
|
52
|
+
when 2
|
53
|
+
args.first.to_s
|
54
|
+
else
|
55
|
+
raise ArgumentError, "handle_intermediate_results passed unexpected number of arguments (#{args.size})"
|
56
|
+
end
|
57
|
+
message = args.last
|
58
|
+
@evmclass.defer(lambda {
|
59
|
+
[deliverable.reply_to, IntermediateMessage.new(deliverable.token, deliverable.reply_to, identity, messagekey, message)]
|
60
|
+
}, lambda { |r|
|
61
|
+
amq.queue(r.first, :no_declare => options[:secure]).publish(serializer.dump(r.last))
|
62
|
+
})
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def describe_error(e)
|
68
|
+
"#{e.class.name}: #{e.message}\n #{e.backtrace.join("\n ")}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_exception(actor, meth, deliverable, e)
|
72
|
+
error = describe_error(e)
|
73
|
+
Nanite::Log.error(error)
|
74
|
+
begin
|
75
|
+
if actor.class.exception_callback
|
76
|
+
case actor.class.exception_callback
|
77
|
+
when Symbol, String
|
78
|
+
actor.send(actor.class.exception_callback, meth.to_sym, deliverable, e)
|
79
|
+
when Proc
|
80
|
+
actor.instance_exec(meth.to_sym, deliverable, e, &actor.class.exception_callback)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
rescue Exception => e1
|
84
|
+
error = describe_error(e1)
|
85
|
+
Nanite::Log.error(error)
|
86
|
+
end
|
87
|
+
error
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|