asir 0.2.0

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.
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
+