rightscale-nanite 0.4.1 → 0.4.1.1
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/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
|