msgpack-rpc 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ #
2
+ # MessagePack-RPC for Ruby
3
+ #
4
+ # Copyright (C) 2010 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module MessagePack
19
+ module RPC
20
+
21
+
22
+ Loop = Rev::Loop
23
+
24
+
25
+ module LoopUtil
26
+ attr_reader :loop
27
+
28
+ class Timer < Rev::TimerWatcher
29
+ def initialize(interval, repeating, &block)
30
+ @block = block
31
+ super(interval, repeating)
32
+ end
33
+ def on_timer
34
+ @block.call
35
+ end
36
+ end
37
+
38
+ def start_timer(interval, repeating, &block)
39
+ @loop.attach Timer.new(interval, repeating, &block)
40
+ end
41
+
42
+ class TaskQueue < Rev::AsyncWatcher
43
+ def initialize
44
+ @queue = []
45
+ super
46
+ end
47
+
48
+ def push(task)
49
+ @queue.push(task)
50
+ signal
51
+ end
52
+
53
+ def on_signal
54
+ while task = @queue.shift
55
+ begin
56
+ task.call
57
+ rescue
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def submit(task = nil, &block)
64
+ task ||= block
65
+ unless @queue
66
+ @queue = TaskQueue.new
67
+ @loop.attach(@queue)
68
+ end
69
+ @queue.push(task)
70
+ end
71
+
72
+ def run
73
+ @loop.run
74
+ end
75
+
76
+ def stop
77
+ @queue.detach if @queue && @queue.attached?
78
+ @loop.stop
79
+ # attach dummy timer
80
+ @loop.attach Rev::TimerWatcher.new(0, false)
81
+ nil
82
+ end
83
+ end
84
+
85
+
86
+ end
87
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # MessagePack-RPC for Ruby
3
+ #
4
+ # Copyright (C) 2010 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module MessagePack
19
+ module RPC
20
+
21
+
22
+ REQUEST = 0 # [0, msgid, method, param]
23
+ RESPONSE = 1 # [1, msgid, error, result]
24
+ NOTIFY = 2 # [2, method, param]
25
+
26
+ NO_METHOD_ERROR = 0x01;
27
+ ARGUMENT_ERROR = 0x02;
28
+
29
+
30
+ end
31
+ end
@@ -0,0 +1,111 @@
1
+ #
2
+ # MessagePack-RPC for Ruby
3
+ #
4
+ # Copyright (C) 2010 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module MessagePack
19
+ module RPC
20
+
21
+
22
+ # Server is usable for RPC server.
23
+ # Note that Server is a SessionPool.
24
+ class Server < SessionPool
25
+ # 1. initialize(builder, loop = Loop.new)
26
+ # 2. initialize(loop = Loop.new)
27
+ def initialize(arg1=nil, arg2=nil)
28
+ super(arg1, arg2)
29
+ @dispatcher = nil
30
+ @listeners = []
31
+ end
32
+
33
+ def serve(obj, accept = obj.public_methods)
34
+ @dispatcher = ObjectDispatcher.new(obj, accept)
35
+ self
36
+ end
37
+
38
+ # 1. listen(listener, obj = nil, accept = obj.public_methods)
39
+ # 2. listen(host, port, obj = nil, accept = obj.public_methods)
40
+ def listen(arg1, arg2 = nil, arg3 = nil, arg4 = nil)
41
+ if arg1.respond_to?(:listen)
42
+ # 1.
43
+ listener = arg1
44
+ obj = arg2
45
+ accept = arg3 || obj.public_methods
46
+ else
47
+ # 2.
48
+ listener = TCPServerTransport.new(Address.new(arg1,arg2))
49
+ obj = arg3
50
+ accept = arg4 || obj.public_methods
51
+ end
52
+
53
+ unless obj.nil?
54
+ serve(obj, accept)
55
+ end
56
+
57
+ listener.listen(self)
58
+ @listeners.push(listener)
59
+ nil
60
+ end
61
+
62
+ def close
63
+ @listeners.reject! {|listener|
64
+ listener.close
65
+ true
66
+ }
67
+ super
68
+ end
69
+
70
+ # from ServerTransport
71
+ def on_request(sendable, msgid, method, param) #:nodoc:
72
+ responder = Responder.new(sendable, msgid)
73
+ @dispatcher.dispatch_request(self, method, param, responder)
74
+ end
75
+
76
+ # from ServerTransport
77
+ def on_notify(method, param) #:nodoc:
78
+ @dispatcher.dispatch_notify(self, method, param)
79
+ end
80
+ end
81
+
82
+
83
+ class Responder
84
+ def initialize(sendable, msgid)
85
+ @sendable = sendable # send_message method is required
86
+ @msgid = msgid
87
+ end
88
+
89
+ def result(retval, err = nil)
90
+ data = [RESPONSE, @msgid, err, retval].to_msgpack
91
+ @sendable.send_data(data)
92
+ end
93
+
94
+ def error(err, retval = nil)
95
+ result(retval, err)
96
+ end
97
+ end
98
+
99
+
100
+ #:nodoc:
101
+ class Server::Base
102
+ def initialize(*args)
103
+ @base = Server.new(*args)
104
+ @base.serve(self)
105
+ end
106
+ attr_reader :base
107
+ end
108
+
109
+
110
+ end
111
+ end
@@ -0,0 +1,157 @@
1
+ #
2
+ # MessagePack-RPC for Ruby
3
+ #
4
+ # Copyright (C) 2010 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module MessagePack
19
+ module RPC
20
+
21
+
22
+ # Session is a abstract class corresponds with a remote host.
23
+ # You can call remote method using call, call_async, callback or notify method.
24
+ class Session
25
+ def initialize(builder, address, loop)
26
+ @address = address
27
+ @loop = loop
28
+ @reqtable = {}
29
+ @timeout = 10 # FIXME default timeout time
30
+ @seqid = 0
31
+ @transport = builder.build_transport(self, address)
32
+ end
33
+ attr_reader :loop, :address
34
+
35
+ # Sets and gets timeout in seconds.
36
+ attr_accessor :timeout
37
+
38
+ # backward compatibility
39
+ def port #:nodoc:
40
+ @address.port;
41
+ end
42
+
43
+ # backward compatibility
44
+ def host #:nodoc:
45
+ @address.host;
46
+ end
47
+
48
+ # call-seq:
49
+ # call(symbol, *args) -> result of remote method
50
+ #
51
+ # Calls remote method.
52
+ # This method is same as call_async(method, *args).get
53
+ def call(method, *args)
54
+ call_async(method, *args).get
55
+ end
56
+
57
+ # call-seq:
58
+ # call_async(symbol, *args) -> Future
59
+ #
60
+ # Calls remote method asynchronously.
61
+ # This method is non-blocking and returns Future.
62
+ def call_async(method, *args)
63
+ future = send_request(method, args)
64
+ end
65
+
66
+ # backward compatibility
67
+ alias send call_async #:nodoc:
68
+
69
+ # call-seq:
70
+ # callback(symbol, *args) {|future| }
71
+ #
72
+ # Calls remote method asynchronously.
73
+ # The callback method is called with Future when the result is reached.
74
+ # This method is same as call_async(method, *args).attach_callback {|future| }
75
+ def callback(method, *args, &block)
76
+ future = send_request(method, args)
77
+ future.attach_callback(block)
78
+ future
79
+ end
80
+
81
+ # call-seq:
82
+ # notify(symbol, *args) -> nil
83
+ #
84
+ # Calls remote method with NOTIFY protocol.
85
+ # It doesn't require server to return results.
86
+ # This method is non-blocking and returns nil.
87
+ def notify(method, *args)
88
+ send_notify(method, args)
89
+ nil
90
+ end
91
+
92
+ # Closes underlaying Transport and destroy resources.
93
+ def close
94
+ @transport.close
95
+ @reqtable = {}
96
+ @seqid = 0
97
+ self
98
+ end
99
+
100
+ # from ClientTransport
101
+ def on_response(sock, msgid, error, result) #:nodoc:
102
+ if future = @reqtable.delete(msgid)
103
+ future.set_result(error, result)
104
+ end
105
+ end
106
+
107
+ # from ClientTransport
108
+ def on_connect_failed #:nodoc:
109
+ @reqtable.reject! {|msgid, future|
110
+ begin
111
+ future.set_result ConnectError.new, nil
112
+ rescue
113
+ end
114
+ true
115
+ }
116
+ nil
117
+ end
118
+
119
+ # from Client, SessionPool
120
+ def step_timeout #:nodoc:
121
+ timedout = []
122
+ @reqtable.reject! {|msgid, future|
123
+ if future.step_timeout
124
+ timedout.push(future)
125
+ true
126
+ end
127
+ }
128
+ timedout.each {|future|
129
+ begin
130
+ future.set_result TimeoutError.new, nil
131
+ rescue
132
+ end
133
+ }
134
+ !@reqtable.empty?
135
+ end
136
+
137
+ private
138
+ def send_request(method, param)
139
+ method = method.to_s
140
+ msgid = @seqid
141
+ @seqid += 1; if @seqid >= 1<<31 then @seqid = 0 end
142
+ data = [REQUEST, msgid, method, param].to_msgpack
143
+ @transport.send_data(data)
144
+ @reqtable[msgid] = Future.new(self, @loop)
145
+ end
146
+
147
+ def send_notify(method, param)
148
+ method = method.to_s
149
+ data = [NOTIFY, method, param].to_msgpack
150
+ @transport.send_data(data)
151
+ nil
152
+ end
153
+ end
154
+
155
+
156
+ end
157
+ end
@@ -0,0 +1,91 @@
1
+ #
2
+ # MessagePack-RPC for Ruby
3
+ #
4
+ # Copyright (C) 2010 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module MessagePack
19
+ module RPC
20
+
21
+
22
+ # SessionPool is usable for connection pooling.
23
+ # You can get pooled Session using get_session method.
24
+ # Note that SessionPool includes LoopUtil.
25
+ class SessionPool
26
+ # 1. initialize(builder, loop = Loop.new)
27
+ # 2. initialize(loop = Loop.new)
28
+ #
29
+ # Creates an SessionPool.
30
+ def initialize(arg1=nil, arg2=nil)
31
+ if arg1.respond_to?(:build_transport)
32
+ # 1.
33
+ builder = arg1
34
+ loop = arg2 || Loop.new
35
+ else
36
+ # 2.
37
+ builder = TCPTransport.new
38
+ loop = arg1 || Loop.new
39
+ end
40
+
41
+ @builder = builder
42
+ @loop = loop
43
+ @pool = {}
44
+
45
+ @timer = Timer.new(1, true, &method(:step_timeout))
46
+ loop.attach(@timer)
47
+ end
48
+
49
+ # 1. get_session(address)
50
+ # 2. get_session(host, port)
51
+ #
52
+ # Returns pooled Session.
53
+ # If there are no pooled Session for the specified address,
54
+ # this method creates the Session and pools it.
55
+ def get_session(arg1, arg2=nil)
56
+ if arg2.nil?
57
+ # 1.
58
+ addr = arg1
59
+ else
60
+ # 2.
61
+ host = arg1
62
+ port = arg2
63
+ addr = Address.new(host, port)
64
+ end
65
+
66
+ @pool[addr] ||= Session.new(@builder, addr, @loop)
67
+ end
68
+
69
+ # backward compatibility
70
+ alias get_session_addr get_session #:nodoc:
71
+
72
+ def close
73
+ @pool.reject! {|addr, s|
74
+ s.close
75
+ true
76
+ }
77
+ @timer.detach if @timer.attached?
78
+ nil
79
+ end
80
+
81
+ include LoopUtil
82
+
83
+ private
84
+ def step_timeout
85
+ @pool.each_pair {|addr,s| s.step_timeout }
86
+ end
87
+ end
88
+
89
+
90
+ end
91
+ end