asir 1.1.12 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -8,4 +8,5 @@ example/*.out
8
8
  coverage
9
9
  tmp.*
10
10
  cabar.yml
11
+ .rbx
11
12
 
data/ChangeLog CHANGED
@@ -1,5 +1,12 @@
1
- 2012-12-28 Kurt A. Stephens <ks.github@kurtstephens.com>
1
+ 2012-12-29 Kurt A. Stephens <ks.github@kurtstephens.com>
2
+
3
+ * v1.2.0: New version: API changes.
4
+ * Message::State: internal object encapsulates state between #send_message, #receive_message, #send_result, #receive_result.
5
+ * Callbacks: most callbacks take a MessageResult object instead of Message and/or Result objects.
6
+ * UUID: Added new_uuid, process_uuid, counter_uuid, thread_uuid as mixin methods.
7
+ * Identity: Removed deprecated #client, #server attributes, use additional_data.
2
8
 
9
+ 2012-12-28 Kurt A. Stephens <ks.github@kurtstephens.com>
3
10
  * v1.1.12: New version.
4
11
  * Rubinius: Rubinius -Xversion=18 and =19 support.
5
12
  * XML: Really removed Coder::XML.
data/example/ex18.rb CHANGED
@@ -9,8 +9,9 @@ begin
9
9
  :encoder => ASIR::Coder::Yaml.new(:yaml_options => { :ASCII_8BIT_ok => true }))
10
10
  tcp = ASIR::Transport::TcpSocket.new(:port => 31918,
11
11
  :encoder => ASIR::Coder::Marshal.new)
12
- start_server_proc = lambda do | transport, message |
13
- $stderr.puts "message = #{message.inspect}"
12
+ start_server_proc = lambda do | transport, message_result |
13
+ message = message_result.message
14
+ # $stderr.puts "message = #{message.inspect}"
14
15
  file.send_message(message)
15
16
  server_process do
16
17
  tcp.prepare_server!
data/example/ex26.rb CHANGED
@@ -7,7 +7,7 @@ begin
7
7
  Email.asir.transport = t =
8
8
  ASIR::Transport::Thread.new
9
9
  spawned_thread = nil
10
- t.after_thread_new = lambda do | transport, message, thread |
10
+ t.after_thread_new = lambda do | transport, message_result, thread |
11
11
  spawned_thread = thread
12
12
  $stderr.puts "\n #{$$}: Spawned Thread #{thread.inspect}"
13
13
  end
@@ -48,7 +48,12 @@ def server_process &blk
48
48
  if ENV['ASIR_JRUBY_SPAWNED']
49
49
  $stderr.puts " spawned server at #{__FILE__}:#{__LINE__}"
50
50
  puts "*** #{$$}: server process"; $stdout.flush
51
- yield
51
+ begin
52
+ yield
53
+ rescue ::Exception => exc
54
+ $stderr.puts "*** #{$$}: service ERROR: #{exc.inspect}\n #{exc.backtrace * " \n"}"
55
+ raise exc
56
+ end
52
57
  Process.exit!(0)
53
58
  # dont do client, client is our parent process.
54
59
  else
@@ -64,7 +69,12 @@ def server_process &blk
64
69
  # $stderr.puts " at #{__FILE__}:#{__LINE__}"
65
70
  $server_pid = Process.fork do
66
71
  puts "*** #{$$}: server process"; $stdout.flush
67
- yield
72
+ begin
73
+ yield
74
+ rescue ::Exception => exc
75
+ $stderr.puts "*** #{$$}: service ERROR: #{exc.inspect}\n #{exc.backtrace * " \n"}"
76
+ raise exc
77
+ end
68
78
  end
69
79
  end
70
80
  sleep 1 # wait for server to be ready.
data/lib/asir.rb CHANGED
@@ -242,6 +242,7 @@ require 'asir/code_block'
242
242
  require 'asir/code_more'
243
243
  require 'asir/message'
244
244
  require 'asir/result'
245
+ require 'asir/message/state'
245
246
  require 'asir/client'
246
247
  require 'asir/coder'
247
248
  require 'asir/coder/null'
@@ -28,19 +28,19 @@ module ASIR
28
28
 
29
29
  module ModuleMethods
30
30
  # Provide a getter method that delegates to addtional_data[...].
31
- def addr_getter *names
31
+ def addit_getter *names
32
32
  names.each do | name |
33
33
  name = name.to_sym
34
- define_method(name) { | | addtional_data[name] }
34
+ define_method(name) { | | self[name] }
35
35
  end
36
36
  end
37
37
 
38
38
  # Provide getter and setter methods that delegate to addtional_data[...].
39
- def addr_accessor *names
39
+ def addit_accessor *names
40
40
  addr_getter *names
41
41
  names.each do | name |
42
42
  name = name.to_sym
43
- define_method(:"#{name}=") { | v | addtional_data[name] = v }
43
+ define_method(:"#{name}=") { | v | self[name] = v }
44
44
  end
45
45
  end
46
46
  end
data/lib/asir/client.rb CHANGED
@@ -54,8 +54,7 @@ module ASIR
54
54
  message = Message.new(@receiver, selector, arguments, block, self)
55
55
  message = @before_send_message.call(message) if @before_send_message
56
56
  @__configure.call(message, self) if @__configure
57
- result = transport.send_message(message)
58
- result
57
+ transport.send_message(message) # => result
59
58
  end
60
59
  # Accept all other messages to be encoded and transported to a service.
61
60
  alias :method_missing :send
data/lib/asir/identity.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'asir/uuid'
2
- require 'thread' # Mutex
3
2
 
4
3
  module ASIR
5
4
  # !SLIDE
@@ -8,29 +7,14 @@ module ASIR
8
7
  module Identity
9
8
  attr_accessor :identifier, :timestamp
10
9
 
11
- # Optional: Opaque data about the Client that created the Message.
12
- attr_accessor :client
13
-
14
- # Optional: Opaque data about the Service that handled the Result.
15
- attr_accessor :server
16
-
17
10
  # Creates a thread-safe unique identifier.
18
11
  def create_identifier!
19
- @identifier ||=
20
- @@identifier_mutex.synchronize do
21
- if @@uuid_pid != $$
22
- @@uuid_pid = $$
23
- @@uuid = nil
24
- end
25
- "#{@@counter += 1}-#{@@uuid ||= ::ASIR::UUID.generate}".freeze
26
- end
12
+ @identifier ||= ::ASIR::UUID.counter_uuid
27
13
  end
28
- @@counter ||= 0; @@uuid ||= nil; @@uuid_pid = nil; @@identifier_mutex ||= Mutex.new
29
14
 
30
15
  # Creates a timestamp.
31
16
  def create_timestamp!
32
- @timestamp ||=
33
- ::Time.now.gmtime
17
+ @timestamp ||= ::Time.now.gmtime
34
18
  end
35
19
  end
36
20
  end
@@ -0,0 +1,18 @@
1
+ module ASIR
2
+ class Message
3
+ # !SLIDE
4
+ # Message::State
5
+ #
6
+ # Encapsulate the Message, Result and their payloads and other state.
7
+ # This is passed between Transport#send_request, #receive_request, #send_response, #receive_response.
8
+ class State
9
+ include Initialization
10
+ attr_accessor :message, :result
11
+ attr_accessor :message_payload, :result_payload
12
+ attr_accessor :message_opaque, :result_opaque
13
+ attr_accessor :in_stream, :out_stream
14
+ attr_accessor :additional_data
15
+ end
16
+ end
17
+ end
18
+
@@ -1,6 +1,7 @@
1
1
  require 'time'
2
2
  require 'asir/thread_variable'
3
3
  require 'asir/message/delay'
4
+ require 'asir/message/state'
4
5
  require 'asir/transport/conduit'
5
6
 
6
7
  module ASIR
@@ -23,46 +24,44 @@ module ASIR
23
24
  # * Send encoded Message.
24
25
  # * Receive decoded Result.
25
26
  def send_message message
26
- @message_count ||= 0; @message_count += 1
27
+ @message_count ||= 0; @message_count += 1 # NOT THREAD-SAFE
27
28
  message.create_timestamp! if needs_message_timestamp? message
28
29
  message.create_identifier! if needs_message_identifier? message
29
- @before_send_message.call(self, message) if @before_send_message
30
30
  relative_message_delay! message
31
- message_payload = encoder.prepare.encode(message)
32
- opaque_result = _send_message(message, message_payload)
33
- receive_result(message, opaque_result)
31
+ state = Message::State.new(:message => message, :message_payload => encoder.prepare.encode(message))
32
+ @before_send_message.call(self, state) if @before_send_message
33
+ _send_message(state)
34
+ receive_result(state)
34
35
  end
35
36
 
36
37
  # !SLIDE
37
38
  # Transport#receive_message
38
39
  # Receive Message payload from stream.
39
- def receive_message stream
40
- # $stderr.puts " #{$$} #{self} receive_message #{stream}"
41
- @message_count ||= 0; @message_count += 1
42
- additional_data = { }
43
- if req_and_state = _receive_message(stream, additional_data)
44
- message = req_and_state[0] = encoder.prepare.decode(req_and_state.first)
45
- message.additional_data!.update(additional_data) if message
46
- if @after_receive_message
47
- @after_receive_message.call(self, message)
40
+ def receive_message state
41
+ @message_count ||= 0; @message_count += 1 # NOT THREAD-SAFE
42
+ if received = _receive_message(state)
43
+ if message = state.message = encoder.prepare.decode(state.message_payload)
44
+ message.additional_data!.update(state.additional_data) if state.additional_data
45
+ @after_receive_message.call(self, state) if @after_receive_message
46
+ self
48
47
  end
49
48
  end
50
- req_and_state
51
49
  end
52
50
  # !SLIDE END
53
51
 
54
52
  # !SLIDE
55
53
  # Transport#send_result
56
54
  # Send Result to stream.
57
- def send_result result, stream, message_state
58
- message = result.message
55
+ def send_result state
56
+ result = state.result
57
+ message = state.message
59
58
  if @one_way && message.block
60
59
  message.block.call(result)
61
60
  else
62
- # avoid sending back entire Message.
61
+ # Avoid sending back entire Message in Result.
63
62
  result.message = nil unless @coder_needs_result_message
64
- result_payload = decoder.prepare.encode(result)
65
- _send_result(message, result, result_payload, stream, message_state)
63
+ state.result_payload = decoder.prepare.encode(result)
64
+ _send_result(state)
66
65
  end
67
66
  end
68
67
  attr_accessor :coder_needs_result_message
@@ -75,19 +74,24 @@ module ASIR
75
74
  # * Receive Result payload
76
75
  # * Decode Result.
77
76
  # * Extract Result result or exception.
78
- def receive_result message, opaque_result
79
- result_payload = _receive_result(message, opaque_result)
80
- result = decoder.prepare.decode(result_payload)
77
+ # * Invoke Exception or return Result value.
78
+ def receive_result state
79
+ value = nil
80
+ return value unless _receive_result(state)
81
+ result = state.result ||= decoder.prepare.decode(state.result_payload)
82
+ message = state.message
81
83
  if result && ! message.one_way
84
+ result.message = message
82
85
  if exc = result.exception
83
86
  invoker.invoke!(exc, self)
84
87
  else
85
88
  if ! @one_way && message.block
86
89
  message.block.call(result)
87
90
  end
88
- result.result
91
+ value = result.result
89
92
  end
90
93
  end
94
+ value
91
95
  end
92
96
  # !SLIDE END
93
97
 
@@ -118,8 +122,8 @@ module ASIR
118
122
  # Proc to call with exception, if exception occurs within #serve_message!, but outside
119
123
  # Message#invoke!.
120
124
  #
121
- # trans.on_exception.call(trans, exception, :message, Message_instance, nil)
122
- # trans.on_exception.call(trans, exception, :result, Message_instance, Result_instance)
125
+ # trans.on_exception.call(trans, exception, :message, message_state)
126
+ # trans.on_exception.call(trans, exception, :result, message_state)
123
127
  attr_accessor :on_exception
124
128
 
125
129
  attr_accessor :needs_message_identifier, :needs_message_timestamp
@@ -139,15 +143,15 @@ module ASIR
139
143
  # !SLIDE
140
144
  # Serve a Message.
141
145
  def serve_message! in_stream, out_stream
142
- message = message_state = message_ok = result = result_ok = nil
146
+ state = message_ok = result = result_ok = nil
143
147
  exception = original_exception = unforwardable_exception = nil
144
- message, message_state = receive_message(in_stream)
145
- if message
148
+ state = Message::State.new(:in_stream => in_stream, :out_stream => out_stream)
149
+ if receive_message(state)
146
150
  message_ok = true
147
- result = invoke_message!(message)
151
+ invoke_message!(state)
148
152
  result_ok = true
149
153
  if @after_invoke_message
150
- @after_invoke_message.call(self, message, result)
154
+ @after_invoke_message.call(self, state)
151
155
  end
152
156
  self
153
157
  else
@@ -156,7 +160,7 @@ module ASIR
156
160
  rescue ::Exception => exc
157
161
  exception = original_exception = exc
158
162
  _log [ :message_error, exc ]
159
- @on_exception.call(self, exc, :message, message, nil) if @on_exception
163
+ @on_exception.call(self, exc, :message, state) if @on_exception
160
164
  ensure
161
165
  begin
162
166
  if message_ok
@@ -165,15 +169,15 @@ module ASIR
165
169
  when *Error::Unforwardable.unforwardable
166
170
  unforwardable_exception = exception = Error::Unforwardable.new(exception)
167
171
  end
168
- result = Result.new(message, nil, exception)
172
+ state.result = Result.new(state.message, nil, exception)
169
173
  end
170
174
  if out_stream
171
- send_result(result, out_stream, message_state)
175
+ send_result(state)
172
176
  end
173
177
  end
174
178
  rescue ::Exception => exc
175
- _log [ :result_error, exc ]
176
- @on_exception.call(self, exc, :result, message, result) if @on_exception
179
+ _log [ :result_error, exc, exc.backtrace ]
180
+ @on_exception.call(self, exc, :result, state) if @on_exception
177
181
  end
178
182
  raise original_exception if unforwardable_exception
179
183
  end
@@ -227,21 +231,25 @@ module ASIR
227
231
  end
228
232
 
229
233
  # Invokes the Message object, returns a Result object.
230
- def invoke_message! message
231
- result = nil
234
+ def invoke_message! state
232
235
  Transport.with_attr! :current, self do
233
- with_attr! :message, message do
234
- wait_for_delay! message
235
- result = invoker.invoke!(message, self)
236
+ with_attr! :message_state, state do
237
+ with_attr! :message, state.message do
238
+ wait_for_delay! state.message
239
+ state.result = invoker.invoke!(state.message, self)
236
240
  # Hook for Exceptions.
237
- if @on_result_exception && result.exception
238
- @on_result_exception.call(self, result)
241
+ if @on_result_exception && state.result.exception
242
+ @on_result_exception.call(self, state)
239
243
  end
240
244
  end
241
245
  end
242
- result
246
+ end
247
+ self
243
248
  end
244
- # The current Message being handled.
249
+
250
+ # The current Message::State.
251
+ attr_accessor_thread :message_state
252
+ # The current Message being invoked. DEPRECATED.
245
253
  attr_accessor_thread :message
246
254
 
247
255
  # The current active Transport.
@@ -9,23 +9,22 @@ module ASIR
9
9
  class Broadcast < self
10
10
  include Composite
11
11
 
12
- def _send_message message, message_payload
12
+ def _send_message state
13
13
  result = first_exception = nil
14
14
  transports.each do | transport |
15
15
  begin
16
- result = transport.send_message(message)
16
+ result = transport.send_message(state.message)
17
17
  rescue ::Exception => exc
18
18
  first_exception ||= exc
19
- _handle_send_message_exception! transport, message, exc
19
+ _handle_send_message_exception! transport, state, exc
20
20
  raise exc unless @continue_on_exception
21
21
  end
22
22
  end
23
23
  if first_exception && @reraise_first_exception
24
24
  raise first_exception
25
25
  end
26
- result
26
+ state.result = Result.new(state.message, result)
27
27
  end
28
-
29
28
  end
30
29
  # !SLIDE END
31
30
  end
@@ -25,15 +25,15 @@ module ASIR
25
25
 
26
26
  # If paused, queue messages,
27
27
  # Otherwise delegate immediately to #transport.
28
- def _send_message message, message_payload
28
+ def _send_message state
29
29
  return nil if @ignore
30
30
  if paused?
31
31
  @messages_mutex.synchronize do
32
- @messages << message
32
+ @messages << state.message
33
33
  end
34
34
  nil
35
35
  else
36
- @transport.send_message(message)
36
+ @transport.send_message(state.message)
37
37
  end
38
38
  end
39
39
 
@@ -97,7 +97,7 @@ module ASIR
97
97
  # Usually used in worker Thread.
98
98
  def process! non_block=false
99
99
  @running = true
100
- while @running && message = shift(non_block)
100
+ while @running && (message = shift(non_block))
101
101
  @transport.send_message(message)
102
102
  end
103
103
  message
@@ -4,7 +4,7 @@ module ASIR
4
4
  # !SLIDE
5
5
  # A Transport composed of other Transports.
6
6
  #
7
- # Classes that mix this in should define #_send_message(message, message_payload).
7
+ # Classes that mix this in should define #_send_message.
8
8
  module Composite
9
9
  include Delegation
10
10
 
@@ -5,10 +5,11 @@ module Asir
5
5
  # Conduit service support.
6
6
  module Conduit
7
7
  attr_accessor :conduit_options, :conduit_pid
8
+
8
9
  def start_conduit! options = nil
9
- opts = { :fork => true }
10
+ opts = @conduit_options ||= {}
11
+ opts.update(:fork => true)
10
12
  opts.update(options) if options
11
- @conduit_options = opts
12
13
  _log { "start_conduit! #{self}" } if @verbose >= 1
13
14
  in_fork = opts[:fork]
14
15
  raise "already running #{@conduit_pid} #{@conduit_cmd}" if @conduit_pid
@@ -19,7 +20,7 @@ module Asir
19
20
  raise "Could not exec"
20
21
  end
21
22
  _log { "start_conduit! #{self} started pid=#{@conduit_pid.inspect}" } if @verbose >= 2
22
- if pid_file = @conduit_options[:pid_file]
23
+ if pid_file = (@conduit_options || EMPTY_HASH)[:pid_file]
23
24
  File.open(pid_file, "w") { | fh | fh.puts @conduit_pid }
24
25
  end
25
26
  else
@@ -29,7 +30,7 @@ module Asir
29
30
  end
30
31
 
31
32
  def conduit_pid
32
- if ! @conduit_pid and pid_file = @conduit_options[:pid_file]
33
+ if ! @conduit_pid and pid_file = (@conduit_options || EMPTY_HASH)[:pid_file]
33
34
  @conduit_pid = (File.read(pid_file).to_i rescue nil)
34
35
  end
35
36
  @conduit_pid
@@ -37,10 +38,11 @@ module Asir
37
38
 
38
39
  def stop_conduit! opts = nil
39
40
  if conduit_pid
40
- pid_file = @conduit_options[:pid_file]
41
41
  _log { "stop_conduit! #{self} pid=#{@conduit_pid.inspect}" } if @verbose >= 1
42
42
  ::Process.kill( (opts && opts[:signal]) || 'TERM', @conduit_pid)
43
- ::File.unlink(pid_file) rescue nil if pid_file
43
+ if pid_file = (@conduit_options || EMPTY_HASH)[:pid_file]
44
+ ::File.unlink(pid_file) rescue nil
45
+ end
44
46
  ::Process.waitpid @conduit_pid
45
47
  end
46
48
  self
@@ -52,33 +52,35 @@ module ASIR
52
52
 
53
53
  # !SLIDE
54
54
  # Sends the encoded Message payload String.
55
- def _send_message message, message_payload
55
+ def _send_message state
56
56
  stream.with_stream! do | io |
57
- _write message_payload, io, message
57
+ state.in_stream = io
58
+ _write(state.message_payload, io, state)
58
59
  end
59
60
  end
60
61
 
61
62
  # !SLIDE
62
63
  # Receives the encoded Message payload String.
63
- def _receive_message stream, additional_data
64
- [ _read(stream, nil), nil ]
64
+ def _receive_message state
65
+ state.message_payload = _read(state.in_stream, state)
65
66
  end
66
67
 
67
68
  # !SLIDE
68
69
  # Sends the encoded Result payload String.
69
- def _send_result message, result, result_payload, stream, message_state
70
- unless @one_way || message.one_way
71
- _write result_payload, stream, message
70
+ def _send_result state
71
+ unless @one_way || state.message.one_way
72
+ # $stderr.write "\n _send_result #{state.result_payload.inspect}\n\n"
73
+ _write(state.result_payload, state.out_stream, state)
74
+ true
72
75
  end
73
76
  end
74
77
 
75
78
  # !SLIDE
76
79
  # Receives the encoded Result payload String.
77
- def _receive_result message, opaque_result
78
- unless @one_way || message.one_way
79
- stream.with_stream! do | io |
80
- _read io, message
81
- end
80
+ def _receive_result state
81
+ unless @one_way || state.message.one_way
82
+ state.result_payload = _read(state.in_stream, state)
83
+ true
82
84
  end
83
85
  end
84
86
 
@@ -115,6 +117,7 @@ module ASIR
115
117
  end
116
118
 
117
119
  def serve_connection!
120
+ _log { "serve_connection!: accepting connection" } if @verbose >= 2
118
121
  in_stream, out_stream = _server_accept_connection! @server
119
122
  _log { "serve_connection!: connected #{in_stream} #{out_stream}" } if @verbose >= 1
120
123
  _server_serve_stream! in_stream, out_stream
@@ -19,29 +19,27 @@ module ASIR
19
19
  self.needs_message_identifier = true
20
20
  end
21
21
 
22
- def _send_message message, message_payload
22
+ def _send_message state
23
23
  if @before_message_save
24
- @before_message_save.call(self, message, message_payload)
24
+ @before_message_save.call(self, state)
25
25
  end
26
- message_payload.save!
26
+ state.message_payload.save!
27
27
  # message[:database_id] ||= message_payload.database_id
28
28
  if @after_message_save
29
- @after_message_save.call(self, message, message_payload)
29
+ @after_message_save.call(self, state)
30
30
  end
31
- nil # message_state
32
31
  end
33
32
 
34
- def _send_result message, result, result_payload, stream, message_state
35
- return if one_way? or message.one_way?
33
+ def _send_result state
34
+ return if one_way? or state.message.one_way?
36
35
  if @before_result_save
37
- @before_result_save.call(self, message, result, result_payload)
36
+ @before_result_save.call(self, state)
38
37
  end
39
- result_payload.save!
38
+ state.result_payload.save!
40
39
  result[:database_id] = result_payload.database_id
41
40
  if @after_result_save
42
- @after_result_save.call(self, message, result, result_payload)
41
+ @after_result_save.call(self, state)
43
42
  end
44
- nil
45
43
  end
46
44
  end
47
45
  end
@@ -14,14 +14,9 @@ module ASIR
14
14
  # Proc to call(transport, message) when #send_message fails with no recourse.
15
15
  attr_accessor :on_failed_message
16
16
 
17
- # Return the subTransports#send_message result unmodified from #_send_message.
18
- def _receive_result message, opaque_result
19
- opaque_result
20
- end
21
-
22
- # Return the subTransports#send_message result unmodified from #_send_message.
23
- def receive_result message, opaque_result
24
- opaque_result
17
+ # Return the subTransports' result unmodified from #_send_message.
18
+ def _receive_result state
19
+ true
25
20
  end
26
21
 
27
22
  def needs_message_identifier? message
@@ -40,10 +35,10 @@ module ASIR
40
35
  end
41
36
 
42
37
  # Called from within _send_message rescue.
43
- def _handle_send_message_exception! transport, message, exc
44
- _log { [ :send_message, :transport_failed, exc ] }
45
- (message[:transport_exceptions] ||= [ ]) << "#{exc.inspect}: #{exc.backtrace.first}"
46
- @on_send_message_exception.call(self, message, exc) if @on_send_message_exception
38
+ def _handle_send_message_exception! transport, state, exc
39
+ _log { [ :send_message, :transport_failed, exc, exc.backtrace ] }
40
+ (state.message[:transport_exceptions] ||= [ ]) << "#{exc.inspect}: #{exc.backtrace.first}"
41
+ @on_send_message_exception.call(self, state, exc) if @on_send_message_exception
47
42
  self
48
43
  end
49
44
  end
@@ -23,8 +23,8 @@ module ASIR
23
23
  end
24
24
  end
25
25
 
26
- def _send_message message, message_payload
27
- transport.send_message(message)
26
+ def _send_message state
27
+ transport.send_message(state.message)
28
28
  end
29
29
  end
30
30
  # !SLIDE END
@@ -7,16 +7,16 @@ module ASIR
7
7
  class Fallback < self
8
8
  include Composite
9
9
 
10
- def _send_message message, message_payload
10
+ def _send_message state
11
11
  result = sent = first_exception = nil
12
12
  transports.each do | transport |
13
13
  begin
14
- result = transport.send_message(message)
14
+ result = transport.send_message(state.message)
15
15
  sent = true
16
16
  break
17
17
  rescue ::Exception => exc
18
18
  first_exception ||= exc
19
- _handle_send_message_exception! transport, message, exc
19
+ _handle_send_message_exception! transport, state, exc
20
20
  end
21
21
  end
22
22
  unless sent
@@ -25,7 +25,7 @@ module ASIR
25
25
  end
26
26
  raise FallbackError, "fallback failed"
27
27
  end
28
- result
28
+ state.result = Result.new(state.message, result)
29
29
  end
30
30
  class FallbackError < Error; end
31
31
  end
@@ -15,24 +15,24 @@ module ASIR
15
15
  def initialize opts = nil; @one_way = true; super; end
16
16
 
17
17
  # Writes a Message payload String.
18
- def _send_message message, message_payload
19
- _write message_payload, stream, message
18
+ def _send_message state
19
+ _write(state.message_payload, state.out_stream || stream, state)
20
20
  ensure
21
21
  close if file && ::File.pipe?(file)
22
22
  end
23
23
 
24
24
  # Returns a Message payload String.
25
- def _receive_message stream, additional_data
26
- [ _read(stream, nil), nil ]
25
+ def _receive_message state
26
+ state.message_payload = _read(state.in_stream || stream, state)
27
27
  end
28
28
 
29
29
  # one-way; no Result.
30
- def _send_result message, result, result_payload, stream, message_state
30
+ def _send_result state
31
31
  nil
32
32
  end
33
33
 
34
34
  # one-way; no Result.
35
- def _receive_result message, opaque_result
35
+ def _receive_result state
36
36
  nil
37
37
  end
38
38
 
@@ -63,7 +63,6 @@ module ASIR
63
63
  # Named Pipe Server
64
64
 
65
65
  def prepare_server!
66
- # _log [ :prepare_pipe_server!, file ]
67
66
  unless ::File.exist? file
68
67
  system(cmd = "mkfifo #{file.inspect}") or raise "cannot run #{cmd.inspect}"
69
68
  ::File.chmod(perms, file) rescue nil if perms
@@ -72,7 +71,6 @@ module ASIR
72
71
  alias :prepare_pipe_server! :prepare_server!
73
72
 
74
73
  def run_server!
75
- # _log [ :run_pipe_server!, file ]
76
74
  with_server_signals! do
77
75
  @running = true
78
76
  while @running
@@ -30,18 +30,23 @@ module ASIR
30
30
 
31
31
  # Send the Message payload String using HTTP POST.
32
32
  # Returns the HTTPClient::Request response object.
33
- def _send_message message, message_payload
33
+ def _send_message state
34
34
  client.with_stream! do | client |
35
- client.post(uri, message_payload)
35
+ state.in_stream =
36
+ client.post(message_uri(state), state.message_payload)
36
37
  end
37
38
  end
38
39
 
40
+ # Subclasses can override.
41
+ def message_uri state
42
+ state.message[:uri] || uri
43
+ end
44
+
39
45
  # Recieve the Result payload String from the opaque
40
46
  # HTTPClient::Request response object returned from #_send_message.
41
- def _receive_result message, http_result_message
42
- # $stderr.puts " ### http_result_message.content.encoding = #{http_result_message.content.encoding.inspect}" rescue nil
43
- # $stderr.puts " ### http_result_message.content = #{http_result_message.content.inspect}" rescue nil
44
- http_result_message.content.to_s
47
+ def _receive_result state
48
+ state.result_payload =
49
+ state.in_stream.content.to_s
45
50
  end
46
51
 
47
52
  CONTENT_TYPE = 'Content-Type'.freeze
@@ -6,14 +6,16 @@ module ASIR
6
6
  # Send Message to same process.
7
7
  # Requires Identity Coder.
8
8
  class Local < self
9
- # Returns Result object after invoking Message.
10
- def _send_message message, message_payload
11
- invoke_message!(message)
9
+ # Capture Result object after invoking Message.
10
+ def _send_message state
11
+ invoke_message!(state)
12
+ self
12
13
  end
13
14
 
14
- # Returns Result object from #send_message.
15
- def _receive_result message, opaque_result
16
- opaque_result
15
+ # Result object was captured in #_send_message.
16
+ def _receive_result state
17
+ state.result_payload = state.result
18
+ self
17
19
  end
18
20
  end
19
21
  # !SLIDE END
@@ -5,7 +5,7 @@ module ASIR
5
5
  #
6
6
  # Never send Message.
7
7
  class Null < self
8
- def _send_message message, message_payload
8
+ def _send_message state
9
9
  nil
10
10
  end
11
11
  end
@@ -14,7 +14,7 @@ module ASIR
14
14
  HEADER = "# asir_payload_size: "
15
15
  FOOTER = "\n# asir_payload_end"
16
16
 
17
- def _write payload, stream, context
17
+ def _write payload, stream, state
18
18
  stream.write HEADER
19
19
  stream.puts payload.size
20
20
  stream.write payload
@@ -23,7 +23,7 @@ module ASIR
23
23
  stream
24
24
  end
25
25
 
26
- def _read stream, context
26
+ def _read stream, state
27
27
  size = /\d+$/.match(stream.readline.chomp)[0].to_i # HEADER (size)
28
28
  payload = stream.read(size)
29
29
  stream.readline # FOOTER
@@ -7,17 +7,16 @@ module ASIR
7
7
  # Rack Transport
8
8
  class Rack < HTTP
9
9
  # Receive the Message payload String from the Rack::Request object.
10
- # Returns the [ Rack::Request, Rack::Response ] as the message_state.
11
- def _receive_message rack_req_res, additional_data
12
- body = rack_req_res.first.body.read
13
- [ body, rack_req_res ]
10
+ def _receive_message state
11
+ rack_request = state.in_stream
12
+ state.message_payload = rack_request.body.read
14
13
  end
15
14
 
16
15
  # Send the Result payload String in the Rack::Response object as application/binary.
17
- def _send_result message, result, result_payload, rack_rq_rs, message_state
18
- rack_response = rack_rq_rs[1]
16
+ def _send_result state
17
+ rack_response = state.out_stream
19
18
  rack_response[CONTENT_TYPE] = APPLICATION_BINARY
20
- rack_response.write result_payload
19
+ rack_response.write state.result_payload
21
20
  end
22
21
 
23
22
  # Constructs a Rackable App from this Transport.
@@ -41,8 +40,7 @@ module ASIR
41
40
  def call(env)
42
41
  rq = ::Rack::Request.new(env)
43
42
  rs = ::Rack::Response.new
44
- rack_rq_rs = [ rq, rs ]
45
- serve_message! rack_rq_rs, rack_rq_rs
43
+ serve_message! rq, rs
46
44
  rs.finish # => [ status, header, rbody ]
47
45
  end
48
46
 
@@ -13,19 +13,20 @@ module ASIR
13
13
  # Proc to call(transport, message) before retry.
14
14
  attr_accessor :before_retry
15
15
 
16
- def _send_message message, message_payload
16
+ def _send_message state
17
17
  first_exception = nil
18
18
  with_retry do | action, data |
19
19
  case action
20
20
  when :try
21
- transport.send_message(message)
21
+ result = transport.send_message(state.message)
22
+ state.result = Result.new(state.message, result)
22
23
  when :rescue #, exc
23
24
  first_exception ||= data
24
- _handle_send_message_exception! transport, message, data
25
+ _handle_send_message_exception! transport, state, data
25
26
  when :retry #, exc
26
- before_retry.call(self, message) if before_retry
27
+ before_retry.call(self, state) if before_retry
27
28
  when :failed
28
- @on_failed_message.call(self, message) if @on_failed_message
29
+ @on_failed_message.call(self, state) if @on_failed_message
29
30
  if first_exception && @reraise_first_exception
30
31
  $! = first_exception
31
32
  raise
@@ -11,18 +11,19 @@ module ASIR
11
11
  @one_way = true; super
12
12
  end
13
13
 
14
- def _send_message message, message_payload
14
+ def _send_message state
15
15
  Process.fork do
16
- send_result(super, nil, nil)
16
+ super
17
+ send_result(state)
17
18
  end
18
19
  end
19
20
 
20
21
  # one-way; no Result
21
- def _receive_result message, opaque_result
22
+ def _receive_result state
22
23
  end
23
24
 
24
25
  # one-way; no Result
25
- def _send_result message, result, result_payload, stream, message_state
26
+ def _send_result state
26
27
  end
27
28
  end
28
29
  # !SLIDE END
@@ -9,14 +9,14 @@ module ASIR
9
9
  # !SLIDE
10
10
  # TCP Socket Client
11
11
  def _client_connect!
12
- sock = TCPSocket.open(host, port)
12
+ sock = ::TCPSocket.open(host, port)
13
13
  end
14
14
 
15
15
  # !SLIDE
16
16
  # TCP Socket Server
17
17
  def _server!
18
- @server = TCPServer.open(port)
19
- @server.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, false)
18
+ @server = ::TCPServer.open(port)
19
+ @server.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, false)
20
20
  end
21
21
 
22
22
  def _server_accept_connection! server
@@ -24,8 +24,8 @@ module ASIR
24
24
  [ socket, socket ] # Use same socket for in_stream and out_stream
25
25
  end
26
26
 
27
- def _server_close_connection! stream, out_stream
28
- stream.close rescue nil
27
+ def _server_close_connection! in_stream, out_stream
28
+ in_stream.close rescue nil
29
29
  end
30
30
  end
31
31
  # !SLIDE END
@@ -12,7 +12,7 @@ module ASIR
12
12
  # Defaults to ::Thread.
13
13
  attr_accessor :thread_class
14
14
 
15
- # Callback: call(self, message, thr).
15
+ # Callback: call(self, MessageState, thr).
16
16
  attr_accessor :after_thread_new
17
17
 
18
18
  def initialize *args
@@ -20,20 +20,26 @@ module ASIR
20
20
  @one_way = true; super
21
21
  end
22
22
 
23
- def _send_message message, message_payload
23
+ def _send_message state
24
24
  thr = thread_class.new do
25
- send_result(super, nil, nil)
25
+ super
26
+ send_result(state)
26
27
  end
27
- @after_thread_new.call(self, message, thr) if @after_thread_new
28
+ state.in_stream = thr
29
+ @after_thread_new.call(self, state, thr) if @after_thread_new
28
30
  thr
29
31
  end
30
32
 
31
33
  # one-way; no Result
32
- def _receive_result message, opaque_result
34
+ def _receive_result state
33
35
  end
34
36
 
35
37
  # one-way; no Result
36
- def _send_result message, result, result_payload, stream, message_state
38
+ def _send_result state
39
+ end
40
+
41
+ # one-may; no Result
42
+ def receive_result state
37
43
  end
38
44
  end
39
45
  # !SLIDE END
@@ -9,16 +9,18 @@ module ASIR
9
9
 
10
10
  # Server-side: WEBrick
11
11
 
12
- # Receive the Message payload String from the HTTP Message object.
13
- # Returns the original http_message as the message_state.
14
- def _receive_message http_message, additional_data
15
- [ http_message.body, http_message ]
12
+ # Receive the Message payload from the HTTP Message body.
13
+ def _receive_message state
14
+ http_message = state.in_stream
15
+ state.message_payload = http_message.body
16
+ state.message_opaque = http_message
16
17
  end
17
18
 
18
- # Send the Result payload String in the HTTP Response object as application/binary.
19
- def _send_result message, result, result_payload, http_result, message_state
19
+ # Send the Result payload in the HTTP Response body as application/binary.
20
+ def _send_result state
21
+ http_result = state.out_stream
20
22
  http_result[CONTENT_TYPE] = APPLICATION_BINARY
21
- http_result.body = result_payload
23
+ http_result.body = state.result_payload
22
24
  end
23
25
 
24
26
  def prepare_server! opts = { }
data/lib/asir/uuid.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'thread'
2
+
1
3
  module ASIR
2
4
  # Provides an RFC4122-compliant random (version 4) UUID service.
3
5
  module UUID
@@ -13,19 +15,55 @@ module UUID
13
15
  PROC_SYS_FILE = "/proc/sys/kernel/random/uuid".freeze
14
16
  case
15
17
  when File.exist?(PROC_SYS_FILE)
16
- def self.generate
18
+ def new_uuid
17
19
  File.read(PROC_SYS_FILE).chomp!
18
20
  end
19
21
  when (gem 'uuid' rescue nil)
20
22
  require 'uuid'
21
- def self.generate
23
+ def new_uuid
22
24
  ::UUID.generate
23
25
  end
24
26
  else
25
- def self.generate
27
+ def new_uuid
26
28
  raise "Unimplemented"
27
29
  end
28
30
  end
31
+ UUID_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\Z/i
32
+
33
+ def process_uuid
34
+ @@process_uuid_mutex.synchronize do
35
+ if @@pid != $$
36
+ @@pid = $$
37
+ @@process_uuid = nil
38
+ end
39
+ @@process_uuid ||= new_uuid
40
+ end
41
+ end
42
+ @@pid = @@process_uuid = nil
43
+ @@process_uuid_mutex = Mutex.new
44
+
45
+ def counter_uuid
46
+ i = @@counter_mutex.synchronize do
47
+ @@counter += 1
48
+ end
49
+ "#{i}-#{process_uuid}"
50
+ end
51
+ @@counter ||= 0
52
+ @@counter_mutex = Mutex.new
53
+ COUNTER_UUID_REGEX = /\A[0-9]+-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\Z/i
54
+
55
+ # Returns a unique counter_uuid for a Thread.
56
+ # thr defaults to Thread.current.
57
+ def thread_uuid thr = nil
58
+ thr ||= Thread.current
59
+ thr[:'ASIR::UUID.thread_uuid'] || @@thread_uuid_mutex.synchronize do
60
+ thr[:'ASIR::UUID.thread_uuid'] = counter_uuid
61
+ end
62
+ end
63
+ @@thread_uuid_mutex = Mutex.new
64
+
65
+ extend self
66
+ alias :generate :new_uuid # DEPRECATED
29
67
  end
30
68
  end
31
69
 
data/lib/asir/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ASIR
2
- VERSION = "1.1.12"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -40,10 +40,10 @@ describe "ASIR::Transport" do
40
40
  end
41
41
 
42
42
  it 'should handle on_result_exception callbacks' do
43
- _transport, _result = nil, nil
44
- p = lambda do | transport, result |
43
+ _transport, _message_result = nil, nil
44
+ p = lambda do | transport, message_result |
45
45
  _transport = transport
46
- _result = result
46
+ _message_result = message_result
47
47
  end
48
48
  transport.on_result_exception = p
49
49
 
@@ -71,12 +71,17 @@ describe "ASIR::Transport" do
71
71
 
72
72
  _transport.should == object.transport
73
73
 
74
+ _result = _message_result.result
74
75
  _result.class.should == ASIR::Result
75
76
  _result.object_id.should == result.object_id
76
77
 
77
78
  _result.message.class.should == ASIR::Message
78
79
  _result.message.object_id.should == result.message.object_id
79
80
  _result.exception.should == exc
81
+
82
+ _message = _message_result.message
83
+ _message.object_id.should == _message_result.message.object_id
84
+
80
85
  end
81
86
  end
82
87
 
data/spec/uuid_spec.rb ADDED
@@ -0,0 +1,76 @@
1
+ require 'asir/uuid'
2
+
3
+ describe "ASIR::UUID" do
4
+ attr_accessor :u
5
+ before { @u = ASIR::UUID }
6
+
7
+ it 'should return a UUID from #new_uuid.' do
8
+ x = u.new_uuid
9
+ x.should =~ ASIR::UUID::UUID_REGEX
10
+ end
11
+
12
+ it 'should return unique result from #new_uuid.' do
13
+ a = [ ]
14
+ 10.times do
15
+ x = u.new_uuid
16
+ x.should =~ ASIR::UUID::UUID_REGEX
17
+ a << u.new_uuid
18
+ end
19
+ a.uniq.size.should == 10
20
+ end
21
+
22
+ it 'should single unique result per process from #process_uuid.' do
23
+ a = [ ]
24
+ 10.times do
25
+ x = u.process_uuid
26
+ x.should =~ ASIR::UUID::UUID_REGEX
27
+ a << u.process_uuid
28
+ a << u.generate
29
+ end
30
+ a.uniq.size.should == 11
31
+ end
32
+
33
+ it 'should single unique result per process from #counter_uuid.' do
34
+ a = [ ]
35
+ 10.times do
36
+ x = u.counter_uuid
37
+ x.should =~ ASIR::UUID::COUNTER_UUID_REGEX
38
+ a << x
39
+ a << u.process_uuid
40
+ a << u.generate
41
+ end
42
+ a.uniq.size.should == 21
43
+ end
44
+
45
+ it 'should single unique result per Thread from #thread_uuid.' do
46
+ a = [ ]
47
+ 10.times do
48
+ x = u.thread_uuid
49
+ x.should =~ ASIR::UUID::COUNTER_UUID_REGEX
50
+ a << x
51
+ a << u.generate
52
+ a << u.process_uuid
53
+ end
54
+ a.uniq.size.should == 12
55
+
56
+ a = [ ]
57
+ b = [ ]
58
+ t1 = Thread.current
59
+ t2 = Thread.new do
60
+ 10.times do
61
+ b << u.thread_uuid(t1)
62
+ b << u.thread_uuid(t2)
63
+ end
64
+ end
65
+ 10.times do
66
+ a << u.thread_uuid(t1)
67
+ a << u.thread_uuid(t2)
68
+ end
69
+
70
+ t2.join
71
+ c = a + b
72
+ c.uniq.size.should == 2
73
+ end
74
+
75
+ end
76
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asir
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.12
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -221,6 +221,7 @@ files:
221
221
  - lib/asir/main.rb
222
222
  - lib/asir/message.rb
223
223
  - lib/asir/message/delay.rb
224
+ - lib/asir/message/state.rb
224
225
  - lib/asir/object_resolving.rb
225
226
  - lib/asir/poll_throttle.rb
226
227
  - lib/asir/result.rb
@@ -264,6 +265,7 @@ files:
264
265
  - spec/thread_pool_spec.rb
265
266
  - spec/thread_variable_spec.rb
266
267
  - spec/transport_spec.rb
268
+ - spec/uuid_spec.rb
267
269
  - spec/yaml_spec.rb
268
270
  - stylesheets/slides.css
269
271
  homepage: http://github.com/kstephens/abstracting_services_in_ruby
@@ -303,4 +305,5 @@ test_files:
303
305
  - spec/thread_pool_spec.rb
304
306
  - spec/thread_variable_spec.rb
305
307
  - spec/transport_spec.rb
308
+ - spec/uuid_spec.rb
306
309
  - spec/yaml_spec.rb