homeq 1.1.4

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