sync_service 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +5 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/Gemfile +13 -0
- data/Guardfile +5 -0
- data/README.md +66 -0
- data/Rakefile +29 -0
- data/examples/application.rb +17 -0
- data/examples/client.rb +29 -0
- data/examples/config.ru +7 -0
- data/examples/php/client.php +17 -0
- data/examples/php/jsonRPCClient.php +165 -0
- data/examples/python/README +5 -0
- data/examples/python/client.py +16 -0
- data/examples/server.rb +4 -0
- data/lib/mobme/infrastructure/rpc/adaptor.rb +31 -0
- data/lib/mobme/infrastructure/rpc/base.rb +15 -0
- data/lib/mobme/infrastructure/rpc/error.rb +3 -0
- data/lib/mobme/infrastructure/rpc/runner.rb +21 -0
- data/lib/mobme/infrastructure/rpc/version.rb +14 -0
- data/lib/rpc/.gitignore +2 -0
- data/lib/rpc/CHANGELOG +10 -0
- data/lib/rpc/Gemfile +19 -0
- data/lib/rpc/LICENSE +20 -0
- data/lib/rpc/README.textile +7 -0
- data/lib/rpc/examples/em-http-request-json/client.rb +39 -0
- data/lib/rpc/examples/helpers.rb +15 -0
- data/lib/rpc/examples/net-http-json/client.rb +34 -0
- data/lib/rpc/examples/net-http-json/console.rb +13 -0
- data/lib/rpc/examples/server.ru +42 -0
- data/lib/rpc/examples/socket-json/client.rb +36 -0
- data/lib/rpc/examples/socket-json/server.rb +41 -0
- data/lib/rpc/lib/rpc.rb +166 -0
- data/lib/rpc/lib/rpc/clients/amqp/coolio.rb +0 -0
- data/lib/rpc/lib/rpc/clients/amqp/eventmachine.rb +0 -0
- data/lib/rpc/lib/rpc/clients/amqp/socket.rb +0 -0
- data/lib/rpc/lib/rpc/clients/em-http-request.rb +58 -0
- data/lib/rpc/lib/rpc/clients/net-http.rb +55 -0
- data/lib/rpc/lib/rpc/clients/redis.rb +0 -0
- data/lib/rpc/lib/rpc/clients/socket.rb +50 -0
- data/lib/rpc/lib/rpc/encoders/json.rb +142 -0
- data/lib/rpc/lib/rpc/encoders/xml.rb +0 -0
- data/lib/rpc/rpc.gemspec +34 -0
- data/lib/sync_service.rb +20 -0
- data/spec/adaptor_spec.rb +104 -0
- data/spec/base_spec.rb +61 -0
- data/spec/error_spec.rb +14 -0
- data/spec/runner_spec.rb +31 -0
- data/spec/spec_helper.rb +9 -0
- data/sync_service.gemspec +32 -0
- metadata +218 -0
data/.autotest
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
3
|
+
sync_service is a library to create synchronous SOA daemons. It's built on top of the JSON/RPC protocol and can be deployed anywhere you deploy a Rack application.
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
Requires ruby-1.9.3p0.
|
8
|
+
|
9
|
+
Then do:
|
10
|
+
|
11
|
+
$ gem install sync_service
|
12
|
+
|
13
|
+
## Hosting a Service
|
14
|
+
|
15
|
+
A service is just any Ruby object that descends from <code>SyncService::Base</code>. Any public method in the object is automatically exposed via SOA.
|
16
|
+
|
17
|
+
|
18
|
+
require "sync_service"
|
19
|
+
|
20
|
+
class Application < SyncService::Base
|
21
|
+
@service_name = "mobme.infrastructure.rpc.test"
|
22
|
+
|
23
|
+
def server_timestamp
|
24
|
+
Time.now.to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def buggy_method
|
28
|
+
raise MobME::Infrastructure::RPC::Error, "This exception is expected."
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(name, *args)
|
32
|
+
logger.err "[SERVER] received method #{name} with #{args.inspect}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
To expose the Application, you can either create a standalone server and run it via <code>SyncService::Runner</code>:
|
37
|
+
|
38
|
+
require 'sync_service'
|
39
|
+
require_relative 'application'
|
40
|
+
|
41
|
+
SyncService::Runner.start Application.new, '0.0.0.0', 8080, '/test_application'
|
42
|
+
|
43
|
+
or make a simple config.ru via SyncService::Adaptor:
|
44
|
+
|
45
|
+
require 'sync_service'
|
46
|
+
require Pathname.new(File.dirname(__FILE__)).join('application')
|
47
|
+
|
48
|
+
map("/test_application") do
|
49
|
+
run SyncService::Adaptor.new(Application.new)
|
50
|
+
end
|
51
|
+
|
52
|
+
## Consuming a Service
|
53
|
+
|
54
|
+
To consume services, use <code>SyncService::Client</code>:
|
55
|
+
|
56
|
+
require "sync_service"
|
57
|
+
RPC.logging = true
|
58
|
+
|
59
|
+
client = SyncService::Client.setup("http://127.0.0.1:8080/test_application")
|
60
|
+
|
61
|
+
# Get result of an existing method.
|
62
|
+
puts "Server timestamp is #{client.server_timestamp}"
|
63
|
+
|
64
|
+
|
65
|
+
You can see the complete example in the examples/ folder.
|
66
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require "rake/tasklib"
|
3
|
+
require "flog"
|
4
|
+
require 'ci/reporter/rake/rspec'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec => ["ci:setup:rspec"]) do |t|
|
7
|
+
t.pattern = 'spec/**/*_spec.rb'
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :spec
|
11
|
+
|
12
|
+
desc "Analyze for code complexity"
|
13
|
+
task :flog do
|
14
|
+
flog = Flog.new
|
15
|
+
flog.flog [ "lib" ]
|
16
|
+
threshold = 10
|
17
|
+
|
18
|
+
bad_methods = flog.totals.select do | name, score |
|
19
|
+
name != "main#none" && score > threshold
|
20
|
+
end
|
21
|
+
bad_methods.sort do | a, b |
|
22
|
+
a[ 1 ] <=> b[ 1 ]
|
23
|
+
end.reverse.each do | name, score |
|
24
|
+
puts "%8.1f: %s" % [ score, name ]
|
25
|
+
end
|
26
|
+
unless bad_methods.empty?
|
27
|
+
raise "#{ bad_methods.size } methods have a flog complexity > #{ threshold }"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "sync_service"
|
2
|
+
|
3
|
+
class Application < SyncService::Base
|
4
|
+
@service_name = "mobme.infrastructure.rpc.test"
|
5
|
+
|
6
|
+
def server_timestamp
|
7
|
+
Time.now.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def buggy_method
|
11
|
+
raise MobME::Infrastructure::RPC::Error, "This exception is expected."
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(name, *args)
|
15
|
+
logger.err "[SERVER] received method #{name} with #{args.inspect}"
|
16
|
+
end
|
17
|
+
end
|
data/examples/client.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "sync_service"
|
2
|
+
|
3
|
+
RPC.logging = true
|
4
|
+
|
5
|
+
client = SyncService::Client.setup("http://127.0.0.1:8080/test_application")
|
6
|
+
|
7
|
+
# Get result of an existing method.
|
8
|
+
puts "Server timestamp is #{client.server_timestamp}"
|
9
|
+
|
10
|
+
# Get result of a non-existing method via method_missing.
|
11
|
+
puts "Method missing works: #{client + 1}"
|
12
|
+
|
13
|
+
# Synchronous error handling.
|
14
|
+
begin
|
15
|
+
client.buggy_method
|
16
|
+
rescue MobME::Infrastructure::RPC::Error => exception
|
17
|
+
STDERR.puts "EXCEPTION CAUGHT: #{exception.inspect}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Notification isn't supported, because HTTP works in
|
21
|
+
# request/response mode, so it does behave in the same
|
22
|
+
# manner as rpc via method_missing. Sense of this is
|
23
|
+
# only to check, that it won't blow up.
|
24
|
+
puts "Sending a notification ..."
|
25
|
+
client.notification(:log, "Some shit.")
|
26
|
+
|
27
|
+
# Batch.
|
28
|
+
result = client.batch([[:log, ["Message"], nil], [:a_method, []]])
|
29
|
+
puts "Batch result: #{result}"
|
data/examples/config.ru
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
<?php
|
2
|
+
require_once 'jsonRPCClient.php';
|
3
|
+
$client = new jsonRPCClient('http://127.0.0.1:8080/test_application');
|
4
|
+
|
5
|
+
// Get result of an existing method.
|
6
|
+
echo 'Server timestamp is '. $client->server_timestamp() . "\n";
|
7
|
+
|
8
|
+
// Get result of a non-existing method via method_missing.
|
9
|
+
echo 'Method missing works: ' . $client->plus(1) . "\n";
|
10
|
+
|
11
|
+
// Synchronous error handling.
|
12
|
+
try {
|
13
|
+
$client->buggy_method();
|
14
|
+
} catch (Exception $e) {
|
15
|
+
echo 'EXCEPTION CAUGHT: ' . nl2br($e->getMessage()) . "\n";
|
16
|
+
}
|
17
|
+
?>
|
@@ -0,0 +1,165 @@
|
|
1
|
+
<?php
|
2
|
+
/*
|
3
|
+
COPYRIGHT
|
4
|
+
|
5
|
+
Copyright 2007 Sergio Vaccaro <sergio@inservibile.org>
|
6
|
+
|
7
|
+
This file is part of JSON-RPC PHP.
|
8
|
+
|
9
|
+
JSON-RPC PHP is free software; you can redistribute it and/or modify
|
10
|
+
it under the terms of the GNU General Public License as published by
|
11
|
+
the Free Software Foundation; either version 2 of the License, or
|
12
|
+
(at your option) any later version.
|
13
|
+
|
14
|
+
JSON-RPC PHP is distributed in the hope that it will be useful,
|
15
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
GNU General Public License for more details.
|
18
|
+
|
19
|
+
You should have received a copy of the GNU General Public License
|
20
|
+
along with JSON-RPC PHP; if not, write to the Free Software
|
21
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
22
|
+
*/
|
23
|
+
|
24
|
+
/**
|
25
|
+
* The object of this class are generic jsonRPC 1.0 clients
|
26
|
+
* http://json-rpc.org/wiki/specification
|
27
|
+
*
|
28
|
+
* @author sergio <jsonrpcphp@inservibile.org>
|
29
|
+
*/
|
30
|
+
class jsonRPCClient {
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Debug state
|
34
|
+
*
|
35
|
+
* @var boolean
|
36
|
+
*/
|
37
|
+
private $debug;
|
38
|
+
|
39
|
+
/**
|
40
|
+
* The server URL
|
41
|
+
*
|
42
|
+
* @var string
|
43
|
+
*/
|
44
|
+
private $url;
|
45
|
+
/**
|
46
|
+
* The request id
|
47
|
+
*
|
48
|
+
* @var integer
|
49
|
+
*/
|
50
|
+
private $id;
|
51
|
+
/**
|
52
|
+
* If true, notifications are performed instead of requests
|
53
|
+
*
|
54
|
+
* @var boolean
|
55
|
+
*/
|
56
|
+
private $notification = false;
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Takes the connection parameters
|
60
|
+
*
|
61
|
+
* @param string $url
|
62
|
+
* @param boolean $debug
|
63
|
+
*/
|
64
|
+
public function __construct($url,$debug = false) {
|
65
|
+
// server URL
|
66
|
+
$this->url = $url;
|
67
|
+
// proxy
|
68
|
+
empty($proxy) ? $this->proxy = '' : $this->proxy = $proxy;
|
69
|
+
// debug state
|
70
|
+
empty($debug) ? $this->debug = false : $this->debug = true;
|
71
|
+
// message id
|
72
|
+
$this->id = 1;
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Sets the notification state of the object. In this state, notifications are performed, instead of requests.
|
77
|
+
*
|
78
|
+
* @param boolean $notification
|
79
|
+
*/
|
80
|
+
public function setRPCNotification($notification) {
|
81
|
+
empty($notification) ?
|
82
|
+
$this->notification = false
|
83
|
+
:
|
84
|
+
$this->notification = true;
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Performs a jsonRCP request and gets the results as an array
|
89
|
+
*
|
90
|
+
* @param string $method
|
91
|
+
* @param array $params
|
92
|
+
* @return array
|
93
|
+
*/
|
94
|
+
public function __call($method,$params) {
|
95
|
+
|
96
|
+
// check
|
97
|
+
if (!is_scalar($method)) {
|
98
|
+
throw new Exception('Method name has no scalar value');
|
99
|
+
}
|
100
|
+
|
101
|
+
// check
|
102
|
+
if (is_array($params)) {
|
103
|
+
// no keys
|
104
|
+
$params = array_values($params);
|
105
|
+
} else {
|
106
|
+
throw new Exception('Params must be given as array');
|
107
|
+
}
|
108
|
+
|
109
|
+
// sets notification or request task
|
110
|
+
if ($this->notification) {
|
111
|
+
$currentId = NULL;
|
112
|
+
} else {
|
113
|
+
$currentId = $this->id;
|
114
|
+
}
|
115
|
+
|
116
|
+
// prepares the request
|
117
|
+
$request = array(
|
118
|
+
'method' => $method,
|
119
|
+
'params' => $params,
|
120
|
+
'id' => $currentId
|
121
|
+
);
|
122
|
+
$request = json_encode($request);
|
123
|
+
$this->debug && $this->debug.='***** Request *****'."\n".$request."\n".'***** End Of request *****'."\n\n";
|
124
|
+
|
125
|
+
// performs the HTTP POST
|
126
|
+
$opts = array ('http' => array (
|
127
|
+
'method' => 'POST',
|
128
|
+
'header' => 'Content-type: application/json',
|
129
|
+
'content' => $request
|
130
|
+
));
|
131
|
+
$context = stream_context_create($opts);
|
132
|
+
if ($fp = fopen($this->url, 'r', false, $context)) {
|
133
|
+
$response = '';
|
134
|
+
while($row = fgets($fp)) {
|
135
|
+
$response.= trim($row)."\n";
|
136
|
+
}
|
137
|
+
$this->debug && $this->debug.='***** Server response *****'."\n".$response.'***** End of server response *****'."\n";
|
138
|
+
$response = json_decode($response,true);
|
139
|
+
} else {
|
140
|
+
throw new Exception('Unable to connect to '.$this->url);
|
141
|
+
}
|
142
|
+
|
143
|
+
// debug output
|
144
|
+
if ($this->debug) {
|
145
|
+
echo nl2br($debug);
|
146
|
+
}
|
147
|
+
|
148
|
+
// final checks and return
|
149
|
+
if (!$this->notification) {
|
150
|
+
// check
|
151
|
+
if ($response['id'] != $currentId) {
|
152
|
+
throw new Exception('Incorrect response id (request id: '.$currentId.', response id: '.$response['id'].')');
|
153
|
+
}
|
154
|
+
if (!is_null($response['error'])) {
|
155
|
+
throw new Exception('Request error: '.$response['error']);
|
156
|
+
}
|
157
|
+
|
158
|
+
return $response['result'];
|
159
|
+
|
160
|
+
} else {
|
161
|
+
return true;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
?>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from jsonrpc import ServiceProxy
|
2
|
+
|
3
|
+
client = ServiceProxy("http://127.0.0.1:8080/test_application")
|
4
|
+
|
5
|
+
# Get result of an existing method.
|
6
|
+
print 'Server timestamp is ' + str(client.server_timestamp())
|
7
|
+
|
8
|
+
# Get result of a non-existing method via method_missing.
|
9
|
+
print 'Method missing works: ' + client.plus(1)
|
10
|
+
|
11
|
+
# Synchronous error handling.
|
12
|
+
try:
|
13
|
+
client.buggy_method()
|
14
|
+
except Exception, e:
|
15
|
+
print 'EXCEPTION CAUGHT: ' + e.error['message']
|
16
|
+
|
data/examples/server.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module MobME::Infrastructure::RPC
|
2
|
+
class Adaptor
|
3
|
+
def initialize(service_object)
|
4
|
+
@service_object = service_object
|
5
|
+
end
|
6
|
+
|
7
|
+
def server
|
8
|
+
@server ||= RPC::Server.new(@service_object)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
request = Rack::Request.new(env)
|
13
|
+
command = request.body.read
|
14
|
+
binary = server.execute(command)
|
15
|
+
|
16
|
+
if binary.match(/NoMethodError/)
|
17
|
+
response(404, binary)
|
18
|
+
else
|
19
|
+
response(200, binary)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def response(status, body)
|
24
|
+
headers = {
|
25
|
+
"Content-Type" => "application/json-rpc",
|
26
|
+
"Content-Length" => body.bytesize.to_s
|
27
|
+
}
|
28
|
+
[status, headers, [body]]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|