first stab at event integration and some new ideas about lifecycle
[god.git] / lib / god / watch.rb
blobeec4cdfc3e0d256ceb6e5eb1a1fe1bbd349af083
1 module God
2   
3   class Watch < Base
4     # config
5     attr_accessor :name, :start, :stop, :restart, :interval, :grace
6     
7     # api
8     attr_accessor :behaviors, :conditions
9     
10     # internal
11     attr_accessor :mutex
12     
13     # 
14     def initialize(meddle)
15       @meddle = meddle
16       
17       # no grace period by default
18       self.grace = 0
19       
20       # keep track of which action each condition belongs to
21       @action = nil
22       
23       self.behaviors = []
24       
25       # the list of conditions for each action
26       self.conditions = {:start => [],
27                          :restart => []}
28                          
29       # mutex
30       self.mutex = Mutex.new
31     end
32     
33     def behavior(kind)
34       # create the behavior
35       begin
36         b = Behavior.generate(kind)
37       rescue NoSuchBehaviorError => e
38         abort e.message
39       end
40       
41       # send to block so config can set attributes
42       yield(b) if block_given?
43       
44       # abort if the Behavior is invalid, the Behavior will have printed
45       # out its own error messages by now
46       abort unless b.valid?
47       
48       self.behaviors << b
49     end
50     
51     def start_if
52       @action = :start
53       yield(self)
54       @action = nil
55     end
56     
57     def restart_if
58       @action = :restart
59       yield(self)
60       @action = nil
61     end
62     
63     # Instantiate a Condition of type +kind+ and pass it into the optional
64     # block. Attributes of the condition must be set in the config file
65     def condition(kind)
66       # must be in a _if block
67       unless @action
68         abort "Watch#condition can only be called from inside a start_if block"
69       end
70       
71       # create the condition
72       begin
73         c = Condition.generate(kind)
74       rescue NoSuchConditionError => e
75         abort e.message
76       end
77       
78       # send to block so config can set attributes
79       yield(c) if block_given?
80       
81       # call prepare on the condition
82       c.prepare
83       
84       # abort if the Condition is invalid, the Condition will have printed
85       # out its own error messages by now
86       unless c.valid?
87         abort
88       end
89       
90       # inherit interval from meddle if no poll condition specific interval was set
91       if c.kind_of?(PollCondition) && !c.interval
92         if self.interval
93           c.interval = self.interval
94         else
95           abort "No interval set for Condition '#{c.class.name}' in Watch '#{self.name}', and no default Watch interval from which to inherit"
96         end
97       end
98       
99       self.conditions[@action] << c
100     end
101         
102     # Schedule all poll conditions and register all condition events
103     def monitor
104       [:start, :restart].each do |cmd|
105         self.conditions[cmd].each do |c|
106           @meddle.timer.register(self, c, cmd) if c.kind_of?(PollCondition)
107           c.register(self) if c.kind_of?(EventCondition)
108         end
109       end
110     end
111     
112     def action(a, c)
113       case a
114       when :start
115         puts self.start
116         call_action(c, :start, self.start)
117         sleep(self.grace)
118       when :restart
119         if self.restart
120           puts self.restart
121           call_action(c, :restart, self.restart)
122         else
123           action(:stop, c)
124           action(:start, c)
125         end
126         sleep(self.grace)
127       when :stop
128         puts self.stop
129         call_action(c, :stop, self.stop)
130         sleep(self.grace)
131       end      
132     end
133     
134     def call_action(condition, action, command)
135       # before
136       (self.behaviors + [condition]).each { |b| b.send("before_#{action}") }
137       
138       # action
139       if command.kind_of?(String)
140         # string command
141         system(command)
142       else
143         # lambda command
144         command.call
145       end
146       
147       # after
148       (self.behaviors + [condition]).each { |b| b.send("after_#{action}") }
149     end
150   end
151