bsl-thor 0.0.2

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/lib/ThorClient.rb ADDED
@@ -0,0 +1,319 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $KCODE="UTF8"
4
+
5
+ # Options parsing gems
6
+ require 'optparse'
7
+ require 'pp'
8
+
9
+ # YAML
10
+ require 'yaml'
11
+
12
+ # Rubygems
13
+ require 'rubygems'
14
+
15
+ # Amqp Gems
16
+ require 'amqp'
17
+ #require 'mq'
18
+
19
+ # Event machine
20
+ require 'eventmachine'
21
+
22
+ require 'socket'
23
+
24
+ require 'digest/md5'
25
+
26
+ # Custom gems
27
+
28
+ require File.join(File.dirname(__FILE__), 'ThorApplication.rb')
29
+ require File.join(File.dirname(__FILE__), 'ThorUtils.rb')
30
+
31
+ require 'Bsl'
32
+
33
+ if $0 == __FILE__
34
+ Thor::require_all_models(true)
35
+ end
36
+
37
+ module Thor
38
+ class AppClient < Thor::Application
39
+ attr_accessor :em_server, :request_exit, :clients, :clients_lock
40
+
41
+ StructCustomer = Struct.new(:pid, :connection)
42
+
43
+ class Server < EventMachine::Connection
44
+ attr_accessor :options, :status
45
+
46
+ def initialize(opts)
47
+ @options = opts
48
+ end
49
+
50
+ def post_init
51
+ @options[:thor_client].em_post_init(self, options)
52
+ end
53
+
54
+ def unbind
55
+ @options[:thor_client].em_unbind(self, options)
56
+ end
57
+
58
+ def receive_data(data)
59
+ @options[:thor_client].em_receive(self, options, status, data)
60
+ end
61
+
62
+ # Stops this connection
63
+ def stop
64
+ puts "AppThorClient::Server::stop() - Not implemented!"
65
+ end
66
+ end
67
+
68
+ def initialize(opts = {})
69
+ super(opts)
70
+
71
+ @@AMQP_DEFAULT_RETRY_INTERVAL = 3
72
+ @@AMQP_MAX_RETRY_INTERVAL = (30)
73
+ @@AMQP_MAX_RETRY_ATTEMPS = -1
74
+ @@AMQP_RETRY_MULTIPLER = 1.5
75
+
76
+ @amqp_retry_interval = @@AMQP_DEFAULT_RETRY_INTERVAL
77
+ @amqp_retry_attempt = 0
78
+
79
+ # Signalizes that application wants exit for some reason
80
+ @request_exit = false
81
+ @em_server = nil
82
+ @clients = {}
83
+ @clients_lock = Mutex.new
84
+
85
+ # AMQP options
86
+ options[:amqp_host] = "localhost"
87
+ options[:amqp_port] = 8467
88
+ options[:amqp_user] = "user"
89
+ options[:amqp_password] = "password"
90
+ options[:amqp_vhost] = "my-vhost"
91
+
92
+ options[:amqp_channel_master] = "master"
93
+
94
+ # Event Machine options
95
+ options[:em_port] = 8467 # Thor on cell-phone keyboard
96
+ options[:em_auth_token] = ""
97
+
98
+ initialize_optparser { |opts|
99
+ ############################
100
+ # AMQP Section
101
+ ############################
102
+ # AMQP Host
103
+ opts.on( '-H', '--amqp-host STRING', "AMQP Server hostname") do |host|
104
+ options[:amqp_host] = host
105
+ end
106
+
107
+ # AMQP Port
108
+ opts.on( '-p', '--amqp-port NUM', "AMQP Server port number") do |port|
109
+ options[:amqp_port] = port
110
+ end
111
+
112
+ # AMQP Username
113
+ opts.on( '-u', '--amqp-user STRING', "AMQP Username") do |user|
114
+ options[:amqp_user] = user
115
+ end
116
+
117
+ # AMQP Password
118
+ opts.on( '-P', '--amqp-password STRING', "AMQP Password") do |password|
119
+ options[:amqp_password] = password
120
+ end
121
+
122
+ # AMQP Vhost
123
+ opts.on( '-V', '--amqp-vhost STRING', "AMQP Virtual Host") do |vhost|
124
+ options[:amqp_vhost] = vhost
125
+ end
126
+
127
+ ## Channels
128
+ # Channel Master
129
+ opts.on( '-acm', '--amqp-channel-master STRING', "AMQP Channel Mastr") do |channel|
130
+ options[:amqp_channel_master] = channel
131
+ end
132
+
133
+ ############################
134
+ # EventMachine Section
135
+ ############################
136
+ # EM Port
137
+ opts.on( '-ep', '--em-port NUM', "EventMachine port") do |port|
138
+ options[:em_port] = port
139
+ end
140
+
141
+ # EM Authentication token
142
+ opts.on( '-eat', '--em-auth-token STRING', "Authentication token used for communication with EM") do |token|
143
+ options[:em_auth_token] = token
144
+ end
145
+ }
146
+ end
147
+
148
+ # Resets internal AMQP connection failure counter/interval
149
+ def amqp_reset_retry_interval
150
+ @amqp_retry_interval = @@AMQP_DEFAULT_RETRY_INTERVAL
151
+ @amqp_retry_attempt = 0
152
+ end
153
+
154
+ # Starts AMQP connection
155
+ def amqp_start
156
+ Bsl::Logger::Log "Starting AMQP - Connecting #{options[:amqp_user]}@#{options[:amqp_host]}:#{options[:amqp_port]}#{options[:amqp_vhost]}"
157
+ AMQP.start(:host => options[:amqp_host], :port => options[:amqp_port], :vhost => options[:amqp_vhost], :user => options[:amqp_user], :password => options[:amqp_password] ) do
158
+ Bsl::Logger::Log "Connected to AMQP broker. Running #{AMQP::VERSION} version of the gem..."
159
+
160
+ amqp_reset_retry_interval()
161
+ em_start()
162
+
163
+ guid = Thor::generate_guid
164
+ amqp_exchange_create_direct(guid) # Create local direct exchange
165
+ amqp_exchange_connect_master(options[:amqp_channel_master], guid)
166
+ end
167
+ end
168
+
169
+ # Stops Running AMQP connection
170
+ def amqp_stop
171
+ Bsl::Logger::Log "Stopping AMQP"
172
+ AMQP.stop { EM.stop }
173
+ end
174
+
175
+ # Creates local direct exchange
176
+ def amqp_exchange_create_direct(guid)
177
+ if(options[:verbose])
178
+ Bsl::Logger::Log "Creating direct exchange '#{guid}'."
179
+ end
180
+ end
181
+
182
+ # Connects to master exchange
183
+ def amqp_exchange_connect_master(master, guid_direct)
184
+ if(options[:verbose])
185
+ Bsl::Logger::Log "GUID '#{guid_direct}', Connecting to master exchange '#{master}'."
186
+ end
187
+ end
188
+
189
+ # Handles failure when connecting to AMQP
190
+ def amqp_handle_failure(e)
191
+ amqp_stop()
192
+
193
+ Bsl::Logger::Log "AMQP Failure, reason: '#{e.inspect}'."
194
+
195
+ if(@request_exit == true)
196
+ return false
197
+ end
198
+
199
+ max_attempts_reached = false
200
+ if(@@AMQP_MAX_RETRY_ATTEMPS != nil && @@AMQP_MAX_RETRY_ATTEMPS >= 0)
201
+ @amqp_retry_attempt = @amqp_retry_attempt + 1
202
+ max_attempts_reached = @amqp_retry_attempt > @@AMQP_MAX_RETRY_ATTEMPS
203
+ end
204
+
205
+ if(max_attempts_reached == false)
206
+ Bsl::Logger::Log "Next attempt in #{@amqp_retry_interval} sec(s)."
207
+
208
+ sleep (@amqp_retry_interval)
209
+ @amqp_retry_interval = @amqp_retry_interval * @@AMQP_RETRY_MULTIPLER
210
+ @amqp_retry_interval = @@AMQP_MAX_RETRY_INTERVAL if @amqp_retry_interval > @@AMQP_MAX_RETRY_INTERVAL
211
+ else
212
+ if(@@AMQP_MAX_RETRY_ATTEMPS != nil)
213
+ Bsl::Logger::Log "Maximum AQMP reconnect attempts limit reached (#{@@AMQP_MAX_RETRY_ATTEMPS}), quitting."
214
+ end
215
+ @request_exit = true
216
+ end
217
+
218
+ return true
219
+ end
220
+
221
+ # Event machine loop
222
+ def em_start
223
+ Bsl::Logger::Log "Starting EventMachine at port #{options[:em_port]}"
224
+ EM.run do
225
+ em_opts = {:thor_client => self}
226
+ EM.start_server 'localhost', options[:em_port], Server, em_opts do |conn|
227
+ em_opts[:thor_client].em_server = conn
228
+ em_opts[:conn] = conn
229
+ conn.options = em_opts
230
+ conn.status = :OK
231
+ end
232
+ end
233
+ end
234
+
235
+ # Stop EventMachine
236
+ def em_stop
237
+ Bsl::Logger::Log "Stopping EventMachine"
238
+ #@em_server.stop
239
+ EventMachine::stop_event_loop
240
+ end
241
+
242
+ # Called when client connects
243
+ def em_post_init(em_connection, em_options)
244
+ pid = Digest::MD5.hexdigest(em_connection.to_s) # em_connection.get_pid
245
+ port, ip = Socket.unpack_sockaddr_in(em_connection.get_peername)
246
+ if(options[:verbose])
247
+ Bsl::Logger::Log "Client connected #{ip}:#{port}, pid: #{pid}"
248
+ end
249
+
250
+ @clients_lock.synchronize {
251
+ c = StructCustomer.new(pid, em_connection)
252
+ @clients[pid] = c
253
+ #puts "Clients now: #{@clients.size}"
254
+ }
255
+ end
256
+
257
+ # Called when client disconnects
258
+ def em_unbind(em_connection, em_options)
259
+ pid = Digest::MD5.hexdigest(em_connection.to_s) # em_connection.get_pid
260
+ if(@options[:verbose])
261
+ Bsl::Logger::Log "Client disconnected, pid: #{pid}"
262
+ end
263
+
264
+ @clients_lock.synchronize {
265
+ @clients.delete(pid)
266
+ #puts "Clients now: #{@clients.size}"
267
+ }
268
+ end
269
+
270
+ # Event machine receive handle
271
+ def em_receive(em_connection, em_options, status, raw_data)
272
+ port, ip = Socket.unpack_sockaddr_in(em_connection.get_peername)
273
+ if(options[:verbose])
274
+ Bsl::Logger::Log "EventMachine received data from #{ip}:#{port}, :status => '#{status}', :raw_data => #{raw_data.chomp}"
275
+ end
276
+
277
+ data = nil
278
+ begin
279
+ data = JSON.parse(raw_data)
280
+ rescue Exception => e
281
+ Bsl::Logger::Log "Unable to parse incomming json, reason: #{e.message}."
282
+ return false
283
+ end
284
+
285
+ if(data['type'] == "system" && data['code'] == "stop")
286
+ @request_exit = true
287
+ em_stop()
288
+ amqp_stop()
289
+ end
290
+
291
+ return true
292
+ end
293
+
294
+ # Main entry-point
295
+ def main
296
+ super()
297
+
298
+ # Run loop while exit is not requested
299
+ while(@request_exit == false)
300
+ begin
301
+ amqp_start()
302
+ rescue SystemExit, Interrupt
303
+ Bsl::Logger::Log "Received interrupt, quitting!"
304
+ @request_exit = true
305
+ #em_stop()
306
+ amqp_stop()
307
+ rescue Exception => e
308
+ amqp_handle_failure(e)
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ if $0 == __FILE__
316
+ Thor::AppClient.new.main
317
+ else
318
+ Thor::AppClient.new.libmain
319
+ end
data/lib/ThorCmd.rb ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $KCODE="UTF8"
4
+
5
+ # Options parsing gems
6
+ require 'optparse'
7
+ require 'pp'
8
+
9
+ # YAML
10
+ require 'yaml'
11
+
12
+ # Rubygems
13
+ require 'rubygems'
14
+
15
+
16
+ # Event machine
17
+ require 'eventmachine'
18
+
19
+ # Custom gems
20
+ require File.join(File.dirname(__FILE__), 'ThorApplication.rb')
21
+ require 'Bsl'
22
+
23
+ module Thor
24
+ class AppCmd < Thor::Application
25
+ attr_accessor :options, :optpars, :request_exit
26
+
27
+ def initialize(opts = {})
28
+ super(ops)
29
+
30
+ # AMQP options
31
+ options[:amqp_host] = "localhost"
32
+ options[:amqp_port] = 1234
33
+ options[:amqp_user] = "user"
34
+ options[:amqp_password] = "password"
35
+ options[:amqp_vhost] = "my-vhost"
36
+
37
+ options[:amqp_channel_master] = "master"
38
+
39
+ initialize_optparser { |opts|
40
+ ############################
41
+ # AMQP Section
42
+ ############################
43
+ # AMQP Host
44
+ opts.on( '-H', '--amqp-host STRING', "AMQP Server hostname") do |host|
45
+ options[:amqp_host] = host
46
+ end
47
+
48
+ # AMQP Port
49
+ opts.on( '-p', '--amqp-port NUM', "AMQP Server port number") do |port|
50
+ options[:amqp_port] = port
51
+ end
52
+
53
+ # AMQP Username
54
+ opts.on( '-u', '--amqp-user STRING', "AMQP Username") do |user|
55
+ options[:amqp_user] = user
56
+ end
57
+
58
+ # AMQP Password
59
+ opts.on( '-P', '--amqp-password STRING', "AMQP Password") do |password|
60
+ options[:amqp_password] = password
61
+ end
62
+
63
+ # AMQP Vhost
64
+ opts.on( '-V', '--amqp-vhost STRING', "AMQP Virtual Host") do |vhost|
65
+ options[:amqp_vhost] = vhost
66
+ end
67
+
68
+ ## Channels
69
+ # Channel Master
70
+ opts.on( '-acm', '--amqp-channel-master STRING', "AMQP Channel Mastr") do |channel|
71
+ options[:amqp_channel_master] = channel
72
+ end
73
+ }
74
+ end
75
+
76
+ # Main entry-point
77
+ def main
78
+ super()
79
+ end
80
+ end
81
+ end
82
+
83
+ if $0 == __FILE__
84
+ Thor::AppCmd.new.main
85
+ end
data/lib/ThorJob.rb ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $KCODE="UTF8"
4
+
5
+ # Options parsing gems
6
+ require 'optparse'
7
+ require 'pp'
8
+
9
+ # YAML
10
+ require 'yaml'
11
+
12
+ # Rubygems
13
+ require 'rubygems'
14
+
15
+ # Event machine
16
+ require 'eventmachine'
17
+
18
+ # Custom gems
19
+ require File.join(File.dirname(__FILE__), 'ThorApplication.rb')
20
+ require File.join(File.dirname(__FILE__), 'ThorUtils.rb')
21
+ require 'Bsl'
22
+
23
+ module Thor
24
+ class Job < Thor::Application
25
+ attr_accessor :thread, :amqp_conn, :em_conn, :sql_conn
26
+
27
+ @@AUTHOR = "<no author>"
28
+ @@DESCRIPTION = "<no description>"
29
+ @@LICENSE = "<no license>"
30
+ @@VERSION = "<no version>"
31
+ @@SUPPORTED_MSGS = []
32
+
33
+ def self.author
34
+ return @@AUTHOR
35
+ end
36
+
37
+ def self.description
38
+ return @@DESCRIPTION
39
+ end
40
+
41
+ def self.license
42
+ return @@LICENSE
43
+ end
44
+
45
+ def self.version
46
+ return @@VERSION
47
+ end
48
+
49
+ def self.supported_msgs
50
+ return @@SUPPORTED_MSGS
51
+ end
52
+
53
+ # C-tor
54
+ def initialize(opts = {})
55
+ super(opts)
56
+
57
+ @thread = nil
58
+ @amqp_conn = nil
59
+ @em_conn = nil
60
+ @sql_conn = nil
61
+
62
+ klass_name = self.class.name
63
+
64
+ name_exchange_direct_global = "#{klass_name}.direct"
65
+ name_exchange_direct_local= "#{klass_name}.direct.#{Thor::generate_guid}"
66
+ name_exchange_fanout = "#{klass_name}.fanout"
67
+ name_exchange_topic = "#{klass_name}.topic"
68
+
69
+ if(false && options[:verbose])
70
+ Bsl::Logger::Log "#{klass_name} exchange direct (global) name: #{name_exchange_direct_global}"
71
+ Bsl::Logger::Log "#{klass_name} exchange direct (local) name: #{name_exchange_direct_local}"
72
+ Bsl::Logger::Log "#{klass_name} exchange fanout name: #{name_exchange_fanout}"
73
+ Bsl::Logger::Log "#{klass_name} exchange topic name: #{name_exchange_topic}"
74
+ end
75
+ end
76
+
77
+ # Called when running as app
78
+ def main()
79
+ @thread = Thread.new do
80
+ run(options)
81
+ end
82
+ @thread.join!
83
+ end
84
+
85
+ # Process Message
86
+ def process_msg(channel, exchange, msg)
87
+ if(options[:verbose])
88
+ Bsl::Logger::Log "Thor::Job::process_msg()"
89
+ end
90
+ end
91
+
92
+ # Start - Called when included in library
93
+ def run(opts = {})
94
+ super(opts)
95
+
96
+ if(options[:verbose])
97
+ Bsl::Logger::Log "Thor::Job::run()"
98
+ end
99
+
100
+ # Extract raw AMQP connection
101
+ tmp = options[:amqp]
102
+ amqp_conn = tmp[:conn] if tmp
103
+
104
+ # Extract raw Event Machine connection
105
+ tmp = options[:em]
106
+ em_conn = tmp[:conn] if tmp
107
+
108
+ # Extract raw SQL connection
109
+ tmp = options[:sql]
110
+ sql_conn = tmp[:conn] if tmp
111
+
112
+ #sleep 10
113
+ end
114
+
115
+ # Start - Called when included in library
116
+ def start(opts = {}, wait = false)
117
+ #puts "#{options[:amqp].inspect}"
118
+ @thread = Thread.new do
119
+ #puts opts.inspect
120
+ self.run(opts)
121
+ end
122
+
123
+ if wait
124
+ @thread.join!
125
+ return true
126
+ end
127
+ return true
128
+ end
129
+
130
+ # Stop - Called when included in library
131
+ def stop(opts = {})
132
+ return false if @thread == nil
133
+ @thread.kill!
134
+ @thread = nil
135
+ end
136
+
137
+ # Checks if is thread running
138
+ def is_running?
139
+ return (@thread != nil)
140
+ end
141
+ end
142
+ end
143
+
144
+ if $0 == __FILE__
145
+
146
+ end