journeta 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -0
- data/License.txt +11 -0
- data/Manifest.txt +27 -0
- data/README.txt +41 -0
- data/Rakefile +123 -0
- data/examples/instant_messenger.rb +77 -0
- data/lib/journeta.rb +16 -0
- data/lib/journeta/asynchronous.rb +34 -0
- data/lib/journeta/event_broadcaster.rb +36 -0
- data/lib/journeta/event_listener.rb +57 -0
- data/lib/journeta/journeta_engine.rb +114 -0
- data/lib/journeta/logger.rb +12 -0
- data/lib/journeta/peer_connection.rb +34 -0
- data/lib/journeta/peer_registry.rb +70 -0
- data/lib/journeta/presence_message.rb +19 -0
- data/lib/journeta/session_handler.rb +13 -0
- data/lib/journeta/session_listener.rb +40 -0
- data/lib/journeta/version.rb +9 -0
- data/scripts/txt2html +67 -0
- data/setup.rb +1585 -0
- data/test/test_event_broadcaster.rb +15 -0
- data/test/test_helper.rb +4 -0
- data/test/test_journeta.rb +11 -0
- data/test/test_lifecycle.rb +15 -0
- data/website/index.html +87 -0
- data/website/index.txt +31 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +48 -0
- metadata +96 -0
data/History.txt
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
== 0.0.2 2008-08-19
|
2
|
+
|
3
|
+
* First working RubyForge release:
|
4
|
+
* See README.txt for general info.
|
5
|
+
* Adding functional chat room example. (See examples/)
|
6
|
+
|
7
|
+
== 0.0.1 2007-07-25
|
8
|
+
|
9
|
+
* Initial release:
|
10
|
+
* Basic port from original Java code.
|
11
|
+
* NOT YET FUNCTIONAL!
|
data/License.txt
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Copyright (c) 2007, OpenRain, LLC. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
4
|
+
|
5
|
+
-Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
6
|
+
|
7
|
+
-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
|
9
|
+
-Neither the name of the OpenRain, LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
10
|
+
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
lib/journeta.rb
|
7
|
+
examples/instant_messenger.rb
|
8
|
+
lib/journeta/logger.rb
|
9
|
+
lib/journeta/version.rb
|
10
|
+
lib/journeta/asynchronous.rb
|
11
|
+
lib/journeta/session_listener.rb
|
12
|
+
lib/journeta/session_handler.rb
|
13
|
+
lib/journeta/event_broadcaster.rb
|
14
|
+
lib/journeta/event_listener.rb
|
15
|
+
lib/journeta/presence_message.rb
|
16
|
+
lib/journeta/peer_registry.rb
|
17
|
+
lib/journeta/peer_connection.rb
|
18
|
+
lib/journeta/journeta_engine.rb
|
19
|
+
scripts/txt2html
|
20
|
+
setup.rb
|
21
|
+
test/test_journeta.rb
|
22
|
+
test/test_helper.rb
|
23
|
+
website/index.html
|
24
|
+
website/index.txt
|
25
|
+
website/javascripts/rounded_corners_lite.inc.js
|
26
|
+
website/stylesheets/screen.css
|
27
|
+
website/template.rhtml
|
data/README.txt
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Journeta
|
2
|
+
|
3
|
+
== About
|
4
|
+
|
5
|
+
|
6
|
+
Journeta is a dirt simple peer discovery and message passing library for processes on the same LAN,
|
7
|
+
requiring no advanced networking knowledge to use.
|
8
|
+
|
9
|
+
Only core Ruby libraries are required, making the library fairly light. As all data is sent accross
|
10
|
+
the wire in YAML form, any arbitrary Ruby object can be sent to peers, provided they..
|
11
|
+
|
12
|
+
* Are running a compatible Journeta version, and
|
13
|
+
* Have access to the same class definitions if you are sending your own custom objects.
|
14
|
+
* Do not have a firewall preventing
|
15
|
+
|
16
|
+
Journeta uses Ruby threading to manage the asynchonous nature of peer-to-peer I/O.
|
17
|
+
For insight into events internal to the library, start ruby with the `--debug` options.
|
18
|
+
|
19
|
+
|
20
|
+
== Use
|
21
|
+
|
22
|
+
|
23
|
+
examples/instant_messenger.rb
|
24
|
+
|
25
|
+
A completely distributed, zero-configuration-required chat room script.
|
26
|
+
Fire up several instances in separate terminals. Multiple instances on the same machine is ok.
|
27
|
+
Everything you type will automatically be sent to all other instances on the LAN!
|
28
|
+
Use `ruby --debug examples/instant_messenger.rb` for detailed internal event details.
|
29
|
+
|
30
|
+
|
31
|
+
== Author
|
32
|
+
|
33
|
+
Preston Lee <preston.lee at openrain d0t com>
|
34
|
+
http://www.prestonlee.com
|
35
|
+
http://www.openrain.com
|
36
|
+
|
37
|
+
|
38
|
+
== Links
|
39
|
+
|
40
|
+
How Journeta discovers peers using UDP multicasting..
|
41
|
+
http://onestepback.org/index.cgi/Tech/Ruby/MulticastingInRuby.red
|
data/Rakefile
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'hoe'
|
11
|
+
|
12
|
+
include FileUtils
|
13
|
+
require File.join(File.dirname(__FILE__), 'lib', 'journeta', 'version')
|
14
|
+
|
15
|
+
AUTHOR = 'Preston Lee' # can also be an array of Authors
|
16
|
+
EMAIL = "preston.lee@openrain.com"
|
17
|
+
DESCRIPTION = "A zero-configuration-required peer-to-peer (P2P) discovery and communications library for closed networks."
|
18
|
+
GEM_NAME = 'journeta' # what ppl will type to install your gem
|
19
|
+
|
20
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
21
|
+
@config = nil
|
22
|
+
def rubyforge_username
|
23
|
+
unless @config
|
24
|
+
begin
|
25
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
26
|
+
rescue
|
27
|
+
puts <<-EOS
|
28
|
+
ERROR: No rubyforge config file found: #{@config_file}"
|
29
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
30
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
31
|
+
EOS
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@rubyforge_username ||= @config["username"]
|
36
|
+
end
|
37
|
+
|
38
|
+
RUBYFORGE_PROJECT = 'journeta' # The unix name for your project
|
39
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
40
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
41
|
+
|
42
|
+
NAME = "journeta"
|
43
|
+
REV = nil
|
44
|
+
# UNCOMMENT IF REQUIRED:
|
45
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
46
|
+
VERS = Journeta::VERSION::STRING + (REV ? ".#{REV}" : "")
|
47
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
|
48
|
+
RDOC_OPTS = ['--quiet', '--title', 'journeta documentation',
|
49
|
+
"--opname", "index.html",
|
50
|
+
"--line-numbers",
|
51
|
+
"--main", "README",
|
52
|
+
"--inline-source"]
|
53
|
+
|
54
|
+
class Hoe
|
55
|
+
def extra_deps
|
56
|
+
@extra_deps.reject { |x| Array(x).first == 'hoe' }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generate all the Rake tasks
|
61
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
62
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
63
|
+
p.author = AUTHOR
|
64
|
+
p.description = DESCRIPTION
|
65
|
+
p.email = EMAIL
|
66
|
+
p.summary = DESCRIPTION
|
67
|
+
p.url = HOMEPATH
|
68
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
69
|
+
p.test_globs = ["test/**/test_*.rb"]
|
70
|
+
p.clean_globs |= CLEAN #An array of file patterns to delete on clean.
|
71
|
+
|
72
|
+
# == Optional
|
73
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
74
|
+
#p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
75
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
76
|
+
end
|
77
|
+
|
78
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\n\n")
|
79
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
80
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
81
|
+
|
82
|
+
desc 'Generate website files'
|
83
|
+
task :website_generate do
|
84
|
+
Dir['website/**/*.txt'].each do |txt|
|
85
|
+
sh %{ ruby scripts/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
desc 'Upload website files to rubyforge'
|
90
|
+
task :website_upload do
|
91
|
+
host = "#{rubyforge_username}@rubyforge.org"
|
92
|
+
remote_dir = "/var/www/gforge-projects/#{PATH}/"
|
93
|
+
local_dir = 'website'
|
94
|
+
sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
|
95
|
+
end
|
96
|
+
|
97
|
+
desc 'Generate and upload website files'
|
98
|
+
task :website => [:website_generate, :website_upload, :publish_docs]
|
99
|
+
|
100
|
+
desc 'Release the website and new gem version'
|
101
|
+
task :deploy => [:check_version, :website, :release] do
|
102
|
+
puts "Remember to create SVN tag:"
|
103
|
+
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
|
104
|
+
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
|
105
|
+
puts "Suggested comment:"
|
106
|
+
puts "Tagging release #{CHANGES}"
|
107
|
+
end
|
108
|
+
|
109
|
+
desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
|
110
|
+
task :local_deploy => [:website_generate, :install_gem]
|
111
|
+
|
112
|
+
task :check_version do
|
113
|
+
unless ENV['VERSION']
|
114
|
+
puts 'Must pass a VERSION=x.y.z release version'
|
115
|
+
exit
|
116
|
+
end
|
117
|
+
unless ENV['VERSION'] == VERS
|
118
|
+
puts "Please update your version.rb to match the release version, currently #{VERS}"
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
current_dir = File.dirname(File.expand_path(__FILE__))
|
3
|
+
lib_path = File.join(current_dir, '..', 'lib')
|
4
|
+
$LOAD_PATH.unshift lib_path
|
5
|
+
|
6
|
+
# Load up the library!
|
7
|
+
require 'journeta'
|
8
|
+
include Journeta
|
9
|
+
|
10
|
+
|
11
|
+
# Any arbitrary object can be sent to peers as long as it's serializable to YAML.
|
12
|
+
# We'll create an ordinary class with a couple typical-looking fields to send to our peers.
|
13
|
+
class ExampleMessage
|
14
|
+
attr_accessor :name
|
15
|
+
attr_accessor :text
|
16
|
+
end
|
17
|
+
|
18
|
+
# A message handler will be called by the engine every time a message is received.
|
19
|
+
# This code will be customized for your application-specific needs.
|
20
|
+
class ExampleHandler < Journeta::DefaultSessionHandler
|
21
|
+
def handle(message)
|
22
|
+
puts "#{message.name.chop}: #{message.text}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Now we'll create an instance of the Journeta P2P engine.
|
27
|
+
# We'll change the default incoming session port to a
|
28
|
+
# pseudo-randomly generated number so multiple instances
|
29
|
+
# may be started on the same machine.
|
30
|
+
#
|
31
|
+
# You'll need to find an unused port if..
|
32
|
+
# (1) you intend to run multiple peers on the same machine, or
|
33
|
+
# (2) the default port (Journeta::JournetaEngine::DEFAULT_SESSION_PORT)
|
34
|
+
# is otherwise already taken on your machine.
|
35
|
+
session_port = (2048 + rand( 2 ** 8))
|
36
|
+
journeta = Journeta::JournetaEngine.new(:session_port => session_port, :session_handler => ExampleHandler.new)
|
37
|
+
|
38
|
+
|
39
|
+
# Let the magic begin!
|
40
|
+
journeta.start
|
41
|
+
|
42
|
+
puts "What's your name?"
|
43
|
+
name = gets
|
44
|
+
|
45
|
+
# The `known_peers` call allows you to access the registry of known available peers on the network.
|
46
|
+
# The UUID associated which each peer will be unique accross the network.
|
47
|
+
peers = journeta.known_peers
|
48
|
+
if peers.size > 0
|
49
|
+
puts 'The following peers IDs are online..'
|
50
|
+
peers.each do |uuid, peer|
|
51
|
+
puts " #{uuid}; version #{peer.version}"
|
52
|
+
end
|
53
|
+
else
|
54
|
+
puts 'No peers known. (Start another client!)'
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Sit around are watch events at the console until the user hits <enter>
|
59
|
+
puts 'Text you enter here will automatically be shown on peers terminals.'
|
60
|
+
begin
|
61
|
+
loop do
|
62
|
+
input = gets
|
63
|
+
m = ExampleMessage.new
|
64
|
+
m.name = name
|
65
|
+
m.text = input
|
66
|
+
journeta.send_to_known_peers(m)
|
67
|
+
end
|
68
|
+
ensure
|
69
|
+
end
|
70
|
+
|
71
|
+
# Please stop the engine when shutting down. This broadcasts a message
|
72
|
+
# stating you are going offline as a courtesy to your peers.
|
73
|
+
journeta.stop
|
74
|
+
|
75
|
+
# The engine can be restarted and stopped as many times as you'd like.
|
76
|
+
journeta.start
|
77
|
+
journeta.stop
|
data/lib/journeta.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
|
5
|
+
require 'journeta/logger'
|
6
|
+
require 'journeta/version'
|
7
|
+
require 'journeta/asynchronous'
|
8
|
+
|
9
|
+
require 'journeta/session_listener'
|
10
|
+
require 'journeta/session_handler'
|
11
|
+
require 'journeta/event_broadcaster'
|
12
|
+
require 'journeta/event_listener'
|
13
|
+
require 'journeta/presence_message'
|
14
|
+
require 'journeta/peer_registry'
|
15
|
+
require 'journeta/peer_connection'
|
16
|
+
require 'journeta/journeta_engine'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'journeta/logger'
|
2
|
+
|
3
|
+
module Journeta
|
4
|
+
|
5
|
+
class Asynchronous
|
6
|
+
|
7
|
+
include Logger
|
8
|
+
|
9
|
+
attr_accessor :thread, :engine
|
10
|
+
|
11
|
+
def initialize(engine)
|
12
|
+
@engine = engine
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
putsd "Creating asynchronous thread for I/O: #{self.class.to_s}."
|
17
|
+
@thread = Thread.new {
|
18
|
+
go # @engine
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
Thread.kill(@thread)
|
24
|
+
@thread.join
|
25
|
+
@thread = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def go #(engine)
|
29
|
+
raise NotImplementedException
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#require 'yaml'
|
2
|
+
|
3
|
+
require 'journeta/asynchronous'
|
4
|
+
|
5
|
+
|
6
|
+
module Journeta
|
7
|
+
|
8
|
+
class EventBroadcaster < Journeta::Asynchronous
|
9
|
+
|
10
|
+
attr_accessor :thread
|
11
|
+
|
12
|
+
def go #(engine)
|
13
|
+
address = @engine.configuration[:event_address]
|
14
|
+
port = @engine.configuration[:event_port]
|
15
|
+
delay = @engine.configuration[:event_period]
|
16
|
+
uuid = @engine.configuration[:uuid]
|
17
|
+
session_port = @engine.configuration[:session_port]
|
18
|
+
begin
|
19
|
+
socket = UDPSocket.open
|
20
|
+
socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, [1].pack('i'))
|
21
|
+
loop do
|
22
|
+
putsd "Sending presence event."
|
23
|
+
note = PresenceMessage.new uuid, session_port
|
24
|
+
socket.send(note.to_yaml, 0, address, port)
|
25
|
+
sleep delay
|
26
|
+
end
|
27
|
+
ensure
|
28
|
+
putsd "Closing event broadcaster socket."
|
29
|
+
socket.close
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'socket'
|
3
|
+
require 'ipaddr'
|
4
|
+
require 'journeta/asynchronous'
|
5
|
+
require 'journeta/peer_connection'
|
6
|
+
|
7
|
+
module Journeta
|
8
|
+
|
9
|
+
# uses the fucked up socket api to listen for events broadcast from peers
|
10
|
+
class EventListener < Journeta::Asynchronous
|
11
|
+
|
12
|
+
|
13
|
+
def go #(engine)
|
14
|
+
event_address = @engine.configuration[:event_address]
|
15
|
+
port = @engine.configuration[:event_port]
|
16
|
+
addresses = IPAddr.new(event_address).hton + IPAddr.new("0.0.0.0").hton
|
17
|
+
begin
|
18
|
+
socket = UDPSocket.new
|
19
|
+
# Remember how i said this was fucked up? yeaahhhhhh. i hope you like C.
|
20
|
+
# `man setsockopt` for details.
|
21
|
+
# SO_REUSEPORT is needed so multiple peers can be run on the same machine.
|
22
|
+
socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, [1].pack("i_") )
|
23
|
+
# socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, [1].pack("i_") )
|
24
|
+
socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, addresses)
|
25
|
+
socket.bind(Socket::INADDR_ANY, port)
|
26
|
+
putsd "Waiting for presence events."
|
27
|
+
loop do
|
28
|
+
# Why 1024? umm.. because it's Thursday!
|
29
|
+
data, meta = socket.recvfrom 1024
|
30
|
+
Thread.new(data) {
|
31
|
+
event = YAML.load(data)
|
32
|
+
if event.uuid != @engine.configuration[:uuid]
|
33
|
+
# putsd "New Event: #{data} #{meta.inspect}"
|
34
|
+
# Update registry
|
35
|
+
m = YAML::load(data)
|
36
|
+
peer = PeerConnection.new
|
37
|
+
# require 'pp'
|
38
|
+
# pp m
|
39
|
+
# Why is this always [2]? Not sure.. they should have returned a hash instead.
|
40
|
+
peer.ip_address = meta[2]
|
41
|
+
peer.session_port = m.session_port
|
42
|
+
peer.uuid = m.uuid
|
43
|
+
peer.version = m.version
|
44
|
+
@engine.register_peer peer
|
45
|
+
end
|
46
|
+
}
|
47
|
+
# putsd "Event received!"
|
48
|
+
end
|
49
|
+
ensure
|
50
|
+
putsd "Closing event listener socket."
|
51
|
+
socket.close
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# Copyright © 2007 OpenRain, LLC. All rights reserved.
|
2
|
+
#
|
3
|
+
# Preston Lee <preston.lee@openrain.com>
|
4
|
+
|
5
|
+
|
6
|
+
module Journeta
|
7
|
+
|
8
|
+
class JournetaEngine
|
9
|
+
|
10
|
+
include Logger
|
11
|
+
|
12
|
+
# Continuously sends out "i'm here" presence messages to the local network
|
13
|
+
attr_accessor :event_broadcaster
|
14
|
+
# continuously listens for "i'm here" presence messages from other peers
|
15
|
+
attr_accessor :event_listener
|
16
|
+
|
17
|
+
# Constantly listens for incoming peer sessions
|
18
|
+
attr_accessor :session_listener
|
19
|
+
# Application logic which processes session data.
|
20
|
+
attr_accessor :session_handler
|
21
|
+
|
22
|
+
# Authoritative peer availability database.
|
23
|
+
attr_accessor :peer_registry
|
24
|
+
|
25
|
+
# Instance-specific configuration which may be overriden at initialization time by the application
|
26
|
+
attr_reader :configuration
|
27
|
+
|
28
|
+
# The universally-unique ID of this instance.
|
29
|
+
attr_reader :uuid
|
30
|
+
|
31
|
+
@@DEFAULT_SESSION_PORT = 31338
|
32
|
+
@@DEFAULT_EVENT_PORT = 31337
|
33
|
+
@@DEFAULT_SESSION_HANDLER = DefaultSessionHandler.new
|
34
|
+
|
35
|
+
# Addresses 224.0.0.0 through 239.255.255.255 are reserved for multicast messages.
|
36
|
+
@@DEFAULT_EVENT_NETWORK = '224.220.221.222'
|
37
|
+
@@DEFAULT_EVENT_PERIOD = 4
|
38
|
+
|
39
|
+
|
40
|
+
def initialize(configuration ={})
|
41
|
+
putsd "CON: #{configuration}"
|
42
|
+
@configuration = Hash.new
|
43
|
+
|
44
|
+
# A supposedly universally unique id for this instance. not technically gauranteed but close enough for now.
|
45
|
+
# TODO make guaranteed to be unique.
|
46
|
+
@configuration[:uuid] = configuration[:uuid] || rand(2 ** 31)
|
47
|
+
# the tcp port to use for direct peer connections
|
48
|
+
@configuration[:session_port] = configuration[:session_port] || @@DEFAULT_SESSION_PORT
|
49
|
+
|
50
|
+
@session_handler = configuration[:session_handler] || @@DEFAULT_SESSION_HANDLER
|
51
|
+
|
52
|
+
# The UDP port for event broadcast messages.
|
53
|
+
@configuration[:event_port] = configuration[:event_port] || @@DEFAULT_EVENT_PORT
|
54
|
+
|
55
|
+
# The UDP network address used for broadcast messages.
|
56
|
+
@configuration[:event_address] = configuration[:event_address] || @@DEFAULT_EVENT_NETWORK
|
57
|
+
|
58
|
+
# The delay, in seconds, between presence notification broadcasts.
|
59
|
+
@configuration[:event_period] = configuration[:event_period] || @@DEFAULT_EVENT_PERIOD
|
60
|
+
|
61
|
+
# Initialize sub-components.
|
62
|
+
@session_listener = Journeta::SessionListener.new self
|
63
|
+
@event_listener = EventListener.new self
|
64
|
+
@event_broadcaster = EventBroadcaster.new self
|
65
|
+
@peer_registry = PeerRegistry.new self
|
66
|
+
end
|
67
|
+
|
68
|
+
def start
|
69
|
+
# start a session listener first so we don't risk missing a connection attempt
|
70
|
+
putsd "Starting #{@session_listener.class.to_s}"
|
71
|
+
@session_listener.start
|
72
|
+
|
73
|
+
# start listening for peer events
|
74
|
+
putsd "Starting #{@event_listener.class.to_s}"
|
75
|
+
@event_listener.start
|
76
|
+
|
77
|
+
# start sending our own events
|
78
|
+
putsd "Starting #{@event_broadcaster.class.to_s}"
|
79
|
+
@event_broadcaster.start
|
80
|
+
end
|
81
|
+
|
82
|
+
def stop
|
83
|
+
# stop broadcasting events
|
84
|
+
@event_broadcaster.stop
|
85
|
+
# stop listener for events
|
86
|
+
@event_listener.stop
|
87
|
+
# stop listening for incoming peer sessions
|
88
|
+
@session_listener.stop
|
89
|
+
end
|
90
|
+
|
91
|
+
def send_to_known_peers(payload)
|
92
|
+
# Delegate directly.
|
93
|
+
peer_registry.send_to_known_peers(payload)
|
94
|
+
end
|
95
|
+
|
96
|
+
def send_to_peer(peer_uuid, payload)
|
97
|
+
# Delegate directly.
|
98
|
+
peer_registry.send_to_peer(peer_uuid, payload)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns metadata on all known peers in a hash, keyed by the uuid of each.
|
102
|
+
# A record corresponding to this peer is not included.
|
103
|
+
def known_peers()
|
104
|
+
peer_registry.peers # FIXME Returns objects outside of synchronized context.
|
105
|
+
end
|
106
|
+
|
107
|
+
# Adds (or updates) the given +PeerConnection+.
|
108
|
+
def register_peer(peer)
|
109
|
+
peer_registry.add(peer)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|