asir 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/.gitignore +11 -0
  2. data/Gemfile +16 -0
  3. data/README.textile +50 -0
  4. data/Rakefile +83 -0
  5. data/VERSION +1 -0
  6. data/asir.gemspec +36 -0
  7. data/asir.riterate.yml +114 -0
  8. data/bin/asir +6 -0
  9. data/doc/Rakefile +8 -0
  10. data/doc/asir-sequence.pic +84 -0
  11. data/doc/asir-sequence.svg +1559 -0
  12. data/doc/sequence.pic +430 -0
  13. data/example/asir_control.sh +24 -0
  14. data/example/asir_control_client_http.rb +14 -0
  15. data/example/asir_control_client_zmq.rb +15 -0
  16. data/example/config/asir_config.rb +63 -0
  17. data/example/delayed_service.rb +15 -0
  18. data/example/ex01.rb +12 -0
  19. data/example/ex02.rb +12 -0
  20. data/example/ex03.rb +19 -0
  21. data/example/ex04.rb +33 -0
  22. data/example/ex05.rb +16 -0
  23. data/example/ex06.rb +26 -0
  24. data/example/ex07.rb +28 -0
  25. data/example/ex08.rb +30 -0
  26. data/example/ex09.rb +25 -0
  27. data/example/ex10.rb +24 -0
  28. data/example/ex11.rb +48 -0
  29. data/example/ex12.rb +34 -0
  30. data/example/ex13.rb +35 -0
  31. data/example/ex14.rb +30 -0
  32. data/example/ex15.rb +13 -0
  33. data/example/ex16.rb +33 -0
  34. data/example/ex17.rb +41 -0
  35. data/example/ex18.rb +62 -0
  36. data/example/ex19.rb +32 -0
  37. data/example/ex20.rb +28 -0
  38. data/example/ex21.rb +28 -0
  39. data/example/ex22.rb +15 -0
  40. data/example/ex23.rb +20 -0
  41. data/example/ex24.rb +35 -0
  42. data/example/example_helper.rb +51 -0
  43. data/example/sample_service.rb +162 -0
  44. data/example/unsafe_service.rb +12 -0
  45. data/hack_night/README.txt +18 -0
  46. data/hack_night/exercise/prob-1.rb +18 -0
  47. data/hack_night/exercise/prob-2.rb +21 -0
  48. data/hack_night/exercise/prob-3.rb +16 -0
  49. data/hack_night/exercise/prob-4.rb +36 -0
  50. data/hack_night/exercise/prob-5.rb +36 -0
  51. data/hack_night/exercise/prob-6.rb +95 -0
  52. data/hack_night/exercise/prob-7.rb +34 -0
  53. data/hack_night/solution/math_service.rb +11 -0
  54. data/hack_night/solution/prob-1.rb +12 -0
  55. data/hack_night/solution/prob-2.rb +15 -0
  56. data/hack_night/solution/prob-3.rb +17 -0
  57. data/hack_night/solution/prob-4.rb +37 -0
  58. data/hack_night/solution/prob-5.rb +21 -0
  59. data/hack_night/solution/prob-6.rb +33 -0
  60. data/hack_night/solution/prob-7.rb +36 -0
  61. data/lab/phony_proc.rb +31 -0
  62. data/lib/asir.rb +253 -0
  63. data/lib/asir/additional_data.rb +25 -0
  64. data/lib/asir/channel.rb +130 -0
  65. data/lib/asir/client.rb +111 -0
  66. data/lib/asir/code_block.rb +57 -0
  67. data/lib/asir/code_more.rb +50 -0
  68. data/lib/asir/coder.rb +26 -0
  69. data/lib/asir/coder/base64.rb +19 -0
  70. data/lib/asir/coder/chain.rb +30 -0
  71. data/lib/asir/coder/identity.rb +23 -0
  72. data/lib/asir/coder/json.rb +30 -0
  73. data/lib/asir/coder/marshal.rb +17 -0
  74. data/lib/asir/coder/null.rb +23 -0
  75. data/lib/asir/coder/proc.rb +22 -0
  76. data/lib/asir/coder/sign.rb +48 -0
  77. data/lib/asir/coder/xml.rb +213 -0
  78. data/lib/asir/coder/yaml.rb +33 -0
  79. data/lib/asir/coder/zlib.rb +21 -0
  80. data/lib/asir/configuration.rb +32 -0
  81. data/lib/asir/error.rb +34 -0
  82. data/lib/asir/identity.rb +36 -0
  83. data/lib/asir/initialization.rb +23 -0
  84. data/lib/asir/log.rb +82 -0
  85. data/lib/asir/main.rb +396 -0
  86. data/lib/asir/message.rb +31 -0
  87. data/lib/asir/message/delay.rb +35 -0
  88. data/lib/asir/object_resolving.rb +15 -0
  89. data/lib/asir/result.rb +39 -0
  90. data/lib/asir/retry_behavior.rb +54 -0
  91. data/lib/asir/transport.rb +241 -0
  92. data/lib/asir/transport/beanstalk.rb +217 -0
  93. data/lib/asir/transport/broadcast.rb +34 -0
  94. data/lib/asir/transport/buffer.rb +115 -0
  95. data/lib/asir/transport/composite.rb +19 -0
  96. data/lib/asir/transport/connection_oriented.rb +180 -0
  97. data/lib/asir/transport/delay.rb +38 -0
  98. data/lib/asir/transport/delegation.rb +53 -0
  99. data/lib/asir/transport/fallback.rb +36 -0
  100. data/lib/asir/transport/file.rb +88 -0
  101. data/lib/asir/transport/http.rb +54 -0
  102. data/lib/asir/transport/local.rb +21 -0
  103. data/lib/asir/transport/null.rb +14 -0
  104. data/lib/asir/transport/payload_io.rb +52 -0
  105. data/lib/asir/transport/rack.rb +73 -0
  106. data/lib/asir/transport/retry.rb +41 -0
  107. data/lib/asir/transport/stream.rb +35 -0
  108. data/lib/asir/transport/subprocess.rb +30 -0
  109. data/lib/asir/transport/tcp_socket.rb +34 -0
  110. data/lib/asir/transport/webrick.rb +50 -0
  111. data/lib/asir/transport/zmq.rb +110 -0
  112. data/lib/asir/uuid.rb +32 -0
  113. data/lib/asir/version.rb +3 -0
  114. data/spec/const_get_speed_spec.rb +33 -0
  115. data/spec/debug_helper.rb +20 -0
  116. data/spec/example_spec.rb +88 -0
  117. data/spec/json_spec.rb +128 -0
  118. data/spec/spec_helper.rb +3 -0
  119. data/spec/xml_spec.rb +144 -0
  120. data/stylesheets/slides.css +105 -0
  121. metadata +173 -0
@@ -0,0 +1,15 @@
1
+ module ASIR
2
+ # !SLIDE
3
+ # Object Resolving
4
+ #
5
+ module ObjectResolving
6
+ class ResolveError < Error; end
7
+ def resolve_object name
8
+ name.to_s.split(MODULE_SEP).inject(Object){|m, n| m.const_get(n)}
9
+ rescue ::Exception => err
10
+ raise ResolveError, "cannot resolve #{name.inspect}: #{err.inspect}", err.backtrace
11
+ end
12
+ end
13
+ # !SLIDE END
14
+ end
15
+
@@ -0,0 +1,39 @@
1
+ module ASIR
2
+ # !SLIDE
3
+ # Result
4
+ #
5
+ # Encapsulate the result returned to the Client.
6
+ class Result
7
+ include AdditionalData, Identity, CodeMore::Result
8
+ attr_accessor :message, :result, :exception
9
+ # Optional: Opaque data about the server that processed the Message.
10
+ attr_accessor :server
11
+
12
+ def initialize msg, res = nil, exc = nil
13
+ @message = msg; @result = res
14
+ @exception = exc && EncapsulatedException.new(exc)
15
+ @identifier = @message.identifier
16
+ end
17
+ end
18
+ # !SLIDE END
19
+
20
+ # !SLIDE
21
+ # Encapsulated Exception
22
+ #
23
+ # Encapsulates exceptions raised in the Service.
24
+ class EncapsulatedException
25
+ include ObjectResolving, AdditionalData
26
+ attr_accessor :exception_class, :exception_message, :exception_backtrace
27
+
28
+ def initialize exc
29
+ @exception_class = exc.class.name
30
+ @exception_message = exc.message
31
+ @exception_backtrace = exc.backtrace
32
+ end
33
+
34
+ def invoke!
35
+ raise resolve_object(@exception_class), @exception_message, @exception_backtrace
36
+ end
37
+ end
38
+ # !SLIDE END
39
+ end
@@ -0,0 +1,54 @@
1
+ module ASIR
2
+ # !SLIDE
3
+ # Generic retry behavior
4
+ module RetryBehavior
5
+ # Maximum trys.
6
+ attr_accessor :try_max
7
+ # Initial amount of seconds to sleep between each try.
8
+ attr_accessor :try_sleep
9
+ # Amount of seconds to increment sleep between each try.
10
+ attr_accessor :try_sleep_increment
11
+ # Maxinum amount of seconds to sleep between each try.
12
+ attr_accessor :try_sleep_max
13
+
14
+ # Yields:
15
+ # :try, n_try
16
+ # :rescue, exc
17
+ # :retry, exc
18
+ # :failed, nil
19
+ def with_retry
20
+ n_try = 0
21
+ sleep_secs = try_sleep
22
+ result = done = last_exception = nil
23
+ begin
24
+ n_try += 1
25
+ result = yield :try, n_try
26
+ done = true
27
+ rescue *Error::Unforwardable.unforwardable => exc
28
+ raise
29
+ rescue ::Exception => exc
30
+ last_exception = exc
31
+ yield :rescue, exc
32
+ if ! try_max || try_max > n_try
33
+ yield :retry, exc
34
+ if sleep_secs
35
+ sleep sleep_secs
36
+ sleep_secs += try_sleep_increment if try_sleep_increment
37
+ sleep_secs = try_sleep_max if try_sleep_max && sleep_secs > try_sleep_max
38
+ end
39
+ retry
40
+ end
41
+ end
42
+ unless done
43
+ unless yield :failed, last_exception
44
+ exc = last_exception
45
+ raise RetryError, "Retry failed: #{exc.inspect}", exc.backtrace
46
+ end
47
+ end
48
+ result
49
+ end
50
+ class RetryError < Error; end
51
+ end # module
52
+ # !SLIDE END
53
+ end # module ASIR
54
+
@@ -0,0 +1,241 @@
1
+ require 'time'
2
+ require 'asir/message/delay'
3
+
4
+ module ASIR
5
+ # !SLIDE
6
+ # Transport
7
+ #
8
+ # Client: Send the Message to the Service.
9
+ # Service: Receive the Message from the Client.
10
+ # Service: Invoke the Message.
11
+ # Service: Send the Result to the Client.
12
+ # Client: Receive the Result from the Service.
13
+ class Transport
14
+ include Log, Initialization, AdditionalData, Message::Delay
15
+
16
+ attr_accessor :encoder, :decoder, :one_way
17
+
18
+ # !SLIDE
19
+ # Transport#send_message
20
+ # * Encode Message.
21
+ # * Send encoded Message.
22
+ # * Receive decoded Result.
23
+ def send_message message
24
+ @message_count ||= 0; @message_count += 1
25
+ message.create_timestamp! if needs_message_timestamp?
26
+ message.create_identifier! if needs_message_identifier?
27
+ @before_send_message.call(self, message) if @before_send_message
28
+ relative_message_delay! message
29
+ message_payload = encoder.dup.encode(message)
30
+ opaque_result = _send_message(message, message_payload)
31
+ receive_result(message, opaque_result)
32
+ end
33
+
34
+ # !SLIDE
35
+ # Transport#receive_message
36
+ # Receive Message payload from stream.
37
+ def receive_message stream
38
+ @message_count ||= 0; @message_count += 1
39
+ additional_data = { }
40
+ if req_and_state = _receive_message(stream, additional_data)
41
+ message = req_and_state[0] = encoder.dup.decode(req_and_state.first)
42
+ message.additional_data!.update(additional_data) if message
43
+ if @after_receive_message
44
+ begin
45
+ @after_receive_message.call(self, message)
46
+ rescue ::Exception => exc
47
+ _log { [ :receive_message, :after_receive_message, :exception, exc ] }
48
+ end
49
+ end
50
+ end
51
+ req_and_state
52
+ end
53
+ # !SLIDE END
54
+
55
+ # !SLIDE
56
+ # Transport#send_result
57
+ # Send Result to stream.
58
+ def send_result result, stream, message_state
59
+ message = result.message
60
+ if @on_result_exception && result.exception
61
+ begin
62
+ @on_result_exception.call(self, result)
63
+ rescue ::Exception => exc
64
+ _log { [ :send_result, :result, result, :on_result_exception, exc ] }
65
+ end
66
+ end
67
+ if @one_way && message.block
68
+ message.block.call(result)
69
+ else
70
+ result.message = nil # avoid sending back entire Message.
71
+ result_payload = decoder.dup.encode(result)
72
+ _send_result(message, result, result_payload, stream, message_state)
73
+ end
74
+ end
75
+ # !SLIDE END
76
+
77
+ # !SLIDE
78
+ # Transport#receive_result
79
+ # Receieve Result from stream:
80
+ # * Receive Result payload
81
+ # * Decode Result.
82
+ # * Extract Result result or exception.
83
+ def receive_result message, opaque_result
84
+ result_payload = _receive_result(message, opaque_result)
85
+ result = decoder.dup.decode(result_payload)
86
+ if result && ! message.one_way
87
+ if exc = result.exception
88
+ exc.invoke!
89
+ else
90
+ if ! @one_way && message.block
91
+ message.block.call(result)
92
+ end
93
+ result.result
94
+ end
95
+ end
96
+ end
97
+ # !SLIDE END
98
+
99
+ def initialize *args
100
+ @verbose = 0
101
+ super
102
+ end
103
+
104
+ # Incremented for each message sent or received.
105
+ attr_accessor :message_count
106
+
107
+ # A Proc to call within #receive_message, after #_receive_message.
108
+ # trans.after_receiver_message(trans, message)
109
+ attr_accessor :after_receive_message
110
+
111
+ # A Proc to call within #send_message, before #_send_message.
112
+ # trans.before_send_message(trans, message)
113
+ attr_accessor :before_send_message
114
+
115
+ # Proc to call after #_send_result if result.exception.
116
+ # trans.on_result_exception.call(trans, result)
117
+ attr_accessor :on_result_exception
118
+
119
+ # Proc to call with exception, if exception occurs within #serve_message!, but outside
120
+ # Message#invoke!.
121
+ #
122
+ # trans.on_exception.call(trans, exception, :message, Message_instance)
123
+ # trans.on_exception.call(trans, exception, :result, Message_instance, Result_instance)
124
+ attr_accessor :on_exception
125
+
126
+ attr_accessor :needs_message_identifier, :needs_message_timestamp
127
+ alias :needs_message_identifier? :needs_message_identifier
128
+ alias :needs_message_timestamp? :needs_message_timestamp
129
+
130
+ attr_accessor :verbose
131
+
132
+ def _subclass_responsibility *args
133
+ raise Error::SubclassResponsibility "subclass responsibility"
134
+ end
135
+ alias :_send_message :_subclass_responsibility
136
+ alias :_receive_message :_subclass_responsibility
137
+ alias :_send_result :_subclass_responsibility
138
+ alias :_receive_result :_subclass_responsibility
139
+
140
+ # !SLIDE
141
+ # Serve a Message.
142
+ def serve_message! in_stream, out_stream
143
+ message = message_state = message_ok = result = result_ok = nil
144
+ exception = original_exception = unforwardable_exception = nil
145
+ message, message_state = receive_message(in_stream)
146
+ if message
147
+ message_ok = true
148
+ result = invoke_message!(message)
149
+ result_ok = true
150
+ self
151
+ else
152
+ nil
153
+ end
154
+ rescue ::Exception => exc
155
+ exception = original_exception = exc
156
+ _log [ :message_error, exc ]
157
+ @on_exception.call(self, exc, :message, message) if @on_exception
158
+ ensure
159
+ begin
160
+ if message_ok
161
+ if exception && ! result_ok
162
+ case exception
163
+ when *Error::Unforwardable.unforwardable
164
+ unforwardable_exception = exception = Error::Unforwardable.new(exception)
165
+ end
166
+ result = Result.new(message, nil, exception)
167
+ end
168
+ if out_stream
169
+ send_result(result, out_stream, message_state)
170
+ end
171
+ end
172
+ rescue ::Exception => exc
173
+ _log [ :result_error, exc ]
174
+ @on_exception.call(self, exc, :result, message, result) if @on_exception
175
+ end
176
+ raise original_exception if unforwardable_exception
177
+ end
178
+
179
+ # !SLIDE pause
180
+
181
+ # !SLIDE
182
+ # Transport Server Support
183
+
184
+ def stop! force = false
185
+ @running = false
186
+ stop_server! if respond_to?(:stop_server!)
187
+ raise Error::Terminate if force
188
+ self
189
+ end
190
+
191
+ def with_server_signals!
192
+ old_trap = { }
193
+ [ "TERM", "HUP" ].each do | sig |
194
+ trap = proc do | *args |
195
+ stop!
196
+ @signal_exception = ::ASIR::Error::Terminate.new("#{self} by SIG#{sig} #{args.inspect} in #{__FILE__}:#{__LINE__}")
197
+ end
198
+ old_trap[sig] = Signal.trap(sig, trap)
199
+ end
200
+ yield
201
+ if exc = @signal_exception
202
+ @signal_exception = nil
203
+ raise exc
204
+ end
205
+ ensure
206
+ # $stderr.puts "old_trap = #{old_trap.inspect}"
207
+ old_trap.each do | sig, trap |
208
+ Signal.trap(sig, trap) rescue nil
209
+ end
210
+ end
211
+
212
+ # !SLIDE
213
+ # Transport Support
214
+ # ...
215
+
216
+ def encoder
217
+ @encoder ||=
218
+ Coder::Identity.new
219
+ end
220
+
221
+ def decoder
222
+ @decoder ||=
223
+ encoder
224
+ end
225
+
226
+ # Invokes the Message object, returns a Result object.
227
+ def invoke_message! message
228
+ _processing_message = @processing_message
229
+ @processing_message = true
230
+ wait_for_delay! message
231
+ message.invoke!
232
+ ensure
233
+ @processing_message = _processing_message
234
+ end
235
+
236
+ # !SLIDE END
237
+ # !SLIDE resume
238
+
239
+ end
240
+ # !SLIDE END
241
+ end
@@ -0,0 +1,217 @@
1
+ require 'asir/transport/tcp_socket'
2
+
3
+ module ASIR
4
+ class Transport
5
+ # !SLIDE
6
+ # Beanstalk Transport
7
+ class Beanstalk < TcpSocket
8
+ LINE_TERMINATOR = "\r\n".freeze
9
+
10
+ attr_accessor :tube, :priority, :delay, :ttr
11
+
12
+ def initialize *args
13
+ @port ||= 11300
14
+ @tube ||= 'asir'
15
+ @priority ||= 0
16
+ @delay ||= 0
17
+ @ttr ||= 600
18
+ super
19
+ end
20
+
21
+ # !SLIDE
22
+ # Sends the encoded Message payload String.
23
+ def _send_message message, message_payload
24
+ stream.with_stream! do | s |
25
+ begin
26
+ match =
27
+ _beanstalk(s,
28
+ "put #{message[:beanstalk_priority] || @priority} #{message[:beanstalk_delay] || @delay} #{message[:beanstalk_ttr] || @ttr} #{message_payload.size}\r\n",
29
+ /\AINSERTED (\d+)\r\n\Z/,
30
+ message_payload)
31
+ job_id = message[:beanstalk_job_id] = match[1].to_i
32
+ _log { "beanstalk_job_id = #{job_id.inspect}" } if @verbose >= 2
33
+ rescue ::Exception => exc
34
+ message[:beanstalk_error] = exc
35
+ close
36
+ raise exc
37
+ end
38
+ end
39
+ end
40
+
41
+ RESERVE = "reserve\r\n".freeze
42
+
43
+ # !SLIDE
44
+ # Receives the encoded Message payload String.
45
+ def _receive_message channel, additional_data
46
+ channel.with_stream! do | stream |
47
+ begin
48
+ match =
49
+ _beanstalk(stream,
50
+ RESERVE,
51
+ /\ARESERVED (\d+) (\d+)\r\n\Z/)
52
+ additional_data[:beanstalk_job_id] = match[1].to_i
53
+ additional_data[:beanstalk_message_size] =
54
+ size = match[2].to_i
55
+ message_payload = stream.read(size)
56
+ _read_line_and_expect! stream, /\A\r\n\Z/
57
+ # Pass the original stream used to #_send_result below.
58
+ [ message_payload, stream ]
59
+ rescue ::Exception => exc
60
+ _log { [ :_receive_message, :exception, exc ] }
61
+ additional_data[:beanstalk_error] = exc
62
+ channel.close
63
+ end
64
+ end
65
+ end
66
+
67
+ # !SLIDE
68
+ # Sends the encoded Result payload String.
69
+ def _send_result message, result, result_payload, channel, stream
70
+ #
71
+ # There is a possibility here the following could happen:
72
+ #
73
+ # _receive_message
74
+ # channel == #<Channel:1>
75
+ # channel.stream == #<TCPSocket:1234>
76
+ # end
77
+ # ...
78
+ # ERROR OCCURES:
79
+ # channel.stream.close
80
+ # channel.stream = nil
81
+ # ...
82
+ # _send_result
83
+ # channel == #<Channel:1>
84
+ # channel.stream == #<TCPSocket:5678> # NEW CONNECTION
85
+ # stream.write "delete #{job_id}"
86
+ # ...
87
+ #
88
+ # Therefore: _receiver_message passes the original message stream to us.
89
+ # We insure that the same stream is still the active one and use it.
90
+ channel.with_stream! do | maybe_other_stream |
91
+ _log [ :_send_result, "stream lost" ] if maybe_other_stream != stream
92
+ job_id = message[:beanstalk_job_id] or raise "no beanstalk_job_id"
93
+ _beanstalk(stream,
94
+ "delete #{job_id}\r\n",
95
+ /\ADELETED\r\n\Z/)
96
+ end
97
+ end
98
+
99
+ # !SLIDE
100
+ # Receives the encoded Result payload String.
101
+ def _receive_result message, opaque_result
102
+ nil
103
+ end
104
+
105
+ # !SLIDE
106
+ # Sets beanstalk_delay if message.delay was specified.
107
+ def relative_message_delay! message, now = nil
108
+ if delay = super
109
+ message[:beanstalk_delay] = delay.to_i
110
+ end
111
+ delay
112
+ end
113
+
114
+ # !SLIDE
115
+ # Beanstalk protocol support
116
+
117
+ # Send "something ...\r\n".
118
+ # Expect /\ASOMETHING (\d+)...\r\n".
119
+ def _beanstalk stream, message, expect, payload = nil
120
+ _log { [ :_beanstalk, :message, message ] } if @verbose >= 3
121
+ stream.write message
122
+ if payload
123
+ stream.write payload
124
+ stream.write LINE_TERMINATOR
125
+ end
126
+ stream.flush
127
+ if match = _read_line_and_expect!(stream, expect)
128
+ _log { [ :_beanstalk, :result, match[0] ] } if @verbose >= 3
129
+ end
130
+ match
131
+ end
132
+
133
+ def _after_connect! stream
134
+ if @tube
135
+ _beanstalk(stream,
136
+ "use #{@tube}\r\n",
137
+ /\AUSING #{@tube}\r\n\Z/)
138
+ end
139
+ end
140
+
141
+ # !SLIDE
142
+ # Beanstalk Server
143
+
144
+ def prepare_beanstalk_server!
145
+ _log { "prepare_beanstalk_server! #{uri}" } if @verbose >= 1
146
+ @server = connect!(:try_max => nil,
147
+ :try_sleep => 1,
148
+ :try_sleep_increment => 0.1,
149
+ :try_sleep_max => 10) do | stream |
150
+ if @tube
151
+ _beanstalk(stream,
152
+ "watch #{@tube}\r\n",
153
+ /\AWATCHING (\d+)\r\n\Z/)
154
+ end
155
+ end
156
+ self
157
+ end
158
+ alias :prepare_server! :prepare_beanstalk_server!
159
+
160
+ def run_beanstalk_server!
161
+ _log :run_beanstalk_server! if @verbose >= 1
162
+ with_server_signals! do
163
+ @running = true
164
+ while @running
165
+ prepare_beanstalk_server! unless @server
166
+ # Same socket for both in and out stream.
167
+ serve_stream! @server, @server
168
+ end
169
+ end
170
+ self
171
+ ensure
172
+ _server_close!
173
+ end
174
+ alias :run_server! :run_beanstalk_server!
175
+
176
+ def serve_stream! in_stream, out_stream
177
+ while @running
178
+ begin
179
+ serve_stream_message! in_stream, out_stream
180
+ rescue ::Exception => exc
181
+ _log [ :serve_stream_error, exc ]
182
+ @running = false
183
+ end
184
+ end
185
+ self
186
+ ensure
187
+ _server_close!
188
+ end
189
+
190
+ def start_beanstalkd!
191
+ _log { "run_beanstalkd! #{uri}" } if @verbose >= 1
192
+ raise "already running #{@beanstalkd_pid}" if @beanstalkd_pid
193
+ addr = @address ? "-l #{@address} " : ""
194
+ cmd = "beanstalkd #{addr}-p #{port}"
195
+ @beanstalkd_pid = Process.fork do
196
+ _log { "Start beanstalkd: #{cmd} ..." } if @verbose >= 1
197
+ exec(cmd)
198
+ raise "exec #{cmd.inspect} failed"
199
+ end
200
+ _log { "Start beanstalkd: #{cmd} pid=#{@beanstalkd_pid.inspect}" } if @verbose >= 2
201
+ self
202
+ end
203
+
204
+ def stop_beanstalkd!
205
+ _log { "stop_beanstalkd! #{uri} pid=#{@beanstalkd_pid.inspect}" } if @verbose >= 1
206
+ Process.kill 'TERM', @beanstalkd_pid
207
+ Process.waitpid @beanstalkd_pid
208
+ self
209
+ ensure
210
+ @beanstalkd_pid = nil
211
+ end
212
+
213
+ end
214
+ # !SLIDE END
215
+ end # class
216
+ end # module
217
+