homeq 1.1.4

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.
Files changed (63) hide show
  1. data/CHANGELOG +103 -0
  2. data/COPYING +348 -0
  3. data/README.rdoc +64 -0
  4. data/Rakefile +131 -0
  5. data/bin/hq +6 -0
  6. data/config/boot.rb +224 -0
  7. data/config/databases/frontbase.yml +28 -0
  8. data/config/databases/mysql.yml +54 -0
  9. data/config/databases/oracle.yml +39 -0
  10. data/config/databases/postgresql.yml +48 -0
  11. data/config/databases/sqlite2.yml +16 -0
  12. data/config/databases/sqlite3.yml +19 -0
  13. data/config/environment.rb +20 -0
  14. data/config/environments/development.cfg +35 -0
  15. data/config/environments/production.cfg +35 -0
  16. data/config/environments/test.cfg +35 -0
  17. data/config/generators/job/templates/job.rb.erb +20 -0
  18. data/config/generators/message/templates/messages/MESSAGE.proto.erb +12 -0
  19. data/config/generators/model/templates/models/MODEL.rb.erb +3 -0
  20. data/config/generators/service/templates/services/SERVICE.rb.erb +43 -0
  21. data/config/homeq.cfg +35 -0
  22. data/extras/consumer.rb +85 -0
  23. data/extras/homeq.cfg +49 -0
  24. data/extras/hqd.rb +33 -0
  25. data/extras/producer.rb +79 -0
  26. data/extras/simple_consumer.rb +53 -0
  27. data/lib/homeq/base/base.rb +44 -0
  28. data/lib/homeq/base/commando.rb +81 -0
  29. data/lib/homeq/base/config.rb +99 -0
  30. data/lib/homeq/base/exception.rb +48 -0
  31. data/lib/homeq/base/histogram.rb +141 -0
  32. data/lib/homeq/base/logger.rb +185 -0
  33. data/lib/homeq/base/ohash.rb +297 -0
  34. data/lib/homeq/base/options.rb +171 -0
  35. data/lib/homeq/base/poolable.rb +100 -0
  36. data/lib/homeq/base/system.rb +446 -0
  37. data/lib/homeq/cli.rb +35 -0
  38. data/lib/homeq/cp/commands.rb +71 -0
  39. data/lib/homeq/cp/connection.rb +97 -0
  40. data/lib/homeq/cp/cp.rb +30 -0
  41. data/lib/homeq/cp/server.rb +105 -0
  42. data/lib/homeq/sobs/client.rb +119 -0
  43. data/lib/homeq/sobs/connection.rb +635 -0
  44. data/lib/homeq/sobs/foreman.rb +237 -0
  45. data/lib/homeq/sobs/job.rb +66 -0
  46. data/lib/homeq/sobs/message.rb +49 -0
  47. data/lib/homeq/sobs/queue.rb +224 -0
  48. data/lib/homeq/sobs/sender.rb +150 -0
  49. data/lib/homeq/sobs/server.rb +654 -0
  50. data/lib/homeq/sobs/sobs.rb +45 -0
  51. data/lib/homeq/sobs/topology.rb +111 -0
  52. data/lib/homeq.rb +106 -0
  53. data/lib/tasks/Rakefile +49 -0
  54. data/lib/tasks/database.rake +387 -0
  55. data/lib/tasks/gem.rake +9 -0
  56. data/lib/tasks/generate.rake +192 -0
  57. data/lib/tasks/hq.rake +171 -0
  58. data/lib/tasks/testing.rake +95 -0
  59. data/lib/tasks/utility.rb +17 -0
  60. data/script/console.rb +45 -0
  61. data/script/generate +7 -0
  62. data/test/unittest.rb +51 -0
  63. metadata +222 -0
@@ -0,0 +1,654 @@
1
+ #############################################################################
2
+ #
3
+ # $Id: server.rb 48 2008-11-19 05:11:59Z colin $
4
+ #
5
+ # Author:: Colin Steele (colin@colinsteele.org)
6
+ # Homepage::
7
+ #
8
+ # TODO: info
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2008 by Colin Steele. All Rights Reserved.
13
+ # colin@colinsteele.org
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #############################################################################
26
+
27
+ module HomeQ
28
+
29
+ module SOBS
30
+
31
+ ######################################################################
32
+ # Server Job
33
+ ######################################################################
34
+ #
35
+ # This class is the server-side representation of a message, or a
36
+ # "job" - a piece of work that somebody sent, that somebody will
37
+ # eventually pick up and do something with.
38
+ #
39
+ # On the server side, the job lifecycle looks like this:
40
+ #
41
+ # put with delay release with delay
42
+ # ----------------> [DELAYED] <------------.
43
+ # | |
44
+ # |(time passes) |
45
+ # | |
46
+ # put v reserve | delete
47
+ # -----------------> [READY] ---------> [RESERVED] --------> *poof*
48
+ # ^ ^ | |
49
+ # | \ release | |
50
+ # | `---------------' |
51
+ # | |
52
+ # | kick |
53
+ # | |
54
+ # | bury |
55
+ # [BURIED] <----------------'
56
+ # |
57
+ # | delete
58
+ # `--------> *poof*
59
+ #
60
+ # In the simplest case, jobs go from start to ready to reserved to
61
+ # *poof*.
62
+ #
63
+ class ServerJob < SOBS::Job
64
+
65
+ # The Foreman watches jobs
66
+ include Observable
67
+
68
+ # We have to carp.
69
+ include Base::Logging
70
+
71
+ # Object pool
72
+ include Poolable
73
+ pool_init(10000)
74
+
75
+ # Time To Run
76
+ attr :ttr, true
77
+
78
+ # How important? Not implemented.
79
+ attr :priority, true
80
+
81
+ # Delay N seconds before ready
82
+ attr :delay, true
83
+
84
+ # Connection working on this job
85
+ attr :reserving_connection
86
+
87
+ # A count of the total number of jobs that have had a life in
88
+ # the system.
89
+ @@job_count = 0
90
+
91
+ # Create a new ServerJob. Called by Foreman#create_job.
92
+ # A Job is based on receipt of a message, from a source, and it
93
+ # is managed by a foreman. The message is assumed to have three
94
+ # args: priority (ignored), delay, and ttr (time to run).
95
+ def initialize(message, source)
96
+ logger.debug4 {
97
+ 'INIT'
98
+ }
99
+ super(message, source)
100
+ @@job_count += 1
101
+ @priority, @delay, @ttr = @message.args.collect { |arg|
102
+ arg.to_i
103
+ }
104
+
105
+ # Create a state machine to drive.
106
+ @state = Statemachine.build do
107
+ state :start do
108
+ event :put, :ready
109
+ event :put_with_delay, :delayed
110
+ end
111
+ state :delayed do
112
+ on_entry :start_delay_timer
113
+ event :delay_timeout, :ready
114
+ end
115
+ state :ready do
116
+ on_entry :broadcast_change
117
+ event :reserve, :reserved
118
+ end
119
+ state :reserved do
120
+ on_entry :start_run_timer
121
+ event :delete, :deleted
122
+ event :release, :ready
123
+ event :run_timeout, :ready
124
+ event :release_with_delay, :delayed
125
+ event :bury, :buried
126
+ on_exit :exit_reserved_state
127
+ end
128
+ state :buried do
129
+ on_entry :broadcast_change
130
+ event :delete, :deleted
131
+ event :kick, :ready
132
+ end
133
+ state :deleted do
134
+ on_entry :broadcast_change
135
+ end
136
+ end
137
+ @state.context = self
138
+ end
139
+
140
+ def deinitialize
141
+ logger.debug4 {
142
+ 'DEINIT'
143
+ }
144
+ # first take care of superclass's vars
145
+ @message = @queue = @job_id = nil
146
+
147
+ # now ours
148
+ instance_variables.each {|v|
149
+ next if v == "@state" # keeper. largely the point of recycling
150
+ next if v =~ /observer/ # keeper. our observer(s)
151
+ eval "#{v} = nil"
152
+ }
153
+ @state.state = :start
154
+ end
155
+
156
+ def reinitialize(message, source)
157
+ # Call super's initialize().
158
+ SOBS::Job.instance_method(:initialize).bind(self).call(message,source)
159
+ @@job_count += 1
160
+ @priority, @delay, @ttr = @message.args.collect { |arg|
161
+ arg.to_i
162
+ }
163
+ logger.debug4 {
164
+ "REINIT #{@state.state}"
165
+ }
166
+ end
167
+
168
+ def run
169
+ logger.debug4 {
170
+ 'LAUNCH'
171
+ }
172
+ broadcast_change
173
+ if delay > 0
174
+ put_with_delay
175
+ else
176
+ put
177
+ end
178
+ end
179
+
180
+ # Takes this job from the start state to ready.
181
+ def put
182
+ @state.put
183
+ end
184
+
185
+ # Takes this job from the start state to delayed. As a
186
+ # consequence, with kick off a timer that will move this job
187
+ # from delayed to ready when it expires.
188
+ def put_with_delay
189
+ @state.put_with_delay
190
+ end
191
+
192
+ # Delete this job (*poof*). A 'deleted' reply is sent to the
193
+ # connection that sent the delete (which may or may not be the
194
+ # connection that reserved the job in the first place).
195
+ def delete(conn)
196
+ conn.deleted(job_id) # may or may not be @reserving_connection
197
+ @state.delete
198
+ end
199
+
200
+ # Mark this job as being processed. The reserving connection is
201
+ # sent a 'reserved' reply along with the contents of this job.
202
+ # We store a notion of the reserving connection, and push this
203
+ # job from the 'ready' state to the 'reserved' state.
204
+ def reserve(conn)
205
+ @state.reserve
206
+ conn.reserved(job_id, payload)
207
+ @reserving_connection = conn
208
+ end
209
+
210
+ # Take this job out of the "being worked on" state and back to
211
+ # availability in the 'ready' state.
212
+ def release
213
+ @state.release
214
+ end
215
+
216
+ # Take this job out of the "being worked on" state and back to
217
+ # availability in the 'ready' state, but after going through a
218
+ # 'delayed' state. We kick off a timer that moves the job from
219
+ # 'delayed' to 'ready' when it expires.
220
+ def release_with_delay(priority, delay)
221
+ @priority = priority
222
+ @delay = delay
223
+ @state.release_with_delay
224
+ end
225
+
226
+ # Push this job off to the side so it is not available to be
227
+ # worked on.
228
+ def bury(conn)
229
+ conn.buried(job_id) # may or may not be @reserving_connection
230
+ @state.bury
231
+ end
232
+
233
+ # Move this job from the 'buried' state (see #bury, above) to
234
+ # the 'ready' state.
235
+ def kick
236
+ @state.kick
237
+ end
238
+
239
+ protected
240
+
241
+ def start_delay_timer
242
+ broadcast_change
243
+ @delay_timer = EventMachine::Timer.new(@delay.to_i) do
244
+ @delay_timer = nil
245
+ @state.delay_timeout
246
+ end
247
+ end
248
+
249
+ def start_run_timer
250
+ broadcast_change
251
+ return if @ttr < 1
252
+ start_deadline_soon_timer
253
+ @run_timer = EventMachine::Timer.new(@ttr.to_i) do
254
+ logger.info {
255
+ peer = @reserving_connection.remote_endpoint
256
+ "Job #{job_id} run timeout on SOBS Peer #{peer}."
257
+ }
258
+ @run_timer = nil
259
+ @state.run_timeout
260
+ end
261
+ end
262
+
263
+ def start_deadline_soon_timer
264
+ @deadline_soon_timer = EventMachine::Timer.new(@ttr - 1) do
265
+ @deadline_soon_timer = nil
266
+ if !@reserving_connection
267
+ msg = "Logic error: nil reserving connection."
268
+ Base::System.instance.die(msg)
269
+ end
270
+ if @reserving_connection.state.state == :open
271
+ @reserving_connection.deadline_soon(job_id)
272
+ end
273
+ end
274
+ end
275
+
276
+ def exit_reserved_state
277
+ @run_timer.cancel if @run_timer
278
+ @deadline_soon_timer.cancel if @deadline_soon_timer
279
+ @deadline_soon_timer = nil
280
+ @run_timer = nil
281
+ @reserving_connection = nil
282
+ end
283
+
284
+ def broadcast_change
285
+ changed
286
+ notify_observers(self, @state.state)
287
+ end
288
+
289
+ end
290
+
291
+
292
+ ######################################################################
293
+ # Server Connection
294
+ ######################################################################
295
+ #
296
+ # This class represents the server-side of a connection between a
297
+ # SOBS client and the SOBS server.
298
+ #
299
+ class ServerConnection < SOBS::Connection
300
+
301
+ # This connection's state
302
+ attr :server_state, false
303
+
304
+ # Job handler
305
+ attr :handler, true
306
+
307
+ # Create a new ServerConnection, which is a child of SOBS::Connection.
308
+ # Called from Server#start via EventMachine. When an incoming
309
+ # TCP connection is received, a ServerConnection is created to
310
+ # handle it.
311
+ def initialize
312
+ super
313
+
314
+ # The state machine that drives our behavior.
315
+
316
+ @server_state = Statemachine.build do
317
+ state :start do
318
+ event :open, :open
319
+ event :close, :closed
320
+ end
321
+ state :waiting_for_job do
322
+ on_entry :reserve_ready_job
323
+ event :sent_job, :open
324
+ event :reserve, :waiting_for_job
325
+ event :received_job, :waiting_for_job, :reserve_ready_job
326
+ event :subscribe, :waiting_for_job
327
+ event :unsubscribe, :open
328
+ event :close, :closed
329
+ end
330
+ state :open do
331
+ event :subscribe, :waiting_for_job
332
+ event :unsubscribe, :open
333
+ event :reserve, :waiting_for_job
334
+ event :received_job, :open
335
+ event :close, :closed
336
+ end
337
+ state :closed do
338
+ event :close, :closed
339
+ end
340
+ end
341
+ @server_state.context = self
342
+
343
+ # stats
344
+ @jobs_received = 0
345
+
346
+ # An attribute of this connection. If true, the client has
347
+ # indicated that they want to be sent a ready job whenever
348
+ # it's available. Otherwise, they must ask for each job via
349
+ # the 'reserve' command.
350
+ @subscribed = nil
351
+ end
352
+
353
+ #--
354
+ # SOBS::Connection entry points
355
+ #++
356
+
357
+ # Called from the lower-level Connection abstraction when a
358
+ # client connection is complete and ready to use.
359
+ def opened
360
+ @server_state.open
361
+ end
362
+
363
+ # Called from the lower-level Connection abstraction when a
364
+ # client connection is dead.
365
+ def closed
366
+ logger.info {
367
+ "Job pool size: #{ServerJob.pool.size}"
368
+ }
369
+ @server_state.close
370
+ end
371
+
372
+
373
+ #--
374
+ # High level message interface - sending
375
+ #++
376
+
377
+ # Send a 'reserved' message to the remote endpoint, indicating
378
+ # that it's now in charge of doing the indicated piece of work.
379
+ # Track the server state appropriately (switch state to 'open'
380
+ # unless the remote endpoint has subscribed).
381
+ def reserved(job_id, payload)
382
+ super(job_id, payload) # send the msg to the client
383
+ @server_state.sent_job unless @subscribed
384
+ end
385
+
386
+ #--
387
+ # High level message interface - receiving
388
+ #++
389
+
390
+ # Handle a 'put' message from the remote endoint. We create a
391
+ # message and reply to let the other side know we got it.
392
+ def receive_put(message)
393
+ if !ok_to_receive?
394
+ logger.error {
395
+ "Connection not ready for 'put'"
396
+ }
397
+ close
398
+ return
399
+ end
400
+ if message.args.length != 5
401
+ logger.error {
402
+ "Malformed put from SOBS peer #{remote_endpoint}. " +
403
+ "Closing connection."
404
+ }
405
+ close
406
+ return
407
+ end
408
+ create_job(message)
409
+ @server_state.received_job
410
+ end
411
+
412
+ # Handle a 'reserve' message from the client, which says that
413
+ # they're ready for work to be sent to them.
414
+ def receive_reserve(message)
415
+ if !ok_to_receive?
416
+ logger.error {
417
+ "Connction not ready to 'receive'"
418
+ }
419
+ return
420
+ end
421
+ @server_state.reserve
422
+ end
423
+
424
+ # Handle a 'delete' message from the client, which says that
425
+ # they've completed the work. We let the foreman know.
426
+ def receive_delete(message)
427
+ ok_to_receive? || return
428
+ @server.foreman.delete_job(self, message.args[0])
429
+ end
430
+
431
+ # Handle a 'release' message from the client, which says that
432
+ # they're not going to finish (or delete) the work, and to make
433
+ # it available for someone else to work on.
434
+ def receive_release(message)
435
+ ok_to_receive? || return
436
+ @server.foreman.release_job(self,
437
+ message.args[0],
438
+ message.args[1],
439
+ message.args[2])
440
+ end
441
+
442
+ # Handle a 'bury' message from the client, which says that
443
+ # the job shouldn't be available for anyone.
444
+ def receive_bury(message)
445
+ ok_to_receive? || return
446
+ @server.foreman.bury_job(self, message.args[0])
447
+ end
448
+
449
+ # Handle a 'kick' message from the client, which move some
450
+ # number of buried jobs to the 'ready' state.
451
+ def receive_kick(message)
452
+ ok_to_receive? || return
453
+ @server.foreman.kick(self, message.args[0])
454
+ end
455
+
456
+ # Handle a 'subscribe' message from the client, which tells us
457
+ # that they want to be issued work to do whenever it's available.
458
+ def receive_subscribe(message)
459
+ @subscribed = true
460
+ @server_state.subscribe
461
+ end
462
+
463
+ # Handle a 'unsubscribe' message from the client, which tells us
464
+ # that they want to send explicit requests for more work via
465
+ # 'reserve' commands.
466
+ def receive_unsubscribed(message)
467
+ @subscribed = false
468
+ @server_state.unsubscribed
469
+ end
470
+
471
+ #--
472
+ # Other
473
+ #++
474
+
475
+ def open
476
+ super
477
+ @server_state.open
478
+ end
479
+
480
+ def unknown_message(command_name)
481
+ logger.info {
482
+ "SOBS peer #{remote_endpoint} sent unknown/unsupported " +
483
+ "command: #{command_name}"
484
+ }
485
+ close
486
+ end
487
+
488
+ def release_job(job)
489
+ end
490
+
491
+ def to_s
492
+ str = ""
493
+ str << "#{self.class} state: #{server_state.state} "
494
+ str << "subscribed?: #{@subscribed ? 'Y' : 'N'}\n"
495
+ str << "jobs in: #{@jobs_received} "
496
+ str << "job pool size: #{ServerJob.pool ? ServerJob.pool.size : '?'}\n"
497
+ str << super.gsub(/\n/m, "\n ")
498
+ end
499
+
500
+ protected
501
+
502
+ def reserve_ready_job
503
+ @server.foreman.dole_out_jobs
504
+ end
505
+
506
+ def create_job(message)
507
+ logger.debug4 {
508
+ "Server handler: #{@handler}"
509
+ }
510
+ if @handler
511
+ if @handler.respond_to?(:call)
512
+ @handler.call(message, self)
513
+ elsif @handler.respond_to?(:create_job)
514
+ @handler.create_job(message, self)
515
+ elsif @handler.respond_to?(:new)
516
+ job = @handler.new(message, self)
517
+ if job.respond_to?(:run)
518
+ job.run
519
+ end
520
+ end
521
+ else
522
+ @jobs_received += 1
523
+ @server.foreman.create_job(message, self)
524
+ end
525
+ end
526
+
527
+ def get_next_message
528
+ reserve
529
+ end
530
+
531
+ end # ServerConnection
532
+
533
+
534
+ ######################################################################
535
+ # Server / Listener class
536
+ ######################################################################
537
+
538
+ class Server
539
+
540
+ DEFAULT_PORT = 56000
541
+ DEFAULT_HOST = 'localhost'
542
+
543
+ include Base::Logging
544
+ include Base::Configuration
545
+
546
+ attr_accessor :connections
547
+ attr :foreman, true
548
+ attr :queue_name, true
549
+ attr :total_connections, false
550
+
551
+ # Make our config available
552
+ module HomeQ::Base::Commando::InstanceMethods
553
+ config_accessor :queue_name
554
+ end
555
+
556
+ def self.create_home_queue(queuename, handler=nil)
557
+ logger = HomeQ::Base::Logging::Logger.instance.logger
558
+ config = HomeQ::Base::Configuration::Configuration.instance
559
+ if queuename =~ /^__/
560
+ return
561
+ elsif !config.topology[queuename]
562
+ Base::System.instance.die("No queue info for '#{queuename}'")
563
+ end
564
+ logger.info {
565
+ "Starting home queue '#{queuename}'" +
566
+ (handler ? ", handler: #{handler}" : '')
567
+ }
568
+ q, host, port, hostname = config.topology[queuename]
569
+ server = HomeQ::SOBS::Server.new(host, port, queuename, handler)
570
+ Base::System.instance.servers << server
571
+ end
572
+
573
+ def initialize(host=nil, port=nil, queuename=nil, handler=nil)
574
+ @host = host
575
+ @port = port
576
+ @connections = []
577
+ @foreman = nil
578
+ @queue_name = queuename
579
+ @total_connections = 0
580
+ @started_at = nil
581
+ @stopped_at = nil
582
+ @handler = handler
583
+ end
584
+
585
+ def start
586
+ @started_at = Time.now
587
+ @foreman ||= Foreman.new(self)
588
+ begin
589
+ @signature =
590
+ EventMachine.start_server(@host,
591
+ @port,
592
+ HomeQ::SOBS::ServerConnection) { |conn|
593
+ conn.server = self
594
+ conn.handler = @handler
595
+ @connections << conn
596
+ @total_connections += 1
597
+ }
598
+ rescue Exception => e
599
+ msg = "Error starting SOBS listener '#{@host}:#{@port}'." +
600
+ "Probably a config error. Original error: #{e.message}."
601
+ Base::System.instance.die(msg)
602
+ else
603
+ logger.info {
604
+ "SOBS server running on #{@host}:#{@port}"
605
+ }
606
+ end
607
+ end
608
+
609
+ def stop
610
+ @stopped_at = Time.now
611
+ EventMachine.stop_server(@signature)
612
+ unless wait_for_connections
613
+ # Still some running
614
+ EventMachine.add_periodic_timer(1) {
615
+ wait_for_connections
616
+ }
617
+ end
618
+ end
619
+
620
+ def to_s
621
+ str = ''
622
+ str << "#{self.class} Queuename: #{@queue_name}"
623
+ str << " on #{@host}:#{@port}" if @host || @port
624
+ str << "\n"
625
+ str << "Started: #{@started_at} (#{Time.now - @started_at}s)\n"
626
+ str << "Total connections: #{@total_connections} "
627
+ str << "Active connections: #{connections.length}"
628
+ str << ("\n" + connections.join("\n")).gsub(/\n/m, "\n ")
629
+ str << ("\n" + @foreman.to_s).gsub(/\n/m, "\n ") if @foreman
630
+ str
631
+ end
632
+
633
+ protected
634
+
635
+ def wait_for_connections
636
+ if @connections.empty?
637
+ true
638
+ else
639
+ logger.info {
640
+ "Waiting for #{@connections.length} connections to finish."
641
+ }
642
+ @connections.each { |c|
643
+ c.close
644
+ }
645
+ false
646
+ end
647
+ end
648
+
649
+ end # Server
650
+
651
+ end # SOBS
652
+
653
+ end # HomeQ
654
+
@@ -0,0 +1,45 @@
1
+ #############################################################################
2
+ #
3
+ # $Id: sobs.rb 42 2008-10-24 05:53:35Z colin $
4
+ #
5
+ # Author:: Colin Steele (colin@colinsteele.org)
6
+ # Homepage::
7
+ #
8
+ # TODO: info
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2008 by Colin Steele. All Rights Reserved.
13
+ # colin@colinsteele.org
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #############################################################################
26
+
27
+ require 'statemachine'
28
+ require 'observer'
29
+ require 'uuidtools'
30
+
31
+ require 'homeq/sobs/sender'
32
+ require 'homeq/sobs/connection'
33
+ require 'homeq/sobs/client'
34
+ require 'homeq/sobs/message'
35
+ require 'homeq/sobs/job'
36
+ require 'homeq/sobs/server'
37
+ require 'homeq/sobs/topology'
38
+ require 'homeq/sobs/queue'
39
+ require 'homeq/sobs/foreman'
40
+
41
+ module HomeQ
42
+ module SOBS
43
+ PROTOCOL_VERSION = 1
44
+ end
45
+ end