foreign_actor 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +17 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/README.md +44 -0
- data/Rakefile +27 -0
- data/bin/device +75 -0
- data/bin/puppetmaster.rb +100 -0
- data/docs/design.graphml +221 -0
- data/docs/design.png +0 -0
- data/example/Procfile +2 -0
- data/example/client.rb +27 -0
- data/example/common.rb +10 -0
- data/example/device.yml +7 -0
- data/example/worker.rb +42 -0
- data/example2/Procfile +2 -0
- data/example2/README.md +9 -0
- data/example2/device.yml +4 -0
- data/example2/node.rb +59 -0
- data/foreign_actor.gemspec +20 -0
- data/lib/foreign_actor/client.rb +90 -0
- data/lib/foreign_actor/reactor.rb +275 -0
- data/lib/foreign_actor/reactor_mailbox.rb +29 -0
- data/lib/foreign_actor/serializer.rb +16 -0
- data/lib/foreign_actor/version.rb +3 -0
- data/lib/foreign_actor.rb +15 -0
- data/specs/spec_helper.rb +27 -0
- data/specs/unit/reactor_spec.rb +125 -0
- data/specs/unit/serializer_spec.rb +17 -0
- metadata +129 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'rake'
|
6
|
+
gem 'celluloid', git: 'git://github.com/celluloid/celluloid.git'
|
7
|
+
|
8
|
+
group(:test) do
|
9
|
+
gem 'eetee', '= 0.0.1'
|
10
|
+
gem 'mocha', '~> 0.12.0'
|
11
|
+
gem 'factory_girl'
|
12
|
+
|
13
|
+
gem 'simplecov'
|
14
|
+
gem 'guard', '~> 1.5.4'
|
15
|
+
gem 'rb-fsevent'
|
16
|
+
gem 'growl'
|
17
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
|
2
|
+
# parameters:
|
3
|
+
# output => the formatted to use
|
4
|
+
# backtrace => number of lines, nil = everything
|
5
|
+
guard 'bacon', :output => "BetterOutput", :backtrace => nil do
|
6
|
+
watch(%r{^lib/foreign_actor/(.+)\.rb$}) { |m| "specs/unit/#{m[1]}_spec.rb" }
|
7
|
+
watch(%r{specs/.+\.rb$})
|
8
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Julien Ammous
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# What is this ?
|
2
|
+
|
3
|
+
I wanted to combine two great libraries for concurrent work: Crossroads (zeromq fork) and Celluloid.
|
4
|
+
Foreign Actor is my attempt at building a simple way to distribute celluloid actors
|
5
|
+
in multiple processes/machines, the idea is to be able to move an actor to another process with no change to the actor itself and minimal changes on the server and client side code.
|
6
|
+
|
7
|
+
|
8
|
+
# How ?
|
9
|
+
I used the Crossroads library (zeromq fork) to achieve my goal using XREQ/XREP sockets, this allows multiple clients with multiple workers, each clients being able to make calls with
|
10
|
+
or without a return value.
|
11
|
+
|
12
|
+
Here is what it looks like:
|
13
|
+
![](docs/design.png "Architecture")
|
14
|
+
|
15
|
+
In this picture the square boxes are processes while the rounded box are the services (actors).
|
16
|
+
In a standard client/server interaction one client connects to one or more server and to do so
|
17
|
+
needs to know the addresses of all of them, in my case the device process is here to simplify this: both clients and workers connects to it which allows as a direct result things like adding
|
18
|
+
a worker in live, you just start it, it connects to the device and it can starts to process requests immediately.
|
19
|
+
Something to note is that each of these processes can run on a different physical machine
|
20
|
+
allowing effectively to distribute jobs on multiple cores as well as multiple machines.
|
21
|
+
|
22
|
+
|
23
|
+
# Constraints
|
24
|
+
Since the arguments to the actions are serialized using MessagePack they need to be serializable by it, which means that you can only send basic types to a foreign actor but messagepack allows everything you need really:
|
25
|
+
- Integers
|
26
|
+
- Floats (ex: 2.34)
|
27
|
+
- Strings (ex: "a cat")
|
28
|
+
- Hash
|
29
|
+
- Boolean
|
30
|
+
- Nil
|
31
|
+
- Array (ex: [1, "a cat", {"a" : nil}])
|
32
|
+
|
33
|
+
Why not going with Marshal ? Because I don't want to close the door to another language, you
|
34
|
+
can well imagine calling a service which is in fact provided bya C server, a python server or
|
35
|
+
anything else, I just see no reason to close that door.
|
36
|
+
|
37
|
+
|
38
|
+
# Examples
|
39
|
+
You can look at the example and example2 folder.
|
40
|
+
|
41
|
+
|
42
|
+
# TODO
|
43
|
+
- handle workers crash better, the client should be informed instead of waiting for a timeout.
|
44
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
task :test do
|
8
|
+
|
9
|
+
# do not generate coverage report under travis
|
10
|
+
# unless ENV['TRAVIS']
|
11
|
+
|
12
|
+
# require 'simplecov'
|
13
|
+
# SimpleCov.command_name "E.T."
|
14
|
+
# SimpleCov.start do
|
15
|
+
# add_filter ".*_spec"
|
16
|
+
# add_filter "/helpers/"
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
|
20
|
+
require 'eetee'
|
21
|
+
|
22
|
+
runner = EEtee::Runner.new
|
23
|
+
runner.run_pattern('specs/**/*_spec.rb')
|
24
|
+
runner.report_results()
|
25
|
+
|
26
|
+
end
|
27
|
+
|
data/bin/device
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ffi-rxs'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
context = XS::Context.new
|
7
|
+
poller = XS::Poller.new
|
8
|
+
|
9
|
+
path = nil
|
10
|
+
|
11
|
+
if ARGV.size == 0
|
12
|
+
path = 'device.yml'
|
13
|
+
elsif ARGV
|
14
|
+
path = ARGV[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
unless path && File.exists?(path)
|
18
|
+
puts "Usage: #{File.basename($0)} <config_path>"
|
19
|
+
exit(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Using #{path}"
|
23
|
+
|
24
|
+
conf = YAML.load_file(path)
|
25
|
+
endpoints = []
|
26
|
+
|
27
|
+
Pipe = Struct.new(:front, :back) do
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
conf.each do |name, paths|
|
32
|
+
frontend = context.socket(XS::XREP)
|
33
|
+
backend = context.socket(XS::XREQ)
|
34
|
+
|
35
|
+
frontend.bind(paths['front'])
|
36
|
+
backend.bind(paths['back'])
|
37
|
+
|
38
|
+
poller.register_readable(frontend)
|
39
|
+
poller.register_readable(backend)
|
40
|
+
|
41
|
+
|
42
|
+
endpoints << Pipe.new(frontend, backend)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
trap('INT'){ exit }
|
47
|
+
trap('TERM'){ exit }
|
48
|
+
|
49
|
+
loop do
|
50
|
+
poller.poll()
|
51
|
+
poller.readables.each do |socket|
|
52
|
+
endpoints.each do |p|
|
53
|
+
if socket === p.front
|
54
|
+
loop do
|
55
|
+
socket.recv_string(message = "")
|
56
|
+
more = socket.more_parts?
|
57
|
+
p.back.send_string(message, more ? XS::SNDMORE : 0)
|
58
|
+
break unless more
|
59
|
+
end
|
60
|
+
next
|
61
|
+
|
62
|
+
elsif socket === p.back
|
63
|
+
loop do
|
64
|
+
socket.recv_string(message = "")
|
65
|
+
more = socket.more_parts?
|
66
|
+
p.front.send_string(message, more ? XS::SNDMORE : 0)
|
67
|
+
break unless more
|
68
|
+
end
|
69
|
+
next
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
data/bin/puppetmaster.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
require 'ffi-rxs'
|
3
|
+
|
4
|
+
class PuppetMaster
|
5
|
+
def initialize(workers_endpoint, client_endpoint)
|
6
|
+
@context = XS::Context.new
|
7
|
+
@source_id = nil
|
8
|
+
|
9
|
+
@workers_socket = @context.socket(XS::XREQ)
|
10
|
+
@clients_socket = @context.socket(XS::XREP)
|
11
|
+
|
12
|
+
@workers_socket.bind(workers_endpoint)
|
13
|
+
@clients_socket.bind(client_endpoint)
|
14
|
+
|
15
|
+
@control_socket_srv = @context.socket(XS::PAIR)
|
16
|
+
@control_socket_cl = @context.socket(XS::PAIR)
|
17
|
+
|
18
|
+
addr = "inproc://ctrl"
|
19
|
+
@control_socket_srv.bind(addr)
|
20
|
+
@control_socket_cl.connect(addr)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def start()
|
25
|
+
trap('INT'){ @control_socket_cl.send_string('') }
|
26
|
+
trap('TERM'){ @control_socket_cl.send_string('') }
|
27
|
+
|
28
|
+
poller = XS::Poller.new
|
29
|
+
poller.register_readable(@workers_socket)
|
30
|
+
poller.register_readable(@clients_socket)
|
31
|
+
# control socket
|
32
|
+
poller.register_readable(@control_socket_srv)
|
33
|
+
|
34
|
+
exit = false
|
35
|
+
|
36
|
+
while exit == false
|
37
|
+
poller.poll
|
38
|
+
poller.readables.each do |s|
|
39
|
+
if s == @control_socket_srv
|
40
|
+
puts "Exiting..."
|
41
|
+
exit = true
|
42
|
+
else
|
43
|
+
redirect_data(s)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def redirect_data(from)
|
52
|
+
# read whole message
|
53
|
+
msg = receive_msg(from)
|
54
|
+
if msg
|
55
|
+
# store the source_id
|
56
|
+
if from == @clients_socket
|
57
|
+
@source_id = msg.shift
|
58
|
+
|
59
|
+
# send to the other side
|
60
|
+
handle_xs_err(@workers_socket, :send_string, msg[0], XS::DONTWAIT)
|
61
|
+
|
62
|
+
else
|
63
|
+
handle_xs_err(@clients_socket, :send_string, @source_id, XS::DONTWAIT | XS::SNDMORE)
|
64
|
+
handle_xs_err(@clients_socket, :send_string, msg[0], XS::DONTWAIT)
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def receive_msg(s)
|
73
|
+
parts = []
|
74
|
+
loop do
|
75
|
+
data = ""
|
76
|
+
if s.recv_string(data, XS::DONTWAIT) == -1
|
77
|
+
raise "error while reading socket: #{}"
|
78
|
+
end
|
79
|
+
parts << data
|
80
|
+
break unless s.more_parts?
|
81
|
+
end
|
82
|
+
|
83
|
+
parts
|
84
|
+
|
85
|
+
rescue
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def handle_xs_err(s, method, *args)
|
90
|
+
rc = s.send(method, *args)
|
91
|
+
unless XS::Util.resultcode_ok?(rc)
|
92
|
+
raise "error: #{method}: #{XS::Util.error_string}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
puts "Started."
|
99
|
+
PuppetMaster.new('tcp://127.0.0.1:7001', 'tcp://127.0.0.1:7000').start
|
100
|
+
|
data/docs/design.graphml
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
3
|
+
<!--Created by yFiles for Java 2.8-->
|
4
|
+
<key for="graphml" id="d0" yfiles.type="resources"/>
|
5
|
+
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
6
|
+
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
7
|
+
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
8
|
+
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
9
|
+
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
10
|
+
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
11
|
+
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
|
12
|
+
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
13
|
+
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
14
|
+
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
15
|
+
<graph edgedefault="directed" id="G">
|
16
|
+
<data key="d7"/>
|
17
|
+
<node id="n0">
|
18
|
+
<data key="d5"/>
|
19
|
+
<data key="d6">
|
20
|
+
<y:ShapeNode>
|
21
|
+
<y:Geometry height="26.0" width="92.0" x="590.0" y="489.0"/>
|
22
|
+
<y:Fill hasColor="false" transparent="false"/>
|
23
|
+
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
24
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="21.853515625" x="35.0732421875" y="3.93359375">W1</y:NodeLabel>
|
25
|
+
<y:Shape type="roundrectangle"/>
|
26
|
+
</y:ShapeNode>
|
27
|
+
</data>
|
28
|
+
</node>
|
29
|
+
<node id="n1">
|
30
|
+
<data key="d5"/>
|
31
|
+
<data key="d6">
|
32
|
+
<y:ShapeNode>
|
33
|
+
<y:Geometry height="120.0" width="122.0" x="579.0" y="477.0"/>
|
34
|
+
<y:Fill hasColor="false" transparent="false"/>
|
35
|
+
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
36
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="sides" modelPosition="s" textColor="#000000" visible="true" width="45.15625" x="38.421875" y="124.0">Worker</y:NodeLabel>
|
37
|
+
<y:Shape type="rectangle"/>
|
38
|
+
</y:ShapeNode>
|
39
|
+
</data>
|
40
|
+
</node>
|
41
|
+
<node id="n2">
|
42
|
+
<data key="d5"/>
|
43
|
+
<data key="d6">
|
44
|
+
<y:ShapeNode>
|
45
|
+
<y:Geometry height="41.0" width="109.0" x="495.0" y="390.0"/>
|
46
|
+
<y:Fill hasColor="false" transparent="false"/>
|
47
|
+
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
48
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="42.185546875" x="33.4072265625" y="11.43359375">Device</y:NodeLabel>
|
49
|
+
<y:Shape type="rectangle"/>
|
50
|
+
</y:ShapeNode>
|
51
|
+
</data>
|
52
|
+
</node>
|
53
|
+
<node id="n3">
|
54
|
+
<data key="d5"/>
|
55
|
+
<data key="d6">
|
56
|
+
<y:ShapeNode>
|
57
|
+
<y:Geometry height="60.0" width="74.0" x="327.0" y="240.0"/>
|
58
|
+
<y:Fill hasColor="false" transparent="false"/>
|
59
|
+
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
60
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="37.861328125" x="18.0693359375" y="20.93359375">Client</y:NodeLabel>
|
61
|
+
<y:Shape type="rectangle"/>
|
62
|
+
</y:ShapeNode>
|
63
|
+
</data>
|
64
|
+
</node>
|
65
|
+
<node id="n4">
|
66
|
+
<data key="d5"/>
|
67
|
+
<data key="d6">
|
68
|
+
<y:ShapeNode>
|
69
|
+
<y:Geometry height="60.0" width="74.0" x="452.0" y="240.0"/>
|
70
|
+
<y:Fill hasColor="false" transparent="false"/>
|
71
|
+
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
72
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="37.861328125" x="18.0693359375" y="20.93359375">Client</y:NodeLabel>
|
73
|
+
<y:Shape type="rectangle"/>
|
74
|
+
</y:ShapeNode>
|
75
|
+
</data>
|
76
|
+
</node>
|
77
|
+
<node id="n5">
|
78
|
+
<data key="d5"/>
|
79
|
+
<data key="d6">
|
80
|
+
<y:ShapeNode>
|
81
|
+
<y:Geometry height="60.0" width="74.0" x="577.0" y="240.0"/>
|
82
|
+
<y:Fill hasColor="false" transparent="false"/>
|
83
|
+
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
84
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="37.861328125" x="18.0693359375" y="20.93359375">Client</y:NodeLabel>
|
85
|
+
<y:Shape type="rectangle"/>
|
86
|
+
</y:ShapeNode>
|
87
|
+
</data>
|
88
|
+
</node>
|
89
|
+
<node id="n6">
|
90
|
+
<data key="d5"/>
|
91
|
+
<data key="d6">
|
92
|
+
<y:ShapeNode>
|
93
|
+
<y:Geometry height="60.0" width="74.0" x="702.0" y="240.0"/>
|
94
|
+
<y:Fill hasColor="false" transparent="false"/>
|
95
|
+
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
96
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="37.861328125" x="18.0693359375" y="20.93359375">Client</y:NodeLabel>
|
97
|
+
<y:Shape type="rectangle"/>
|
98
|
+
</y:ShapeNode>
|
99
|
+
</data>
|
100
|
+
</node>
|
101
|
+
<node id="n7">
|
102
|
+
<data key="d5"/>
|
103
|
+
<data key="d6">
|
104
|
+
<y:ShapeNode>
|
105
|
+
<y:Geometry height="26.0" width="92.0" x="416.0" y="529.0"/>
|
106
|
+
<y:Fill hasColor="false" transparent="false"/>
|
107
|
+
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
108
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="21.853515625" x="35.0732421875" y="3.93359375">W2</y:NodeLabel>
|
109
|
+
<y:Shape type="roundrectangle"/>
|
110
|
+
</y:ShapeNode>
|
111
|
+
</data>
|
112
|
+
</node>
|
113
|
+
<node id="n8">
|
114
|
+
<data key="d5"/>
|
115
|
+
<data key="d6">
|
116
|
+
<y:ShapeNode>
|
117
|
+
<y:Geometry height="26.0" width="92.0" x="416.0" y="489.0"/>
|
118
|
+
<y:Fill hasColor="false" transparent="false"/>
|
119
|
+
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
120
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="21.853515625" x="35.0732421875" y="3.93359375">W1</y:NodeLabel>
|
121
|
+
<y:Shape type="roundrectangle"/>
|
122
|
+
</y:ShapeNode>
|
123
|
+
</data>
|
124
|
+
</node>
|
125
|
+
<node id="n9">
|
126
|
+
<data key="d5"/>
|
127
|
+
<data key="d6">
|
128
|
+
<y:ShapeNode>
|
129
|
+
<y:Geometry height="120.0" width="122.0" x="405.0" y="477.0"/>
|
130
|
+
<y:Fill hasColor="false" transparent="false"/>
|
131
|
+
<y:BorderStyle color="#000000" type="line" width="2.0"/>
|
132
|
+
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="sides" modelPosition="s" textColor="#000000" visible="true" width="45.15625" x="38.421875" y="124.0">Worker</y:NodeLabel>
|
133
|
+
<y:Shape type="rectangle"/>
|
134
|
+
</y:ShapeNode>
|
135
|
+
</data>
|
136
|
+
</node>
|
137
|
+
<edge id="e0" source="n0" target="n1">
|
138
|
+
<data key="d10">
|
139
|
+
<y:PolyLineEdge>
|
140
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
141
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
142
|
+
<y:Arrows source="none" target="standard"/>
|
143
|
+
<y:BendStyle smoothed="false"/>
|
144
|
+
</y:PolyLineEdge>
|
145
|
+
</data>
|
146
|
+
</edge>
|
147
|
+
<edge id="e1" source="n8" target="n9">
|
148
|
+
<data key="d10">
|
149
|
+
<y:PolyLineEdge>
|
150
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
151
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
152
|
+
<y:Arrows source="none" target="standard"/>
|
153
|
+
<y:BendStyle smoothed="false"/>
|
154
|
+
</y:PolyLineEdge>
|
155
|
+
</data>
|
156
|
+
</edge>
|
157
|
+
<edge id="e2" source="n3" target="n2">
|
158
|
+
<data key="d10">
|
159
|
+
<y:PolyLineEdge>
|
160
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
161
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
162
|
+
<y:Arrows source="delta" target="delta"/>
|
163
|
+
<y:BendStyle smoothed="false"/>
|
164
|
+
</y:PolyLineEdge>
|
165
|
+
</data>
|
166
|
+
</edge>
|
167
|
+
<edge id="e3" source="n4" target="n2">
|
168
|
+
<data key="d10">
|
169
|
+
<y:PolyLineEdge>
|
170
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
171
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
172
|
+
<y:Arrows source="delta" target="delta"/>
|
173
|
+
<y:BendStyle smoothed="false"/>
|
174
|
+
</y:PolyLineEdge>
|
175
|
+
</data>
|
176
|
+
</edge>
|
177
|
+
<edge id="e4" source="n5" target="n2">
|
178
|
+
<data key="d10">
|
179
|
+
<y:PolyLineEdge>
|
180
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
181
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
182
|
+
<y:Arrows source="delta" target="delta"/>
|
183
|
+
<y:BendStyle smoothed="false"/>
|
184
|
+
</y:PolyLineEdge>
|
185
|
+
</data>
|
186
|
+
</edge>
|
187
|
+
<edge id="e5" source="n6" target="n2">
|
188
|
+
<data key="d10">
|
189
|
+
<y:PolyLineEdge>
|
190
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
191
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
192
|
+
<y:Arrows source="delta" target="delta"/>
|
193
|
+
<y:BendStyle smoothed="false"/>
|
194
|
+
</y:PolyLineEdge>
|
195
|
+
</data>
|
196
|
+
</edge>
|
197
|
+
<edge id="e6" source="n2" target="n9">
|
198
|
+
<data key="d10">
|
199
|
+
<y:PolyLineEdge>
|
200
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
201
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
202
|
+
<y:Arrows source="delta" target="delta"/>
|
203
|
+
<y:BendStyle smoothed="false"/>
|
204
|
+
</y:PolyLineEdge>
|
205
|
+
</data>
|
206
|
+
</edge>
|
207
|
+
<edge id="e7" source="n2" target="n1">
|
208
|
+
<data key="d10">
|
209
|
+
<y:PolyLineEdge>
|
210
|
+
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
211
|
+
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
212
|
+
<y:Arrows source="delta" target="delta"/>
|
213
|
+
<y:BendStyle smoothed="false"/>
|
214
|
+
</y:PolyLineEdge>
|
215
|
+
</data>
|
216
|
+
</edge>
|
217
|
+
</graph>
|
218
|
+
<data key="d0">
|
219
|
+
<y:Resources/>
|
220
|
+
</data>
|
221
|
+
</graphml>
|