File Coverage

blib/lib/Launcher/Cascade/Base.pm
Criterion Covered Total %
statement 88 90 97.7
branch 24 24 100.0
condition 8 10 80.0
subroutine 22 24 91.6
pod 18 18 100.0
total 160 166 96.3


line stmt bran cond sub pod time code
1             package Launcher::Cascade::Base;
2              
3             =head1 NAME
4              
5             Launcher::Cascade::Base - a base class for a Launcher. Provides everything but the launch() and test()
6              
7             =head1 SYNOPSIS
8              
9             use base qw( Launch::Cascade::Base );
10              
11             sub launch {
12             my $self = shift;
13             # implement the launcher
14             }
15              
16             sub test {
17             my $self = shift;
18             # implement the test
19             ...
20             return 1 if $success;
21             return 0 if $error;
22             return undef; # don't know
23             }
24              
25             =head1 DESCRIPTION
26              
27             This is a base class for a process launcher. It implements a mechanism to
28             handle dependencies between process (i.e., processes that require other
29             processes to be successfully finished before they can start).
30              
31             Subclasses must overload the launch() and test() methods to define what to
32             launch, and how to test whether is succeeded or failed.
33              
34             The run() method will invoke the launch() method, but only if:
35              
36             =over 4
37              
38             =item *
39              
40             The launcher has not run yet.
41              
42             =item *
43              
44             All its dependencies, if any, have been run successfully.
45              
46             =item *
47              
48             It has already run and failed, but hasn't reached its maximum number of
49             retries.
50              
51             =back
52              
53             =cut
54              
55 9     9   572988 use strict;
  9         21  
  9         319  
56 9     9   47 use warnings;
  9         16  
  9         254  
57              
58 9     9   46 use base qw( Launcher::Cascade );
  9         18  
  9         3731  
59 9     9   21094 use overload '""' => \&as_string;
  9         8176  
  9         85  
60              
61 9     9   6191 use Log::Log4perl qw( get_logger );
  9         683061  
  9         80  
62 9     9   6441 use Launcher::Cascade::ListOfStrings::Errors;
  9         28  
  9         9106  
63              
64             =head2 Attributes
65              
66             Attributes are accessed through accessor methods. These methods, when called
67             without an argument, will return the attribute's value. With an argument, they
68             will set the attribute's value to that argument, and return the former value.
69              
70             =over 4
71              
72             =item B
73              
74             A simple string used only for printing hopefully useful messages. This should
75             be set to something meaningfully.
76              
77             =item B
78              
79             The status of the the launcher. Its value should not be accessed directly but
80             through set_success(), set_failure(), is_ready(), has_run(), is_success(),
81             is_failure() methods.
82              
83             The possible values are:
84              
85             =over 5
86              
87             =item B<0>
88              
89             Not run yet
90              
91             =item B<1>
92              
93             Running, but it is still unknown whether it has succeeded or failed.
94              
95             =item B<2>
96              
97             Success
98              
99             =item B<3>
100              
101             Failure
102              
103             =back
104              
105             =cut
106              
107             =item B
108              
109             Number of retries so far (0 at the first attempt).
110              
111             =item B
112              
113             Number of failed attempts after which to consider the process as failed. 0 will
114             try only once. -1 will B try forever (you don't want your launcher to last
115             forever do you?).
116              
117             =item B
118              
119             How long to wait between retries, in seconds. The program will not block during
120             this time.
121              
122             =item B
123              
124             A C object, containing a series of
125             error messages, as pushed by the add_error() method.
126              
127             =cut
128              
129             Launcher::Cascade::make_accessors qw( name errors );
130             Launcher::Cascade::make_accessors_with_defaults
131             status => 0,
132             retries => 0,
133             max_retries => 0,
134             time_between_retries => 0,
135             _last_retry_at => 0,
136             ;
137              
138             =back
139              
140             =head2 Methods
141              
142             =over 4
143              
144             =item B
145              
146             =item B I
147              
148             =item B I
149              
150             Returns the list of C objects that this one depends
151             upon (i.e., they must have successfully been run before this one runs).
152              
153             When called with a I of arguments, this methods also sets the list of
154             dependencies to I. The argument can also be an I, in which case
155             it will automatically be dereferenced.
156              
157             All elements in I or I should be instances of
158             C or one of its subclasses.
159              
160             =cut
161              
162             sub dependencies {
163              
164 33     33 1 66 my $self = shift;
165 33   100     234 $self->{_dependencies} ||= [];
166 33 100       88 if ( @_ ) {
167 5 100       30 if ( UNIVERSAL::isa($_[0], 'ARRAY') ) {
168             # Dereference the first arg if it is an arrayref (so that the
169             # method can be called with an arrayref from the constructor).
170 3         10 $self->{_dependencies} = $_[0];
171             }
172             else {
173 2         8 $self->{_dependencies} = [ @_ ];
174             }
175             }
176 33         49 @{$self->{_dependencies}};
  33         143  
177             }
178              
179             =item B I
180              
181             Pushes a dependency to the list of dependencies. All elements in I
182             should be instances of C or one of its subclasses.
183              
184             =cut
185              
186             sub add_dependencies {
187              
188 1     1 1 11 my $self = shift;
189 1   50     2 push @{$self->{_dependencies} ||= []}, @_;
  1         23  
190             }
191              
192             =item B
193              
194             =item B
195              
196             Sets the status() attribute to the value corresponding to a success, resp. failure.
197              
198             =cut
199              
200             sub set_success {
201 10     10 1 19 my $self = shift;
202 10         91 $self->status(2);
203             }
204              
205             sub set_failure {
206 7     7 1 90 my $self = shift;
207 7         37 $self->status(3);
208             }
209              
210             =item B
211              
212             Checks whether the object is ready to run. Several conditions must be met for
213             this to happen:
214              
215             =over 5
216              
217             =item *
218              
219             status() must be 0, otherwise is_ready() yields C.
220              
221             =item *
222              
223             all dependencies must be successful, otherwise is_ready() yields 0.
224              
225             =back
226              
227             Returns 1 in all other cases.
228              
229             =cut
230              
231             sub is_ready {
232              
233 42875     42875 1 53302 my $self = shift;
234 42875 100       117299 return unless $self->status() == 0;
235              
236 24         99 foreach ( $self->dependencies() ) {
237 11 100       35 return 0 unless $_->is_success();
238             }
239 19         79 return 1;
240             }
241              
242             =item B
243              
244             Performs the real action. This method should be overridden in subclasses of
245             C.
246              
247             =cut
248              
249 0     0 1 0 sub launch {}
250              
251             =item B
252              
253             Performs the test to decide whether the process succeeded or failed. This
254             method should be overridden in subclasses of C. It
255             must return:
256              
257             =over 5
258              
259             =item *
260              
261             C if it cannot be determined whether the process has succeeded or failed
262             (e.g., the process is still in its starting phase)
263              
264             =item *
265              
266             a B status if the process has succeeded.
267              
268             =item *
269              
270             a B status if the process has failed.
271              
272             =back
273              
274             =cut
275              
276 0     0 1 0 sub test {}
277              
278             =item B
279              
280             Invokes method launch() if the object is_ready(), and sets its status
281             accordingly.
282              
283             =cut
284              
285             sub run {
286              
287 42865     42865 1 198903 my $self = shift;
288 42865 100       78138 return unless $self->is_ready();
289 17         79 $self->launch();
290 17         98 $self->status(1); # Running
291             }
292              
293             =item B
294              
295             Performs the test() and sets the status according to its result. Will not run
296             test() until the number of seconds specified by time_between_retries() has
297             elapsed since the last test.
298              
299             =cut
300              
301             sub check_status {
302              
303 42865     42865 1 140887 my $self = shift;
304 42865         112444 my $logger = get_logger;
305              
306 42865 100       1215279 return unless $self->status() == 1;
307              
308             # Checking whether it is too early for another attempt
309 42857         62401 my $time = time;
310 42857 100 100     114217 return if $self->time_between_retries()
311             && $time - $self->_last_retry_at() < $self->time_between_retries();
312 39         244 $self->_last_retry_at($time);
313              
314 39         160 $logger->debug("Performing the test() for $self");
315 39         366 my $result = eval {
316 39         142 $self->test();
317             };
318 39 100       153 if ( $@ ) {
319 1         5 my $msg = "Test for $self died unexpectedly: $@";
320 1         8 $logger->error($msg);
321 1         14 $self->add_error($msg);
322 1         9 $self->set_failure();
323 1         4 return;
324             }
325              
326 38 100       103 if ( !defined($result) ) {
    100          
327 26         66 $logger->debug("Still not sure whether $self succeeded");
328 26 100       235 if ( $self->retries() < $self->max_retries() ) {
329 22         63 $self->retries($self->retries() + 1);
330 22         64 return;
331             }
332             else {
333 4         10 my $msg = "Maximum number of retries reached";
334 4         20 $logger->error($msg);
335 4         54 $self->add_error($msg);
336 4         29 $self->set_failure();
337             }
338             }
339             elsif ( $result > 0) {
340 10         29 $logger->info("$self ran successfully");
341 10         126 $self->set_success();
342             }
343             else {
344 2         5 $logger->info("$self failed to run successfully");
345 2         22 $self->set_failure();
346             }
347             }
348              
349             =item B
350              
351             =item B
352              
353             =item B
354              
355             This methods query the object's current status, and return a true status if,
356             respectively, the object has run successfully, or the object has run and
357             failed, or if the object has run (whether it succeeded or failed).
358              
359             =cut
360              
361             sub is_success {
362 42904     42904 1 49408 my $self = shift;
363 42904         108944 $self->status() == 2;
364             }
365              
366             sub is_failure {
367 42900     42900 1 58354 my $self = shift;
368 42900         106715 $self->status() == 3;
369             }
370              
371             sub has_run {
372              
373 42859     42859 1 83676 my $self = shift;
374 42859 100       85128 $self->is_success() || $self->is_failure();
375             }
376              
377             =item B
378              
379             Reset the object's status so that it can be run again.
380              
381             =cut
382              
383             sub reset {
384              
385 8     8 1 15 my $self = shift;
386 8         33 $self->retries(0);
387 8         30 $self->status(0);
388 8         32 $self->_last_retry_at(0);
389             }
390              
391             =item B
392              
393             Returns a string representing the object (its name()). This method is invoked
394             when the object is interpolated in a double quoted string.
395              
396             =cut
397              
398             sub as_string {
399              
400 85     85 1 221 my $self = shift;
401              
402 85         443 return $self->name();
403             }
404              
405             =item B I
406              
407             Pushes I to the list of messages hold by the the
408             C object in the errors() attribute.
409             I should be either a string or a C
410             object, or any object that can be stringified.
411              
412             =cut
413              
414             sub add_error {
415              
416 8     8 1 40 my $self = shift;
417 8   66     19 push @{$self->{_errors} ||= new Launcher::Cascade::ListOfStrings::Errors }, @_;
  8         162  
418             }
419              
420             =back
421              
422             =head2 Constants
423              
424             =over 4
425              
426             =item B
427              
428             =item B
429              
430             =item B
431              
432             Subclasses can use theses "constant" methods within their test() methods, to
433             report either success, failure or the undetermined state of a test.
434              
435             sub test {
436              
437             my $self = shift;
438              
439             if ( ... ) {
440             return $self->SUCCESS;
441             }
442             elsif ( ... ) {
443             return $self->FAILURE;
444             }
445             else {
446             return $self->UNDEFINED;
447             }
448             }
449              
450             =back
451              
452             =cut
453              
454 1     1 1 4 sub SUCCESS { 1 }
455              
456 1     1 1 4 sub FAILURE { -1 }
457              
458 9     9 1 47 sub UNDEFINED { undef }
459              
460             =head1 SEE ALSO
461              
462             L, L,
463             L.
464              
465             =head1 AUTHOR
466              
467             Cédric Bouvier C<< >>
468              
469             =head1 COPYRIGHT & LICENSE
470              
471             Copyright (C) 2006 Cédric Bouvier, All Rights Reserved.
472              
473             This program is free software; you can redistribute it and/or modify it under
474             the same terms as Perl itself.
475              
476             =cut
477              
478             1; # end of Launcher::Cascade::Base