4 attr_accessor :name, :interval, :group, :valid_states, :initial_state, :phase
7 def autostart?; @autostart; end
10 attr_accessor :state, :behaviors, :metrics
18 # initial state is unmonitored
19 self.state = :unmonitored
21 # the list of behaviors
24 # the list of conditions for each action
25 self.metrics = {nil => [], :unmonitored => []}
28 self.mutex = Monitor.new
32 self.valid_states.each do |state|
33 self.metrics[state] ||= []
40 # a name must be specified
43 applog(self, :error, "No name was specified")
46 # valid_states must be specified
47 if self.valid_states.nil?
49 applog(self, :error, "No valid_states array was specified")
52 # valid_states must be specified
53 if self.initial_state.nil?
55 applog(self, :error, "No initial_state was specified")
61 ###########################################################################
65 ###########################################################################
67 def canonical_hash_form(to)
68 to.instance_of?(Symbol) ? {true => to} : to
71 # Define a transition handler which consists of a set of conditions
72 def transition(start_states, end_states)
73 # convert end_states into canonical hash form
74 canonical_end_states = canonical_hash_form(end_states)
76 Array(start_states).each do |start_state|
77 # validate start state
78 unless self.valid_states.include?(start_state)
79 abort "Invalid state :#{start_state}. Must be one of the symbols #{self.valid_states.map{|x| ":#{x}"}.join(', ')}"
82 # create a new metric to hold the watch, end states, and conditions
83 m = Metric.new(self, canonical_end_states)
86 # let the config file define some conditions on the metric
89 # add an :always condition if no block
90 m.condition(:always) do |c|
96 self.metrics[start_state] ||= []
97 self.metrics[start_state] << m
102 # create a new metric to hold the watch and conditions
105 # let the config file define some conditions on the metric
109 self.metrics[nil] << m
112 ###########################################################################
116 ###########################################################################
120 self.move(self.initial_state)
125 self.move(:unmonitored)
128 # Move from one state to another
130 self.mutex.synchronize do
131 # set the phase for this move
132 self.phase = Time.now
134 orig_to_state = to_state
135 from_state = self.state
137 msg = "#{self.name} move '#{from_state}' to '#{to_state}'"
138 applog(self, :info, msg)
140 # cleanup from current state
141 self.metrics[from_state].each { |m| m.disable }
143 if to_state == :unmonitored
144 self.metrics[nil].each { |m| m.disable }
148 self.action(to_state)
151 if [:start, :restart].include?(to_state) && self.metrics[to_state].empty?
156 self.metrics[to_state].each { |m| m.enable }
158 # if no from state, enable lifecycle metric
159 if from_state == :unmonitored
160 self.metrics[nil].each { |m| m.enable }
164 self.state = to_state
167 Trigger.broadcast(self, :state_change, [from_state, orig_to_state])
169 msg = "#{self.name} moved '#{from_state}' to '#{to_state}'"
170 applog(self, :info, msg)
177 ###########################################################################
181 ###########################################################################
183 def method_missing(sym, *args)
184 unless (sym.to_s =~ /=$/)
188 base = sym.to_s.chop.intern
190 unless self.valid_states.include?(base)
194 self.class.send(:attr_accessor, base)
195 self.send(sym, *args)
198 # +a+ is the action Symbol
199 # +c+ is the Condition
200 def action(a, c = nil)
201 if self.respond_to?(a)
202 command = self.send(a)
206 msg = "#{self.name} #{a}: #{command}"
207 applog(self, :info, msg)
211 msg = "#{self.name} #{a}: lambda"
212 applog(self, :info, msg)
216 raise NotImplementedError
221 ###########################################################################
225 ###########################################################################
228 # override if necessary
232 # override if necessary