journeta 0.0.2 → 0.0.3
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.
- data/History.txt +12 -0
- data/Manifest.txt +10 -5
- data/README.txt +2 -2
- data/examples/instant_messenger.rb +31 -14
- data/examples/queue_client.rb +38 -0
- data/examples/queue_server.rb +51 -0
- data/lib/journeta.rb +9 -4
- data/lib/journeta/asynchronous.rb +59 -28
- data/lib/journeta/common/dummy_peer_handler.rb +13 -0
- data/lib/journeta/common/job.rb +15 -0
- data/lib/journeta/common/shutdown.rb +21 -0
- data/lib/journeta/journeta_engine.rb +84 -56
- data/lib/journeta/logger.rb +3 -0
- data/lib/journeta/peer_connection.rb +43 -12
- data/lib/journeta/{session_handler.rb → peer_handler.rb} +1 -1
- data/lib/journeta/{session_listener.rb → peer_listener.rb} +4 -4
- data/lib/journeta/peer_registry.rb +70 -16
- data/lib/journeta/presence_broadcaster.rb +41 -0
- data/lib/journeta/{event_listener.rb → presence_listener.rb} +10 -11
- data/lib/journeta/presence_message.rb +7 -5
- data/lib/journeta/version.rb +1 -1
- data/website/index.html +8 -8
- metadata +12 -7
- data/lib/journeta/event_broadcaster.rb +0 -36
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
== 0.0.3 2008-08-21
|
2
|
+
|
3
|
+
* Added peer 'groups' support to avoid peer message spamming.
|
4
|
+
* Major refactoring to make outbound messages sent asynchonously to the sending thread.
|
5
|
+
* Example and documentation updates.
|
6
|
+
* Shared network queue example added!
|
7
|
+
* examples/queue_client.rb
|
8
|
+
* examples/queue_server.rb
|
9
|
+
* Renaming..
|
10
|
+
* Session -> Peer
|
11
|
+
* Event -> Presence
|
12
|
+
|
1
13
|
== 0.0.2 2008-08-19
|
2
14
|
|
3
15
|
* First working RubyForge release:
|
data/Manifest.txt
CHANGED
@@ -5,17 +5,22 @@ README.txt
|
|
5
5
|
Rakefile
|
6
6
|
lib/journeta.rb
|
7
7
|
examples/instant_messenger.rb
|
8
|
+
examples/queue_client.rb
|
9
|
+
examples/queue_server.rb
|
8
10
|
lib/journeta/logger.rb
|
9
11
|
lib/journeta/version.rb
|
10
12
|
lib/journeta/asynchronous.rb
|
11
|
-
lib/journeta/
|
12
|
-
lib/journeta/
|
13
|
-
lib/journeta/event_broadcaster.rb
|
14
|
-
lib/journeta/event_listener.rb
|
15
|
-
lib/journeta/presence_message.rb
|
13
|
+
lib/journeta/peer_listener.rb
|
14
|
+
lib/journeta/peer_handler.rb
|
16
15
|
lib/journeta/peer_registry.rb
|
17
16
|
lib/journeta/peer_connection.rb
|
17
|
+
lib/journeta/presence_broadcaster.rb
|
18
|
+
lib/journeta/presence_listener.rb
|
19
|
+
lib/journeta/presence_message.rb
|
18
20
|
lib/journeta/journeta_engine.rb
|
21
|
+
lib/journeta/common/dummy_peer_handler.rb
|
22
|
+
lib/journeta/common/job.rb
|
23
|
+
lib/journeta/common/shutdown.rb
|
19
24
|
scripts/txt2html
|
20
25
|
setup.rb
|
21
26
|
test/test_journeta.rb
|
data/README.txt
CHANGED
@@ -9,9 +9,9 @@ requiring no advanced networking knowledge to use.
|
|
9
9
|
Only core Ruby libraries are required, making the library fairly light. As all data is sent accross
|
10
10
|
the wire in YAML form, any arbitrary Ruby object can be sent to peers, provided they..
|
11
11
|
|
12
|
-
* Are running a compatible Journeta version
|
12
|
+
* Are running a compatible Journeta version.
|
13
13
|
* Have access to the same class definitions if you are sending your own custom objects.
|
14
|
-
* Do not have a firewall preventing
|
14
|
+
* Do not have a firewall preventing network I/O.
|
15
15
|
|
16
16
|
Journeta uses Ruby threading to manage the asynchonous nature of peer-to-peer I/O.
|
17
17
|
For insight into events internal to the library, start ruby with the `--debug` options.
|
@@ -17,28 +17,46 @@ end
|
|
17
17
|
|
18
18
|
# A message handler will be called by the engine every time a message is received.
|
19
19
|
# This code will be customized for your application-specific needs.
|
20
|
-
class ExampleHandler < Journeta::
|
20
|
+
class ExampleHandler < Journeta::DefaultPeerHandler
|
21
21
|
def handle(message)
|
22
|
-
|
22
|
+
if message.class == ExampleMessage
|
23
|
+
puts "#{message.name.chop}: #{message.text}"
|
24
|
+
else
|
25
|
+
putsd("Unsupported message type received from peer. (#{message})")
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
30
|
# Now we'll create an instance of the Journeta P2P engine.
|
27
|
-
#
|
31
|
+
#
|
32
|
+
# :peer_port -- We'll change the default incoming session port to a
|
28
33
|
# pseudo-randomly generated number so multiple instances
|
29
34
|
# may be started on the same machine.
|
30
35
|
#
|
31
36
|
# You'll need to find an unused port if..
|
32
37
|
# (1) you intend to run multiple peers on the same machine, or
|
33
|
-
# (2) the default port (Journeta::JournetaEngine::
|
38
|
+
# (2) the default port (Journeta::JournetaEngine::DEFAULT_PEER_PORT)
|
34
39
|
# is otherwise already taken on your machine.
|
35
|
-
|
36
|
-
|
40
|
+
#
|
41
|
+
# :peer_handler -- A piece of logic you must specify to process objects sent to you from peers.
|
42
|
+
# :groups -- Defines the peer types which care about the objects you broadcast. (Optional: by default, all peers will receive all your object broadcasts.)
|
43
|
+
peer_port = (2048 + rand( 2 ** 8))
|
44
|
+
journeta = Journeta::JournetaEngine.new(:peer_port => peer_port, :peer_handler => ExampleHandler.new, :groups => ['im_example'])
|
37
45
|
|
38
46
|
|
39
47
|
# Let the magic begin!
|
40
48
|
journeta.start
|
41
49
|
|
50
|
+
|
51
|
+
# You can use the following helper to automatically stop the given engine when the application is killed with CTRL-C.
|
52
|
+
include Journeta::Common::Shutdown
|
53
|
+
stop_on_shutdown(journeta)
|
54
|
+
|
55
|
+
# Alternatively, you can stop the engine manually by calling +JournetaEngine#stop+.
|
56
|
+
# Do this before exiting to broadcast a message stating you are going offline as a courtesy to your peers, like so..
|
57
|
+
# @journeta.stop
|
58
|
+
|
59
|
+
|
42
60
|
puts "What's your name?"
|
43
61
|
name = gets
|
44
62
|
|
@@ -58,20 +76,19 @@ end
|
|
58
76
|
# Sit around are watch events at the console until the user hits <enter>
|
59
77
|
puts 'Text you enter here will automatically be shown on peers terminals.'
|
60
78
|
begin
|
61
|
-
|
79
|
+
loop do
|
80
|
+
begin
|
62
81
|
input = gets
|
63
82
|
m = ExampleMessage.new
|
64
83
|
m.name = name
|
65
84
|
m.text = input
|
66
85
|
journeta.send_to_known_peers(m)
|
67
|
-
|
68
|
-
|
86
|
+
end
|
87
|
+
end
|
69
88
|
end
|
70
89
|
|
71
|
-
|
72
|
-
# stating you are going offline as a courtesy to your peers.
|
73
|
-
journeta.stop
|
90
|
+
|
74
91
|
|
75
92
|
# The engine can be restarted and stopped as many times as you'd like.
|
76
|
-
journeta.start
|
77
|
-
journeta.stop
|
93
|
+
#journeta.start
|
94
|
+
#journeta.stop
|
@@ -0,0 +1,38 @@
|
|
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
|
+
require 'journeta'
|
7
|
+
include Journeta
|
8
|
+
include Journeta::Common
|
9
|
+
include Journeta::Common::Shutdown
|
10
|
+
|
11
|
+
class JobProcessor
|
12
|
+
def handle(msg)
|
13
|
+
if msg.class == Job && !msg.submission
|
14
|
+
puts "Processing job ##{msg.name} from peer ##{msg.owner}."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
peer_port = (2048 + rand( 2 ** 8))
|
20
|
+
journeta = Journeta::JournetaEngine.new(:peer_port => peer_port, :peer_handler => JobProcessor.new, :groups => ['queue_example'])
|
21
|
+
stop_on_shutdown(journeta)
|
22
|
+
journeta.start
|
23
|
+
|
24
|
+
|
25
|
+
# Keep creating random jobs.
|
26
|
+
puts "Don't forget to start a server at some point! CTRL-C to exit this client."
|
27
|
+
while true
|
28
|
+
num = rand(1024)
|
29
|
+
puts "Creating random job ##{num}."
|
30
|
+
job = Job.new
|
31
|
+
job.owner = journeta.uuid
|
32
|
+
job.name = num
|
33
|
+
job.description = 'whatever'
|
34
|
+
job.data = 'Anything YAML serializable!'
|
35
|
+
job.submission = true
|
36
|
+
journeta.send_to_known_peers(job) # All servers will get it.
|
37
|
+
sleep 4
|
38
|
+
end
|
@@ -0,0 +1,51 @@
|
|
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
|
+
require 'thread'
|
7
|
+
require 'journeta'
|
8
|
+
include Journeta
|
9
|
+
include Journeta::Common
|
10
|
+
include Journeta::Common::Shutdown
|
11
|
+
|
12
|
+
@queue = Queue.new
|
13
|
+
|
14
|
+
class JobQueuer
|
15
|
+
def initialize(queue)
|
16
|
+
@queue = queue
|
17
|
+
end
|
18
|
+
def handle(msg)
|
19
|
+
if msg.class == Job && msg.submission
|
20
|
+
puts "Enqueing job '##{msg.name}' from peer ##{msg.owner}."
|
21
|
+
msg.submission = false
|
22
|
+
@queue.push msg
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
peer_port = (2048 + rand( 2 ** 8))
|
28
|
+
journeta = Journeta::JournetaEngine.new(:peer_port => peer_port, :peer_handler => JobQueuer.new(@queue), :groups => ['queue_example'])
|
29
|
+
stop_on_shutdown(journeta)
|
30
|
+
journeta.start
|
31
|
+
|
32
|
+
|
33
|
+
puts "Start multiple peers to see the server doing stuff! CTRL-C to stop this server. "
|
34
|
+
total = 0
|
35
|
+
while true
|
36
|
+
job = @queue.pop
|
37
|
+
puts "Job found! (#{job.name})"
|
38
|
+
all = journeta.known_peers.values
|
39
|
+
# Note that this list might already be outdated by the time we reach the next line!
|
40
|
+
|
41
|
+
if all.size > 0
|
42
|
+
# Pick a random client
|
43
|
+
worker = all[rand(all.size)]
|
44
|
+
puts "Sending to peer ##{worker.uuid}."
|
45
|
+
journeta.send_to_peer(worker.uuid, job)
|
46
|
+
else
|
47
|
+
puts "No workers found :( Will check again soon!"
|
48
|
+
@queue.push job
|
49
|
+
sleep 4 # Wait for peers to join
|
50
|
+
end
|
51
|
+
end
|
data/lib/journeta.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
|
2
2
|
require 'yaml'
|
3
|
+
require 'thread'
|
3
4
|
|
4
5
|
|
5
6
|
require 'journeta/logger'
|
6
7
|
require 'journeta/version'
|
7
8
|
require 'journeta/asynchronous'
|
8
9
|
|
9
|
-
require 'journeta/
|
10
|
-
require 'journeta/
|
11
|
-
require 'journeta/
|
12
|
-
require 'journeta/
|
10
|
+
require 'journeta/peer_listener'
|
11
|
+
require 'journeta/peer_handler'
|
12
|
+
require 'journeta/presence_broadcaster'
|
13
|
+
require 'journeta/presence_listener'
|
13
14
|
require 'journeta/presence_message'
|
14
15
|
require 'journeta/peer_registry'
|
15
16
|
require 'journeta/peer_connection'
|
16
17
|
require 'journeta/journeta_engine'
|
18
|
+
|
19
|
+
require 'journeta/common/job'
|
20
|
+
require 'journeta/common/shutdown'
|
21
|
+
require 'journeta/common/dummy_peer_handler'
|
@@ -1,34 +1,65 @@
|
|
1
|
-
|
1
|
+
# Copyright 2007, OpenRain, LLC. All rights reserved.
|
2
2
|
|
3
|
-
module Journeta
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
include Logger
|
8
|
-
|
9
|
-
attr_accessor :thread, :engine
|
4
|
+
require 'journeta/logger'
|
10
5
|
|
11
|
-
|
12
|
-
|
6
|
+
module Journeta
|
7
|
+
|
8
|
+
class Asynchronous
|
9
|
+
|
10
|
+
include Logger
|
11
|
+
|
12
|
+
attr_accessor :thread, :engine
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(engine)
|
17
|
+
@engine = engine
|
18
|
+
@thread_lock = Mutex.new
|
19
|
+
@thread = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
# Start the +Thread+ for this instance, iff not already running.
|
25
|
+
def start
|
26
|
+
@thread_lock.synchronize do
|
27
|
+
if @thread
|
28
|
+
# Do not restart it.
|
29
|
+
else
|
30
|
+
putsd "Creating asynchronous thread for I/O: #{self.class.to_s}."
|
31
|
+
@thread = Thread.new {
|
32
|
+
go
|
33
|
+
}
|
34
|
+
end
|
13
35
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
36
|
+
end
|
37
|
+
|
38
|
+
# This method is intentionally not present because it would be of no real value. By the time a boolean is returned, the instance could be in a totally different state.
|
39
|
+
# def started
|
40
|
+
# stopped = true
|
41
|
+
# @thread_lock.synchronize do
|
42
|
+
# stopped = @thread.nil?
|
43
|
+
# end
|
44
|
+
# return stopped
|
45
|
+
# end
|
46
|
+
|
47
|
+
# Stop the +Thread+ associated with this instance, iff not already stopped.
|
48
|
+
def stop
|
49
|
+
@thread_lock.synchronize do
|
50
|
+
if @thread
|
51
|
+
Thread.kill(@thread)
|
52
|
+
@thread.join
|
53
|
+
@thread = nil
|
54
|
+
end
|
20
55
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
56
|
+
end
|
57
|
+
|
58
|
+
# Abstract thread logic method which must be implemented by the sub-class.
|
59
|
+
def go
|
60
|
+
raise NotImplementedException
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
34
65
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Journeta
|
2
|
+
|
3
|
+
module Common
|
4
|
+
|
5
|
+
module Shutdown
|
6
|
+
|
7
|
+
def stop_on_shutdown(engine)
|
8
|
+
bye = Proc.new {
|
9
|
+
engine.stop
|
10
|
+
exit 0
|
11
|
+
}
|
12
|
+
Signal::trap("HUP", bye)
|
13
|
+
Signal::trap("INT", bye)
|
14
|
+
Signal::trap("KILL", bye)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -5,87 +5,111 @@
|
|
5
5
|
|
6
6
|
module Journeta
|
7
7
|
|
8
|
+
# The primary fascade of the entire +Journeta+ library, which composite a number of objects that may or may not have code running asynchronously to the primary application +Thread+.
|
8
9
|
class JournetaEngine
|
9
10
|
|
10
11
|
include Logger
|
11
|
-
|
12
|
+
|
13
|
+
# A supposedly universally unique id for this instance.
|
14
|
+
attr_reader :uuid
|
15
|
+
|
16
|
+
# An array of peer network names. Ex: ['OpenRain Test', 'quick_chat_app']
|
17
|
+
# An empty array indicates implicit membership in all discovered groups.
|
18
|
+
attr_reader :groups
|
19
|
+
|
20
|
+
|
12
21
|
# Continuously sends out "i'm here" presence messages to the local network
|
13
|
-
|
22
|
+
attr_reader :presence_broadcaster
|
23
|
+
|
14
24
|
# continuously listens for "i'm here" presence messages from other peers
|
15
|
-
|
16
|
-
|
25
|
+
attr_reader :presence_listener
|
26
|
+
|
27
|
+
# The UDP port for event broadcast messages.
|
28
|
+
attr_reader :presence_port
|
29
|
+
|
30
|
+
# The UDP network address used for broadcast messages.
|
31
|
+
attr_reader :presence_address
|
32
|
+
|
33
|
+
# The amount of time between presence broadcasts.
|
34
|
+
attr_reader :presence_period
|
35
|
+
|
36
|
+
|
37
|
+
|
17
38
|
# Constantly listens for incoming peer sessions
|
18
|
-
|
39
|
+
attr_reader :peer_listener
|
40
|
+
|
19
41
|
# Application logic which processes session data.
|
20
|
-
|
21
|
-
|
42
|
+
attr_reader :peer_handler
|
43
|
+
|
44
|
+
# The TCP port used to receive direct peer messages.
|
45
|
+
attr_reader :peer_port
|
46
|
+
|
22
47
|
# Authoritative peer availability database.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
48
|
+
attr_reader :peer_registry
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
# Incoming direct peer TCP connections will use this port.
|
55
|
+
@@DEFAULT_PEER_PORT = 31338
|
56
|
+
|
57
|
+
# The application message callback handler.
|
58
|
+
@@DEFAULT_PEER_HANDLER = DefaultPeerHandler.new
|
30
59
|
|
31
|
-
@@
|
32
|
-
@@DEFAULT_EVENT_PORT = 31337
|
33
|
-
@@DEFAULT_SESSION_HANDLER = DefaultSessionHandler.new
|
60
|
+
@@DEFAULT_PRESENCE_PORT = 31337
|
34
61
|
|
35
62
|
# Addresses 224.0.0.0 through 239.255.255.255 are reserved for multicast messages.
|
36
|
-
@@
|
37
|
-
|
63
|
+
@@DEFAULT_PRESENCE_NETWORK = '224.220.221.222'
|
64
|
+
|
65
|
+
# The wait time, in seconds, between rebroadcasts of peer presence.
|
66
|
+
@@DEFAULT_PRESENCE_PERIOD = 4
|
38
67
|
|
39
68
|
|
40
69
|
def initialize(configuration ={})
|
41
70
|
putsd "CON: #{configuration}"
|
42
|
-
@configuration = Hash.new
|
43
71
|
|
44
|
-
# A supposedly universally unique id for this instance. not technically gauranteed but close enough for now.
|
45
72
|
# TODO make guaranteed to be unique.
|
46
|
-
@
|
47
|
-
|
48
|
-
@configuration[:session_port] = configuration[:session_port] || @@DEFAULT_SESSION_PORT
|
49
|
-
|
50
|
-
@session_handler = configuration[:session_handler] || @@DEFAULT_SESSION_HANDLER
|
73
|
+
@uuid = configuration[:uuid] || rand(2 ** 31)
|
74
|
+
@groups = configuration[:groups]
|
51
75
|
|
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
76
|
|
61
|
-
|
62
|
-
@
|
63
|
-
@
|
64
|
-
|
77
|
+
@peer_port = configuration[:peer_port] || @@DEFAULT_PEER_PORT
|
78
|
+
@peer_handler = configuration[:peer_handler] || @@DEFAULT_PEER_HANDLER
|
79
|
+
@peer_listener = Journeta::PeerListener.new self
|
80
|
+
|
81
|
+
@presence_port = configuration[:presence_port] || @@DEFAULT_PRESENCE_PORT
|
82
|
+
@presence_address = configuration[:presence_address] || @@DEFAULT_PRESENCE_NETWORK
|
83
|
+
@presence_period = configuration[:presence_period] || @@DEFAULT_PRESENCE_PERIOD
|
84
|
+
@presence_listener = EventListener.new self
|
85
|
+
@presence_broadcaster = EventBroadcaster.new self
|
86
|
+
|
65
87
|
@peer_registry = PeerRegistry.new self
|
66
88
|
end
|
67
89
|
|
68
90
|
def start
|
69
|
-
#
|
70
|
-
putsd "Starting #{@
|
71
|
-
@
|
91
|
+
# Start a peer listener first so we don't risk missing a connection attempt.
|
92
|
+
putsd "Starting #{@peer_listener.class.to_s}"
|
93
|
+
@peer_listener.start
|
72
94
|
|
73
|
-
#
|
74
|
-
putsd "Starting #{@
|
75
|
-
@
|
95
|
+
# Start listening for presence events.
|
96
|
+
putsd "Starting #{@presence_listener.class.to_s}"
|
97
|
+
@presence_listener.start
|
76
98
|
|
77
|
-
#
|
78
|
-
putsd "Starting #{@
|
79
|
-
@
|
99
|
+
# Start sending our own presence events!
|
100
|
+
putsd "Starting #{@presence_broadcaster.class.to_s}"
|
101
|
+
@presence_broadcaster.start
|
80
102
|
end
|
81
103
|
|
82
104
|
def stop
|
83
|
-
#
|
84
|
-
@
|
85
|
-
#
|
86
|
-
@
|
87
|
-
#
|
88
|
-
@
|
105
|
+
# Stop broadcasting presence.
|
106
|
+
@presence_broadcaster.stop
|
107
|
+
# Stop listening for presence events, which prevents new peer registrations
|
108
|
+
@presence_listener.stop
|
109
|
+
# Stop listening for incoming peer data.
|
110
|
+
@peer_listener.stop
|
111
|
+
# Forcefull terminate all connections, which may be actively passing data.
|
112
|
+
@peer_registry.unregister_all
|
89
113
|
end
|
90
114
|
|
91
115
|
def send_to_known_peers(payload)
|
@@ -100,13 +124,17 @@ module Journeta
|
|
100
124
|
|
101
125
|
# Returns metadata on all known peers in a hash, keyed by the uuid of each.
|
102
126
|
# A record corresponding to this peer is not included.
|
103
|
-
def known_peers()
|
104
|
-
peer_registry.
|
127
|
+
def known_peers(all_groups = false)
|
128
|
+
peer_registry.all(all_groups)
|
105
129
|
end
|
106
130
|
|
107
131
|
# Adds (or updates) the given +PeerConnection+.
|
108
132
|
def register_peer(peer)
|
109
|
-
peer_registry.
|
133
|
+
peer_registry.register(peer)
|
134
|
+
end
|
135
|
+
|
136
|
+
def unregister_peer(peer)
|
137
|
+
peer_registry.unregister(peer)
|
110
138
|
end
|
111
139
|
|
112
140
|
end
|
data/lib/journeta/logger.rb
CHANGED
@@ -1,32 +1,63 @@
|
|
1
|
+
# Copyright 2007, OpenRain, LLC. All rights reserved.
|
2
|
+
|
1
3
|
module Journeta
|
2
4
|
|
3
5
|
# An outgoing message tube. Messages may or may not arrive at the destination, but if they do they'll be in order.
|
4
|
-
class PeerConnection
|
6
|
+
class PeerConnection < Journeta::Asynchronous
|
5
7
|
|
6
8
|
include Logger
|
7
9
|
|
8
10
|
attr_accessor :uuid
|
9
11
|
attr_accessor :ip_address
|
10
|
-
attr_accessor :
|
12
|
+
attr_accessor :peer_port
|
11
13
|
attr_accessor :version
|
12
14
|
attr_accessor :created_at
|
13
15
|
attr_accessor :updated_at
|
16
|
+
attr_accessor :groups
|
14
17
|
|
15
|
-
|
18
|
+
def initialize(engine)
|
19
|
+
super(engine)
|
20
|
+
@queue = Queue.new
|
21
|
+
@settings_lock = Mutex.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# Adds the given payload to the outbound message queue and immediately returns.
|
16
25
|
def send_payload(payload)
|
17
26
|
raise "Don't try to send nil payloads!" if payload.nil?
|
18
|
-
|
27
|
+
@queue.push payload
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_settings(other)
|
31
|
+
@settings_lock.synchronize do
|
32
|
+
self.ip_address = other.ip_address
|
33
|
+
self.peer_port = other.peer_port
|
34
|
+
self.version = other.version
|
35
|
+
self.created_at = other.created_at
|
36
|
+
self.updated_at = other.updated_at
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def go
|
19
41
|
begin
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
42
|
+
while true
|
43
|
+
# TODO Reuse TCP connections between pops!
|
44
|
+
payload = @queue.pop
|
45
|
+
s = nil
|
46
|
+
@settings_lock.synchronize do # To prevent corruption of settings.
|
47
|
+
s = TCPSocket.new(ip_address, peer_port)
|
48
|
+
end
|
49
|
+
data = YAML::dump(payload)
|
50
|
+
# pp data
|
51
|
+
s.send(data , 0)
|
52
|
+
s.close
|
53
|
+
end
|
25
54
|
rescue
|
26
|
-
putsd "Peer #{uuid} has gone away."
|
27
|
-
|
55
|
+
putsd "Peer #{uuid} has gone away. Deregistering self."
|
56
|
+
# Yeah... kindof wierd, I know.
|
57
|
+
Thread.new {
|
58
|
+
@engine.unregister_peer(self)
|
59
|
+
}
|
28
60
|
end
|
29
|
-
return ok
|
30
61
|
end
|
31
62
|
|
32
63
|
end
|
@@ -2,11 +2,11 @@ require 'socket'
|
|
2
2
|
|
3
3
|
module Journeta
|
4
4
|
|
5
|
-
class
|
5
|
+
class PeerListener < Journeta::Asynchronous
|
6
6
|
|
7
|
-
def go
|
7
|
+
def go
|
8
8
|
begin
|
9
|
-
port = engine.
|
9
|
+
port = @engine.peer_port
|
10
10
|
socket = TCPServer.new(port)
|
11
11
|
putsd "Listening on port #{port}"
|
12
12
|
|
@@ -21,7 +21,7 @@ module Journeta
|
|
21
21
|
end
|
22
22
|
# pp data
|
23
23
|
msg = YAML::load(data)
|
24
|
-
h = @engine.
|
24
|
+
h = @engine.peer_handler
|
25
25
|
h.handle msg
|
26
26
|
end
|
27
27
|
end
|
@@ -9,7 +9,7 @@ module Journeta
|
|
9
9
|
include Logger
|
10
10
|
|
11
11
|
# {<:uuid> => PeerConnection}
|
12
|
-
attr_reader :peers
|
12
|
+
# attr_reader :peers
|
13
13
|
|
14
14
|
def initialize(engine)
|
15
15
|
@engine = engine
|
@@ -23,7 +23,17 @@ module Journeta
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
26
|
+
def all(all_groups = false)
|
27
|
+
r = nil
|
28
|
+
@mutex.synchronize do
|
29
|
+
r = all_do(all_groups)
|
30
|
+
end
|
31
|
+
return r
|
32
|
+
end
|
33
|
+
|
34
|
+
# Adds a +PeerConnection+ to the registry. It is optional but not necessary to call +PeerConnection#start+ before manually adding it to the registry!
|
35
|
+
# If a peer with the same UUID is already registered, the given peer will be stopped and the existing one updated.
|
36
|
+
def register(peer)
|
27
37
|
raise "Do not try to register a nil peer!" if peer.nil?
|
28
38
|
raise "You can only add #{PeerConnection} instances to this registry, not #{peer.class}!" unless peer.class == PeerConnection
|
29
39
|
@mutex.synchronize do
|
@@ -31,40 +41,84 @@ module Journeta
|
|
31
41
|
existing = @peers[peer.uuid]
|
32
42
|
if existing.nil?
|
33
43
|
putsd "Adding peer #{peer.uuid}."
|
44
|
+
peer.start
|
45
|
+
@peers[peer.uuid] = peer
|
34
46
|
else
|
35
47
|
putsd "Updating peer #{peer.uuid}."
|
48
|
+
peer.stop
|
49
|
+
existing.update_settings peer
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Removes a +PeerConnection+ from the registry. If the peer is still broadcasting presence, it will magically become reregistered at some point!
|
56
|
+
def unregister(peer)
|
57
|
+
return unless !peer.nil? and peer.class == PeerConnection
|
58
|
+
@mutex.synchronize do
|
59
|
+
peer.stop
|
60
|
+
if peer.uuid
|
61
|
+
@peers.delete peer.uuid
|
36
62
|
end
|
37
|
-
@peers[peer.uuid] = peer
|
38
63
|
end
|
39
64
|
end
|
40
65
|
|
41
66
|
def send_to_known_peers(payload)
|
42
67
|
# Iterate over each currently known peer and stuff the payload into each peers outgoing data queue.
|
43
68
|
@mutex.synchronize do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
69
|
+
# Grab all peers in relevant groups.
|
70
|
+
group = all_do
|
71
|
+
n= group.count
|
72
|
+
if n > 0
|
73
|
+
putsd "Sending payload to #{n} peers."
|
74
|
+
group.each do |uuid, conn|
|
75
|
+
conn.send_payload payload
|
51
76
|
end
|
52
77
|
else
|
53
|
-
putsd 'No peers
|
78
|
+
putsd 'No peers (in relevant groups) to send message to!'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Destroys all active connections.
|
84
|
+
def unregister_all
|
85
|
+
@mutex.synchronize do
|
86
|
+
@peers.each do |uuid, n|
|
87
|
+
n.stop
|
54
88
|
end
|
55
89
|
end
|
56
90
|
end
|
57
91
|
|
58
92
|
def send_to_peer(uuid, payload)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
peers.remove uuid
|
93
|
+
@mutex.synchronize do
|
94
|
+
p = @peers[uuid]
|
95
|
+
if p
|
96
|
+
p.send_payload(payload)
|
64
97
|
end
|
65
98
|
end
|
66
99
|
end
|
67
100
|
|
101
|
+
protected
|
102
|
+
|
103
|
+
def all_do(all_groups = false)
|
104
|
+
res = nil
|
105
|
+
if all_groups
|
106
|
+
# Create a new structure to avoid corruption of the original.
|
107
|
+
res = Hash.new.update @peers
|
108
|
+
else
|
109
|
+
res = Hash.new
|
110
|
+
@peers.each do |uuid, n|
|
111
|
+
n.groups.each do |g|
|
112
|
+
if @engine.groups.include?(g)
|
113
|
+
res[uuid] = n
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
return res
|
119
|
+
end
|
120
|
+
|
121
|
+
|
68
122
|
end
|
69
123
|
|
70
124
|
end
|
@@ -0,0 +1,41 @@
|
|
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.presence_address
|
14
|
+
port = @engine.presence_port
|
15
|
+
delay = @engine.presence_period
|
16
|
+
uuid = @engine.uuid
|
17
|
+
peer_port = @engine.peer_port
|
18
|
+
groups = @engine.groups
|
19
|
+
begin
|
20
|
+
socket = UDPSocket.open
|
21
|
+
if PLATFORM[/linux/i]
|
22
|
+
socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, [1].pack("i_") )
|
23
|
+
else
|
24
|
+
# socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, [1].pack('i')) # Preston's original config for OS X.
|
25
|
+
socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, [1].pack("i_") ) # Remi's suggested default.
|
26
|
+
end
|
27
|
+
loop do
|
28
|
+
putsd "Sending presence event."
|
29
|
+
note = PresenceMessage.new uuid, peer_port, groups
|
30
|
+
socket.send(note.to_yaml, 0, address, port)
|
31
|
+
sleep delay
|
32
|
+
end
|
33
|
+
ensure
|
34
|
+
putsd "Closing event broadcaster socket."
|
35
|
+
socket.close
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -10,10 +10,10 @@ module Journeta
|
|
10
10
|
class EventListener < Journeta::Asynchronous
|
11
11
|
|
12
12
|
|
13
|
-
def go
|
14
|
-
|
15
|
-
port = @engine.
|
16
|
-
addresses = IPAddr.new(
|
13
|
+
def go
|
14
|
+
presence_address = @engine.presence_address
|
15
|
+
port = @engine.presence_port
|
16
|
+
addresses = IPAddr.new(presence_address).hton + IPAddr.new("0.0.0.0").hton
|
17
17
|
begin
|
18
18
|
socket = UDPSocket.new
|
19
19
|
# Remember how i said this was fucked up? yeaahhhhhh. i hope you like C.
|
@@ -29,18 +29,17 @@ module Journeta
|
|
29
29
|
data, meta = socket.recvfrom 1024
|
30
30
|
Thread.new(data) {
|
31
31
|
event = YAML.load(data)
|
32
|
-
if event.uuid != @engine.
|
33
|
-
# putsd "New Event: #{data} #{meta.inspect}"
|
34
|
-
# Update registry
|
32
|
+
if event.uuid != @engine.uuid
|
35
33
|
m = YAML::load(data)
|
36
|
-
peer = PeerConnection.new
|
37
|
-
# require 'pp'
|
38
|
-
# pp m
|
34
|
+
peer = PeerConnection.new @engine
|
39
35
|
# Why is this always [2]? Not sure.. they should have returned a hash instead.
|
40
36
|
peer.ip_address = meta[2]
|
41
|
-
peer.
|
37
|
+
peer.peer_port = m.peer_port
|
42
38
|
peer.uuid = m.uuid
|
43
39
|
peer.version = m.version
|
40
|
+
peer.groups = m.groups
|
41
|
+
# TODO validate peer entry is sane before registering it
|
42
|
+
# peer.start
|
44
43
|
@engine.register_peer peer
|
45
44
|
end
|
46
45
|
}
|
@@ -4,14 +4,16 @@ module Journeta
|
|
4
4
|
|
5
5
|
attr_accessor :version
|
6
6
|
attr_accessor :uuid
|
7
|
-
attr_accessor :
|
8
|
-
attr_accessor :online
|
7
|
+
attr_accessor :peer_port
|
9
8
|
|
10
|
-
|
9
|
+
# An Array of strings. May be empty but not nil. Ex: ['quick_chat', 'Preston Demo 1']
|
10
|
+
attr_accessor :groups
|
11
|
+
|
12
|
+
def initialize(uuid, peer_port, groups = [])
|
11
13
|
@version = Journeta::VERSION::STRING
|
12
14
|
@uuid = uuid
|
13
|
-
@
|
14
|
-
@
|
15
|
+
@peer_port = peer_port
|
16
|
+
@groups = groups
|
15
17
|
end
|
16
18
|
|
17
19
|
end
|
data/lib/journeta/version.rb
CHANGED
data/website/index.html
CHANGED
@@ -31,12 +31,12 @@
|
|
31
31
|
<div id="main">
|
32
32
|
|
33
33
|
<h1>Journeta</h1>
|
34
|
-
|
35
|
-
|
36
|
-
<a href="http://rubyforge.org/
|
37
|
-
</div
|
34
|
+
<!--div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/journeta"; return false'>
|
35
|
+
<p>Get Version</p>
|
36
|
+
<a href="http://rubyforge.org/frs/?group_id=4138" class="numbers">sudo gem install journeta</a>
|
37
|
+
</div-->
|
38
38
|
<h2>Ruby <span class="caps">P2P</span> for your <span class="caps">LAN</span>, yo.</h2>
|
39
|
-
|
39
|
+
<a href="http://rubyforge.org/frs/?group_id=4138" class="numbers">[RubyForge Project]</a>
|
40
40
|
|
41
41
|
<p>Journeta is a dirt simple library for peer discovery and message passing between Ruby applications on a <span class="caps">LAN</span>.</p>
|
42
42
|
|
@@ -74,10 +74,10 @@
|
|
74
74
|
Sweeeeet.</p>
|
75
75
|
|
76
76
|
|
77
|
-
<p>Email <a href="mailto:preston.lee@openrain.com">Preston Lee</a> and read the blog.</p>
|
78
77
|
<p class="coda">
|
79
|
-
|
80
|
-
|
78
|
+
<a href="mailto:preston.lee@openrain.com">Preston Email</a>
|
79
|
+
<a href="http://prestonlee.com/">Preston Blog</a>
|
80
|
+
<a href="http://openrain.com/">Preston's Company</a>
|
81
81
|
</p>
|
82
82
|
</div>
|
83
83
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: journeta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Preston Lee
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-08-
|
12
|
+
date: 2008-08-21 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -42,17 +42,22 @@ files:
|
|
42
42
|
- Rakefile
|
43
43
|
- lib/journeta.rb
|
44
44
|
- examples/instant_messenger.rb
|
45
|
+
- examples/queue_client.rb
|
46
|
+
- examples/queue_server.rb
|
45
47
|
- lib/journeta/logger.rb
|
46
48
|
- lib/journeta/version.rb
|
47
49
|
- lib/journeta/asynchronous.rb
|
48
|
-
- lib/journeta/
|
49
|
-
- lib/journeta/
|
50
|
-
- lib/journeta/event_broadcaster.rb
|
51
|
-
- lib/journeta/event_listener.rb
|
52
|
-
- lib/journeta/presence_message.rb
|
50
|
+
- lib/journeta/peer_listener.rb
|
51
|
+
- lib/journeta/peer_handler.rb
|
53
52
|
- lib/journeta/peer_registry.rb
|
54
53
|
- lib/journeta/peer_connection.rb
|
54
|
+
- lib/journeta/presence_broadcaster.rb
|
55
|
+
- lib/journeta/presence_listener.rb
|
56
|
+
- lib/journeta/presence_message.rb
|
55
57
|
- lib/journeta/journeta_engine.rb
|
58
|
+
- lib/journeta/common/dummy_peer_handler.rb
|
59
|
+
- lib/journeta/common/job.rb
|
60
|
+
- lib/journeta/common/shutdown.rb
|
56
61
|
- scripts/txt2html
|
57
62
|
- setup.rb
|
58
63
|
- test/test_journeta.rb
|
@@ -1,36 +0,0 @@
|
|
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
|
-
|