c3s 0.2.0
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/Manifest +12 -0
- data/README.rdoc +0 -0
- data/Rakefile +14 -0
- data/c3s.gemspec +30 -0
- data/lib/c3s.rb +17 -0
- data/lib/c3s_logger.rb +23 -0
- data/lib/component.rb +80 -0
- data/lib/component_connection.rb +72 -0
- data/lib/configreader.rb +46 -0
- data/lib/databaseadapter.rb +19 -0
- data/lib/nodetracker.rb +27 -0
- data/lib/publisher.rb +80 -0
- metadata +79 -0
data/Manifest
ADDED
data/README.rdoc
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('c3s', '0.2.0') do |p|
|
6
|
+
p.description = "C3s library gem."
|
7
|
+
p.url = "http://github.com/rikas/c3s"
|
8
|
+
p.author = "Ricardo Otero"
|
9
|
+
p.email = "oterosantos@gmail.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/c3s.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{c3s}
|
5
|
+
s.version = "0.2.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Ricardo Otero"]
|
9
|
+
s.date = %q{2009-11-30}
|
10
|
+
s.description = %q{C3s library gem.}
|
11
|
+
s.email = %q{oterosantos@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/c3s.rb", "lib/c3s_logger.rb", "lib/component.rb", "lib/component_connection.rb", "lib/configreader.rb", "lib/databaseadapter.rb", "lib/nodetracker.rb", "lib/publisher.rb"]
|
13
|
+
s.files = ["README.rdoc", "Rakefile", "c3s.gemspec", "lib/c3s.rb", "lib/c3s_logger.rb", "lib/component.rb", "lib/component_connection.rb", "lib/configreader.rb", "lib/databaseadapter.rb", "lib/nodetracker.rb", "lib/publisher.rb", "Manifest"]
|
14
|
+
s.homepage = %q{http://github.com/rikas/c3s}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "C3s", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{c3s}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{C3s library gem.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
data/lib/c3s.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'component_connection'
|
2
|
+
require 'component'
|
3
|
+
require 'configreader'
|
4
|
+
require 'databaseadapter'
|
5
|
+
require 'c3s_logger'
|
6
|
+
|
7
|
+
module C3s
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
def ensure_unique(name)
|
13
|
+
begin
|
14
|
+
self[name] = yield
|
15
|
+
end while self.class.exists?(name => self[name])
|
16
|
+
end
|
17
|
+
end
|
data/lib/c3s_logger.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module C3s
|
5
|
+
class C3sLogger
|
6
|
+
attr_accessor :logger
|
7
|
+
|
8
|
+
def initialize(log_file)
|
9
|
+
logger = Logger.new(log_file, "weekly")
|
10
|
+
logger.formatter = C3sLogFormatter.new
|
11
|
+
self.logger = logger
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Formats log messages
|
18
|
+
class C3sLogFormatter < Logger::Formatter
|
19
|
+
def call(severity, time, program_name, message)
|
20
|
+
datetime = time.strftime("%Y-%m-%d %H:%M")
|
21
|
+
"\n#{severity[0..2]} [#{datetime}] #{String(message)}"
|
22
|
+
end
|
23
|
+
end
|
data/lib/component.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'thread'
|
2
|
+
begin
|
3
|
+
require 'xmpp4r'
|
4
|
+
rescue Exception => e
|
5
|
+
puts "Fatal Error: #{e}"
|
6
|
+
puts "Please install xmpp4r gem with 'sudo gem install xmpp4r'"
|
7
|
+
exit
|
8
|
+
end
|
9
|
+
require 'xmpp4r/discovery'
|
10
|
+
require 'xmpp4r/pubsub/helper/nodebrowser'
|
11
|
+
require 'publisher'
|
12
|
+
|
13
|
+
module C3s
|
14
|
+
class Component < Jabber::Component
|
15
|
+
##
|
16
|
+
# Component configuration hash
|
17
|
+
attr_accessor :config
|
18
|
+
|
19
|
+
##
|
20
|
+
# Initializes the jabber component
|
21
|
+
#
|
22
|
+
# config:: [Hash] configuration hash
|
23
|
+
def initialize(config)
|
24
|
+
super(config['jid'])
|
25
|
+
self.config = config
|
26
|
+
Jabber::debug = config['debug'].to_s.eql?"true"
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Connects component to jabber server
|
31
|
+
#
|
32
|
+
# Throws ClientAuthenticationFailure
|
33
|
+
def connect
|
34
|
+
super(config['url'], config['port'])
|
35
|
+
if is_connected?
|
36
|
+
auth(config['password'])
|
37
|
+
else
|
38
|
+
raise Exception, "Can't connect"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Starts listening for packets and waits for activity. Also
|
44
|
+
# provides a callback named handle_iq for especific components
|
45
|
+
# to use.
|
46
|
+
def start
|
47
|
+
super()
|
48
|
+
|
49
|
+
Thread.abort_on_exception = true
|
50
|
+
|
51
|
+
add_iq_callback do |iq|
|
52
|
+
t = Thread.new do
|
53
|
+
handle_iq(iq)
|
54
|
+
end
|
55
|
+
t.join
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Sends an error response to a received IQ packet.
|
61
|
+
# iq:: [Jabber::IQ] the received IQ packet
|
62
|
+
# error:: [Array] an array with error type and error message. The error types should be valid.
|
63
|
+
#
|
64
|
+
# Look at XEP-0086 for explanation:
|
65
|
+
# http://www.xmpp.org/extensions/xep-0086.html
|
66
|
+
def send_error(iq, err)
|
67
|
+
reply = iq.answer
|
68
|
+
reply.type = :error
|
69
|
+
error = Jabber::ErrorResponse.new(err.first, err.last)
|
70
|
+
reply.add error
|
71
|
+
|
72
|
+
send(reply) if iq.type != :error
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Signal.trap('INT') do
|
77
|
+
puts "Got termination signal"
|
78
|
+
exit!
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module C3s
|
2
|
+
class ComponentConnection
|
3
|
+
##
|
4
|
+
# The configuration file
|
5
|
+
CONFIG_FILE = "component.yml"
|
6
|
+
|
7
|
+
def initialize(componentclass)
|
8
|
+
if !componentclass.superclass.eql?(C3s::Component)
|
9
|
+
puts "ERROR: #{componentclass} is not a valid C3s::Component!"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
reader = C3s::ConfigReader.new(CONFIG_FILE)
|
14
|
+
puts "Reading configurations from '#{CONFIG_FILE}'..."
|
15
|
+
@server_config = reader.section('server')
|
16
|
+
@client_config = reader.section('client')
|
17
|
+
|
18
|
+
# a jid parameter is usefull, so lets merge it on client configuration
|
19
|
+
@client_config.merge!('jid' => @client_config['name']+"."+@server_config['url'])
|
20
|
+
@db_config = reader.section('db')
|
21
|
+
|
22
|
+
puts "Creating new location component with jid '#{@client_config['jid']}'..."
|
23
|
+
@component = componentclass.new(@server_config.merge(@client_config))
|
24
|
+
end
|
25
|
+
|
26
|
+
def start
|
27
|
+
puts "Connecting to database '#{@db_config['database']}'"
|
28
|
+
begin
|
29
|
+
# activerecord connection
|
30
|
+
DatabaseAdapter.new(@db_config)
|
31
|
+
# raises exception if not connected
|
32
|
+
ActiveRecord::Base.retrieve_connection
|
33
|
+
rescue Exception => e
|
34
|
+
puts "Error: #{e}"
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
connect()
|
38
|
+
end
|
39
|
+
|
40
|
+
def connect
|
41
|
+
puts "Connecting to #{@server_config['url']} (port #{@server_config['port']})..."
|
42
|
+
|
43
|
+
begin
|
44
|
+
@component.connect
|
45
|
+
rescue Jabber::ComponentAuthenticationFailure => e
|
46
|
+
puts "Could not authenticate on #{@server_config['url']}: #{e}"
|
47
|
+
exit
|
48
|
+
rescue Errno::ECONNREFUSED => e
|
49
|
+
puts "Connection refused on #{@server_config['url']} (port #{@server_config['port']})"
|
50
|
+
exit
|
51
|
+
rescue Exception => e
|
52
|
+
puts "Error do fim do mundo!"
|
53
|
+
puts e.message
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
puts "External component #{@client_config['jid']} connected successfully."
|
57
|
+
|
58
|
+
@component.on_exception do |e, component, where|
|
59
|
+
$LOG.error "FATAL ERROR"
|
60
|
+
$LOG.error e.message
|
61
|
+
$LOG.error e.backtrace.join("\n")
|
62
|
+
end
|
63
|
+
|
64
|
+
puts "Starting messaging loop..."
|
65
|
+
@component.start()
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.stop
|
69
|
+
puts "Stop aqui!!"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/configreader.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module C3s
|
4
|
+
##
|
5
|
+
# The configuration sections on a standard component yml file
|
6
|
+
SECTIONS = ['client', 'server', 'db']
|
7
|
+
|
8
|
+
class ConfigReader
|
9
|
+
##
|
10
|
+
# Reads the configuration file
|
11
|
+
# file::[String] the configuration file
|
12
|
+
def initialize(file)
|
13
|
+
@config = YAML::load_file(File.join(file))
|
14
|
+
check_config
|
15
|
+
rescue Exception => e
|
16
|
+
fatal_error("Could not find the configuration file '#{file}'")
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Returns the client configuration for a given section
|
21
|
+
# section::[String] the section to read (client, server or db)
|
22
|
+
def section(section)
|
23
|
+
if !SECTIONS.include?(section)
|
24
|
+
fatal_error("Trying to read invalid section '#{section}'")
|
25
|
+
end
|
26
|
+
config_hash = {}
|
27
|
+
@config[section].each do |key, val|
|
28
|
+
config_hash.merge!({key.to_s => val.to_s})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def check_config
|
34
|
+
SECTIONS.each do |section|
|
35
|
+
if !@config[section]
|
36
|
+
fatal_error("Configuration file must contain a '#{section}' section.")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def fatal_error(message)
|
42
|
+
$stderr.puts "Error: #{message}"
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module C3s
|
4
|
+
class DatabaseAdapter
|
5
|
+
def initialize(config)
|
6
|
+
ActiveRecord::Base.colorize_logging = false
|
7
|
+
ActiveRecord::Base.logger = Logger.new(File.open('logs/database.log', 'a'))
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(
|
10
|
+
:adapter => config['adapter'],
|
11
|
+
:host => config['host'],
|
12
|
+
:username => config['username'],
|
13
|
+
:password => config['password'],
|
14
|
+
:database => config['database'],
|
15
|
+
:encoding => config['encoding']
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/nodetracker.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module C3s
|
2
|
+
class NodeTracker
|
3
|
+
##
|
4
|
+
# The existing nodes that node tracker knows of
|
5
|
+
@@existing_nodes = []
|
6
|
+
|
7
|
+
##
|
8
|
+
# Adds a node to the existing nodes
|
9
|
+
# node::[String] the node name
|
10
|
+
def self.add_node(node)
|
11
|
+
@@existing_nodes << node
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Checks if a node already exists on node list
|
16
|
+
# node::[String] the node name
|
17
|
+
def self.include?(node)
|
18
|
+
@@existing_nodes.include?(node)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Returns the existing nodes
|
23
|
+
def self.nodes
|
24
|
+
@@existing_nodes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/publisher.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'xmpp4r/pubsub'
|
2
|
+
require 'nodetracker'
|
3
|
+
|
4
|
+
module C3s
|
5
|
+
class Publisher
|
6
|
+
##
|
7
|
+
# Initializes the publisher
|
8
|
+
# client:: [Jabber::Client] the client publishing
|
9
|
+
# service:: [String] pubsub service name (eg: pubsub.jabber)
|
10
|
+
def initialize(client, service_name)
|
11
|
+
@client = client
|
12
|
+
@service = service_name
|
13
|
+
@pubsub = Jabber::PubSub::ServiceHelper.new(@client, @service)
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Creates the collection node(s) and leaf node if needed
|
18
|
+
# nodetree:: [String] node tree (eg: /location/user@jabber.com)
|
19
|
+
def create_nodes(nodetree)
|
20
|
+
nodes = nodetree.split("/")
|
21
|
+
nodes.delete("")
|
22
|
+
|
23
|
+
# all nodes except the last are assumed as collection nodes
|
24
|
+
last_collection = nil
|
25
|
+
nodes[0..nodes.size-2].each do |name|
|
26
|
+
create_collection_node(name) if !node_exists?(name)
|
27
|
+
last_collection = name
|
28
|
+
end
|
29
|
+
|
30
|
+
# last node is assumed as a leaf node
|
31
|
+
leaf = nodetree[1..nodetree.size-1] if nodetree[0..0].eql?"/"
|
32
|
+
leaf.gsub!("/", ":")
|
33
|
+
create_leaf_node(leaf, last_collection) if !node_exists?(leaf)
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Creates the collection node if needed
|
38
|
+
# name:: [String] collection node name
|
39
|
+
def create_collection_node(name)
|
40
|
+
config = Jabber::PubSub::NodeConfig.new
|
41
|
+
@pubsub.create_collection_node(name, config)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Creates a leaf node to publish if needed
|
46
|
+
# This node must be associated with the collection node
|
47
|
+
# name:: [String] the name of the leaf node
|
48
|
+
# collection:: [String] the collection node that contains the node
|
49
|
+
def create_leaf_node(name, collection)
|
50
|
+
config = Jabber::PubSub::NodeConfig.new
|
51
|
+
config.options = config.options.merge({'pubsub#collection' => collection})
|
52
|
+
@pubsub.create_node(name, config)
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Publishes content to the leaf node
|
57
|
+
# node:: [String] the node to plublish to
|
58
|
+
# content:: [REXML::Element] content xml to publish into node
|
59
|
+
def publish(node, content)
|
60
|
+
item = Jabber::PubSub::Item.new
|
61
|
+
item.add(content)
|
62
|
+
node = [@client.config['name'], node].join(":")
|
63
|
+
@pubsub.publish_item_to(node, item)
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Checks for existance of a node with a given name. This
|
68
|
+
# node can be a leaf or a collection
|
69
|
+
# name:: [String] the node name
|
70
|
+
def node_exists?(name)
|
71
|
+
return true if NodeTracker.include?(name)
|
72
|
+
nodebrowser = Jabber::PubSub::NodeBrowser.new(@client)
|
73
|
+
nodebrowser.get_info(@service, name)
|
74
|
+
NodeTracker.add_node(name)
|
75
|
+
true
|
76
|
+
rescue Jabber::ServerError => error
|
77
|
+
return false if error.to_s.include?("item-not-found")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: c3s
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ricardo Otero
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-30 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: C3s library gem.
|
17
|
+
email: oterosantos@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
- lib/c3s.rb
|
25
|
+
- lib/c3s_logger.rb
|
26
|
+
- lib/component.rb
|
27
|
+
- lib/component_connection.rb
|
28
|
+
- lib/configreader.rb
|
29
|
+
- lib/databaseadapter.rb
|
30
|
+
- lib/nodetracker.rb
|
31
|
+
- lib/publisher.rb
|
32
|
+
files:
|
33
|
+
- README.rdoc
|
34
|
+
- Rakefile
|
35
|
+
- c3s.gemspec
|
36
|
+
- lib/c3s.rb
|
37
|
+
- lib/c3s_logger.rb
|
38
|
+
- lib/component.rb
|
39
|
+
- lib/component_connection.rb
|
40
|
+
- lib/configreader.rb
|
41
|
+
- lib/databaseadapter.rb
|
42
|
+
- lib/nodetracker.rb
|
43
|
+
- lib/publisher.rb
|
44
|
+
- Manifest
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/rikas/c3s
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --line-numbers
|
52
|
+
- --inline-source
|
53
|
+
- --title
|
54
|
+
- C3s
|
55
|
+
- --main
|
56
|
+
- README.rdoc
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "1.2"
|
70
|
+
version:
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project: c3s
|
74
|
+
rubygems_version: 1.3.5
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: C3s library gem.
|
78
|
+
test_files: []
|
79
|
+
|