bsl-thor 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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