File Coverage

blib/lib/Helios/Service.pm
Criterion Covered Total %
statement 47 468 10.0
branch 0 170 0.0
condition 0 48 0.0
subroutine 16 72 22.2
pod 30 55 54.5
total 93 813 11.4


line stmt bran cond sub pod time code
1             package Helios::Service;
2              
3 1     1   1276 use 5.008;
  1         4  
4 1     1   4 use strict;
  1         1  
  1         17  
5 1     1   3 use warnings;
  1         1  
  1         37  
6 1     1   3 use base qw( TheSchwartz::Worker );
  1         2  
  1         440  
7 1     1   324 use File::Spec;
  1         2  
  1         19  
8 1     1   3 use Sys::Hostname;
  1         1  
  1         39  
9 1     1   3 use DBI;
  1         2  
  1         26  
10 1     1   4 use Helios::ObjectDriver::DBI;
  1         56  
  1         7  
11              
12 1     1   25 use Helios::Error;
  1         1  
  1         13  
13 1     1   3 use Helios::Job;
  1         2  
  1         12  
14 1     1   3 use Helios::Config;
  1         2  
  1         14  
15 1     1   2 use Helios::ConfigParam;
  1         1  
  1         17  
16 1     1   3 use Helios::LogEntry;
  1         1  
  1         19  
17 1     1   6 use Helios::LogEntry::Levels qw(:all);
  1         1  
  1         566  
18             # [LH] [2013-10-04]: Using Helios::JobType instead of TheSchwartz::FuncMap now.
19 1     1   355 use Helios::JobType;
  1         2  
  1         23  
20 1     1   4 use Helios::Error::JobTypeError;
  1         1  
  1         3  
21              
22             our $VERSION = '2.83';
23              
24             # FILE CHANGE HISTORY:
25             # [2011-12-07]: Updated to support new Helios::Logger API. Added
26             # %INIT_LOG_CLASSES global. Completely rewrote logMsg() method. Updated
27             # logMsg() documentation.
28             # [2011-12-07]: Updated copyright info.
29             # [2011-12-07]: Removed parseArgXML() method (redundant).
30             # [2011-12-28]: Removed unnecessary 'use XML::Parser' line.
31             # [2011-12-28]: Replaced metajob running code in work() with new runMetajob()
32             # method.
33             # [2011-12-28]: work(): changed so CACHED_CONFIG and
34             # CACHED_CONFIG_RETRIEVAL_COUNT only print to STDOUT if debug() is set.
35             # [2011-12-28]: Updated copyright info.
36             # [2012-01-01]: Renamed runMetajob() method to metarun().
37             # [2012-01-01]: work(): replaced old code calling the service class's run()
38             # method. New code: 1) calls run() or metarun() as appropriate, 2) ignores
39             # value returned by run() and metarun() unless DOWNSHIFT_ON_NONZERO_RUN
40             # parameter is set, 3) is wrapped in an eval {} to catch uncaught exceptions
41             # a service class's run() might throw, forcing the job to failure. Updated
42             # work() documenation for new functionality.
43             # [2012-01-01]: Updated copyright info for new year.
44             # [2012-01-04]: Fixed max_retries() and retry_delay() so they actually pay
45             # attention to MaxRetries() and RetryInterval(). In the original code they
46             # didn't, and MaxRetries() and RetryInterval() did not work as documented.
47             # [2012-01-08]: work(): explicitly return 0 to the calling routine.
48             # [2012-03-27]: Reorganized use module lines. Removed unnecessary TheSchwartz &
49             # TheSchwartz::Job lines.
50             # [2012-03-27]: work(): added debugging code for new driver and logger code.
51             # [2012-03-27]: work(): changed try {} to eval {}.
52             # [2012-03-27]: prep(): Replaced old prep() method with new version that
53             # starts new logger and config initialization.
54             # [2012-03-27]: jobsWaiting(): changed quote operator for query from heredoc
55             # to qq{}
56             # [2012-03-27]: added new setDriver() and initDriver() methods. Replaced
57             # getDriver() method with new one that uses setDriver() and initDriver().
58             # [2012-03-27]: added initLoggers() method to handle logger module
59             # initialization.
60             # [2012-04-25]: added deferredJob() method.
61             # [2012-05-20]: work: removed driver and logger debugging code. Removed
62             # comment about removing a debug message before release (it is useful to leave
63             # that debugging message in).
64             # [2012-05-20]: dbConnect(): removed old commented-out code.
65             # [LH] [2012-07-11]: Switched use line for Data::ObjectDriver::Driver::DBI to
66             # load Helios::ObjectDriver::DBI to start integration of database connection
67             # caching.
68             # [LH] [2012-07-15]: Changed prep() to use new Helios::Config class. Removed
69             # 'use Config::IniFiles' because with Helios::Config it's redundant.
70             # [LH] [2012-07-15]: replaced most of dbConnect() code to implement fork-safe
71             # database connection creation and sharing.
72             # [LH] [2012-07-15]: replaced most of jobWaiting() code for simplicity and to
73             # replace try{} with eval {}.
74             # [LH] [2012-07-15]: replaced most of getFuncidFromDb() code to change try{}
75             # to eval{} and eliminate indirect object notation.
76             # [LH] [2012-07-16]: getFuncidFromDb(): fixed identation of new code.
77             # [LH] [2012-07-16]: updated copyright notices (added Logical Helion, LLC to
78             # main COPYRIGHT section).
79             # [LH] [2012-08-04]: removed 'use Error' line as all of the try {} blocks have
80             # been replaced with eval {}.
81             # [LH] [2012-08-04]: replaced getConfigFromIni() and getConfigFromDb() with
82             # versions that use the new Helios::Config API. Changed POD for both to note
83             # the methods are deprecated.
84             # [LH] [2012-08-04]: added new initConfig() method to manage Helios::Config
85             # module initialization.
86             # [LH] [2012-08-04]: added blank default ConfigClass() method.
87             # [LH] [2012-08-04]: dbConnect(): updated to better handle "options" directives
88             # and improve connection code. Updated dbConnect() POD.
89             # [LH] [2012-08-04]: Reformatted copyright notices for clarity.
90             # [LH] [2012-08-07]: further changes to getConfigFromIni() and
91             # getConfigFromDb() to work with Helios::Config API.
92             # [LH] [2012-09-05]: removed old commented out code from getConfigFromIni(),
93             # getConfigFromDb(), getFuncidFromDb(), dbConnect().
94             # [LH] [2012-09-05]: Added to POD entry for getFuncidFromDb().
95             # [LH] [2012-11-06]: Added _require_module() method to safely load modules at
96             # runtime.
97             # [LH] [2012-11-06]: removed old commented out 'use' lines for
98             # Config::IniFiles, Data::ObjectDriver::Driver::DBI, Error.
99             # [LH] [2012-11-06]: corrected grammar in work() documentation.
100             # [LH] [2012-11-06]: removed old commented out code from prep().
101             # [LH] [2012-11-06]: removed old commented out code from getDriver().
102             # [LH] [2012-11-06]: Added ConfigClass() and initConfig() POD.
103             # [LH] [2013-08-11]: Added code to work() to catch and handle job
104             # initialization errors. [RT79690]
105             # [LH] [2013-08-19]: Removed old commented out code and clarified comments on
106             # job initialization error handling.
107             # [LH] [2013-10-04]: Added code to start conversion to new Helios class
108             # structure and support virtual jobtypes feature. New new() constructor
109             # initializes attribute hashref values and can only be called as a class
110             # method. Added set/get/addJobType(), set/getAltJobTypes(),
111             # set/getAltJobtypeids(), addAltJobtypeid(), lookupJobtypeid(),
112             # lookupAltJobtypeids(). Switched all code that used TheSchwartz::FuncMap to
113             # use Helios::JobType. Replaced set/getFuncid() with new version that mirrors
114             # set/getJobType() (set/getFuncid() will be deprecated on final release).
115             # Replaced jobsWaiting() with new version that uses Helios::JobType and scans
116             # for all jobtypes (primary and alternates) if alternate jobtypes are set.
117             # [LH] [2013-10-04]: Removed 'require XML::Simple' line because Helios::Service
118             # has not used that in a long time.
119             # [LH] [2013-10-18]: Added grab_for() and JobLockInterval() to implement new
120             # retry API. Added $CACHED_HOSTNAME and modified prep() to reduce calls to
121             # Sys::Hostname::hostname().
122             # [LH] [2013-10-24]: Removed old, already commented out code. Added POD for
123             # new methods added in 2.7x development series. Marked getFuncidFromDb() as
124             # deprecated; its function has been replaced by lookupJobtypeid().
125             # [LH] [2014-02-28]: Changed max_retries() and retry_delay() to default to zero
126             # rather than undef to eliminate some warnings.
127             # [LH] [2015-08-14]: Changed all CRLF endings to Unix-style LF endings.
128              
129             =head1 NAME
130              
131             Helios::Service - base class for services in the Helios job processing system
132              
133             =head1 DESCRIPTION
134              
135             Helios::Service is the base class for all services intended to be run by the
136             Helios parallel job processing system. It handles the underlying TheSchwartz job queue system and
137             provides additional methods to handle configuration, job argument parsing, logging, and other
138             functions.
139              
140             A Helios::Service subclass must implement only one method: the run() method. The run() method
141             will be passed a Helios::Job object representing the job to performed. The run() method should
142             mark the job as completed successfully, failed, or permanently failed (by calling completedJob(),
143             failedJob(), or failedJobPermanent(), respectively) before it ends.
144              
145             =head1 TheSchwartz HANDLING METHODS
146              
147             The following 3 methods are used by the underlying TheSchwartz job queuing
148             system to determine what work is to be performed and, if a job fails, how it
149             should be retried.
150              
151             YOU DO NOT NEED TO TOUCH THESE METHODS TO CREATE HELIOS SERVICES. These
152             methods manage interaction between Helios and TheSchwartz. You only need to
153             be concerned with these methods if you are attempting to extend core Helios
154             functionality.
155              
156             =head2 max_retries()
157              
158             Controls how many times a job will be retried.
159              
160             =head2 retry_delay()
161              
162             Controls how long (in secs) before a failed job will be retried.
163              
164             These two methods should return the number of times a job can be retried if it fails and the
165             minimum interval between those retries, respectively. If you don't define them in your subclass,
166             they default to zero, and your job(s) will not be retried if they fail.
167              
168             =head2 work()
169              
170             The work() method is the method called by the underlying TheSchwartz::Worker (which in turn is
171             called by the helios.pl service daemon) to perform the work of a job. Effectively, work() sets
172             up the worker process for the Helios job, and then calls the service subclass's run() method to
173             run it.
174              
175             The work() method is passed a job object from the underlying TheSchwartz job queue system. The
176             service class is instantiated, and the the job is recast into a Helios::Job object. The service's
177             configuration parameters are read from the system and made available as a hashref via the
178             getConfig() method. The job's arguments are parsed from XML into a Perl hashref, and made
179             available via the job object's getArgs() method. Then the service object's run() method is
180             called, and is passed the Helios::Job object.
181              
182             Once the run() method has completed the job and returned, work() determines
183             whether the worker process should exit or stay running. If OVERDRIVE mode is
184             enabled and the service hasn't been HALTed or told to HOLD, the worker process
185             will stay running, and work() will be called to setup and run another job. If
186             the service is not in OVERDRIVE mode, the worker process will exit.
187              
188             =cut
189              
190             our $CACHED_CONFIG;
191             our $CACHED_CONFIG_RETRIEVAL_COUNT = 0;
192             our $WORKER_START_TIME = 0;
193             # [LH] [2013-10-18]: Added $CACHED_HOSTNAME and modified prep() to reduce calls to
194             # Sys::Hostname::hostname().
195             our $CACHED_HOSTNAME = '';
196              
197             our %INIT_LOG_CLASSES; # for the logging system
198             our $INIT_CONFIG_CLASS; # for config system
199              
200             our $DRIVER; # for caching the Data::ObjectDriver
201              
202 0 0   0 1   sub max_retries { $_[0]->MaxRetries() || 0; }
203 0 0   0 1   sub retry_delay { $_[0]->RetryInterval() || 0; }
204             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
205             # [LH] [2013-10-18]: Added grab_for() and JobLockInterval() to implement new
206             # retry API. Like TheSchwartz's setup, the JobLockInterval() defaults to
207             # 3600 sec (1 hr).
208 0 0   0 1   sub grab_for { $_[0]->JobLockInterval() || 3600 }
209             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
210              
211             sub work {
212 0     0 1   my $class = shift;
213 0           my $schwartz_job = shift;
214             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
215             # 2013-08-11: Rewritten job initialization code to catch job init errors, including [RT79690].
216 0           my $job;
217             my $job_init_error;
218             eval {
219             # turn the schwartz job we were given into:
220             # a custom job object defined by the app class,
221             # or a basic Helios::Job object if the app didn't specify anything special
222 0 0         if ( $class->JobClass() ) {
223             # instantiate a custom job object
224 0           $job = $class->JobClass()->new($schwartz_job);
225             } else {
226             # nothing fancy, just a normal Helios::Job object
227 0           $job = Helios::Job->new($schwartz_job);
228             }
229 0           1;
230 0 0         } or do {
231             # uhoh, there was a problem turning the schwartz job into a Helios job
232             # note that, and when the worker is fully prepped,
233             # we'll take care of the problem
234 0           $job_init_error = "$@";
235             };
236             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
237 0 0         $WORKER_START_TIME = $WORKER_START_TIME ? $WORKER_START_TIME : time(); # for WORKER_MAX_TTL
238 0           my $return_code;
239             my $args;
240              
241             # instantiate the service class into a worker
242 0           my $self = new $class;
243             eval {
244             # if we've previously retrieved a config
245             # AND OVERDRIVE is enabled (1)
246             # AND LAZY_CONFIG_UPDATE is enabled (1),
247             # AND we're not servicing the 10th job (or technically a multiple of ten)
248             # THEN just retrieve the pre-existing config
249 0 0         if ($self->debug) {
250 0           print "CACHED_CONFIG=",$CACHED_CONFIG,"\n";
251 0           print "CACHED_CONFIG_RETRIEVAL_COUNT=",$CACHED_CONFIG_RETRIEVAL_COUNT,"\n";
252             }
253 0 0 0       if ( defined($CACHED_CONFIG) &&
      0        
      0        
254             $CACHED_CONFIG->{LAZY_CONFIG_UPDATE} == 1 &&
255             $CACHED_CONFIG->{OVERDRIVE} == 1 &&
256             $CACHED_CONFIG_RETRIEVAL_COUNT % 10 != 0
257             ) {
258 0           $self->prep(CACHED_CONFIG => $CACHED_CONFIG);
259 0           $CACHED_CONFIG_RETRIEVAL_COUNT++;
260 0 0         if ($self->debug) { $self->logMsg(LOG_DEBUG,"Retrieved config params from in-memory cache"); }
  0            
261             } else {
262 0           $self->prep();
263              
264             # prep() just parsed the config for us
265             # let's grab the db driver and loggers for use by the next job
266             # (if we're in OVERDRIVE; if we're not, there won't be much effect
267 0 0 0       if ( defined($self->getConfig()->{LAZY_CONFIG_UPDATE}) &&
268             $self->getConfig()->{LAZY_CONFIG_UPDATE} == 1 ) {
269 0           $CACHED_CONFIG = $self->getConfig();
270 0           $CACHED_CONFIG_RETRIEVAL_COUNT = 1; # "prime the pump"
271             }
272             }
273              
274             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
275             # 2013-08-11: Rewritten job initialization code to catch job init errors, including [RT79690].
276             # if a job initialization error occurred above,
277             # we want to log the error and then exit the worker process
278             # trying to further job setup and/or run the job is ill-advised,
279             # and if we have to exit the process so TheSchwartz doesn't force the job to failure.
280             # (but we have to wait and do it here so we can properly log the error)
281 0 0         if ( defined($job_init_error) ) {
282 0 0         if ($self->debug) { print "JOB INITIALIZATION ERROR: ".$job_init_error."\n"; }
  0            
283 0           $self->logMsg(LOG_CRIT, "JOB INITIALIZATION ERROR: $job_init_error");
284 0           exit(1);
285             }
286             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
287            
288 0           $job->debug( $self->debug );
289 0           $job->setConfig($self->getConfig());
290             # BEGIN CODE Copyright (C) 2011-2012 by Andrew Johnson.
291 0           $job->setDriver($self->getDriver());
292 0           $args = $job->parseArgs();
293 0           1;
294 0 0         } or do {
295 0           my $E = $@;
296 0 0         if ( $E->isa('Helios::Error::InvalidArg') ) {
    0          
297 0           $self->logMsg($job, LOG_ERR, "Invalid arguments: $E");
298 0           $job->failedNoRetry("$E");
299 0           exit(1);
300             } elsif ( $E->isa('Helios::Error::DatabaseError') ) {
301 0           $self->logMsg($job, LOG_ERR, "Database error: $E");
302 0           $job->failed("$E");
303 0           exit(1);
304             } else {
305 0           $self->logMsg($job, LOG_ERR, "Unexpected error: $E");
306 0           $job->failed("$E");
307 0           exit(1);
308             }
309             };
310              
311             # run the job, whether it's a metajob or simple job
312 0           $self->setJob($job);
313             eval {
314 0 0         if ( $job->isaMetaJob() ) {
315             # metajob
316 0 0         if ($self->debug) { print 'CALLING METARUN() for metajob '.$job->getJobid()."...\n"; }
  0            
317 0           $return_code = $self->metarun($job);
318 0 0         if ($self->debug) { print 'METARUN() RETURN CODE: '.$return_code."\n"; }
  0            
319             } else {
320             # must be a simple job then
321 0 0         if ($self->debug) { print 'CALLING RUN() for job '. $job->getJobid()."...\n"; }
  0            
322 0           $return_code = $self->run($job);
323 0 0         if ($self->debug) { print 'RUN() RETURN CODE: '. $return_code."\n"; }
  0            
324             }
325 0           1;
326 0 0         } or do {
327 0           my $E = $@;
328 0           $self->logMsg($job, LOG_CRIT,"Uncaught exception thrown by run() in process ".$$.': '.$E);
329 0           $self->logMsg($job, LOG_CRIT,'Forcing failure of job '.$job->getJobid().' and exit of process '.$$);
330 0           $self->failedJob($job, $E, 1);
331 0           exit(1);
332             };
333              
334             # DOWNSHIFT_ON_NONZERO_RUN
335             # previously a nonzero return from run() was taken to mean a failed job,
336             # and would cause a downshift in OVERDRIVE mode. This was considered a
337             # safety feature as it was unknown what caused the job to fail.
338             # But this feature was underdocumented and misunderstood and has been
339             # removed.
340             # The new default behavior doesn't pay attention to the value returned
341             # from run() or metarun(). You should mark your job as completed or
342             # failed in run() or metarun() and not worry about returning anything.
343             # Anyone requiring the old behavior can use the new DOWNSHIFT_ON_NONZERO_RUN
344             # parameter to enable it.
345 0 0 0       if ( defined($self->getConfig()->{DOWNSHIFT_ON_NONZERO_RUN}) &&
      0        
346             $self->getConfig()->{DOWNSHIFT_ON_NONZERO_RUN} == 1 &&
347             $return_code != 0
348             ) {
349 0           exit(1);
350             }
351             # END CODE Copyright (C) 2011-2012 by Andrew Johnson.
352              
353             # if we're not in OVERDRIVE, the worker process will exit as soon as work() returns anyway
354             # (calling shouldExitOverdrive will be a noop)
355             # if we're in OVERDRIVE, work() will exit and the worker process will call it again with another job
356             # if we were in OVERDRIVE, but now we're NOT, we should explicitly exit() to accomplish the downshift
357 0 0         if ( $self->shouldExitOverdrive() ) {
358 0           $self->logMsg(LOG_NOTICE,"Class $class exited (downshift)");
359 0           exit(0);
360             }
361              
362             # we'll assume if we got here, things went reasonably well
363             # (run() or metarun() succeeded, or it failed and the errors were caught
364             # we're going to return 0 to the calling routine
365             # in normal mode, this will immediately return to launch_worker() in helios.pl
366             # (which will exit with this return code)
367             # in OVERDRIVE, this will return to TheSchwartz->work_until_done(), which
368             # will call this work() with another TheSchwartz::Job, over and over again
369             # until it runs out of jobs. When the jobs are exhausted, then it returns
370             # to launch_worker() in helios.pl (which then exits with this return code)
371 0           return 0;
372             }
373              
374             # BEGIN CODE Copyright (C) 2011-2012 by Andrew Johnson.
375              
376             =head2 metarun($job)
377              
378             Given a metajob, the metarun() method runs the job, returning 0 if the
379             metajob was successful and nonzero otherwise.
380              
381             This is the default metarun() for Helios. In the default Helios system,
382             metajobs consist of multiple simple jobs. These jobs are defined in the
383             metajob's argument XML at job submission time. The metarun() method will
384             burst the metajob apart into its constituent jobs, which are then run by
385             another service.
386              
387             Metajobs' primary use in the base Helios system is to speed the job submission
388             process of large job batches. One metajob containing a batch of thousands of
389             jobs can be submitted and burst apart by the system much faster than thousands
390             of individual jobs can be submitted. In addition, the faster jobs enter the
391             job queue, the faster Helios workers can be launched to handle them. If you
392             have thousands (or tens of thousands, or more) of jobs to run, especially if
393             you are running your service in OVERDRIVE mode, you should use metajobs to
394             greatly increase system throughput.
395              
396             =cut
397              
398             sub metarun {
399 0     0 1   my $self = shift;
400 0           my $metajob = shift;
401 0           my $config = $self->getConfig();
402 0           my $args = $metajob->getArgs();
403 0           my $r;
404            
405             eval {
406 0           $self->logMsg($metajob, LOG_NOTICE, 'Bursting metajob '.$metajob->getJobid);
407 0           my $jobCount = $self->burstJob($metajob);
408 0           $self->logMsg($metajob, LOG_NOTICE, 'Metajob '.$metajob->getJobid().' burst into '.$jobCount.' jobs.');
409 0           1;
410 0 0         } or do {
411 0           my $E = $@;
412 0 0         if ( $E->isa('Helios::Error::BaseError') ) {
413 0           $self->logMsg($metajob,
414             LOG_ERR,
415             'Metajob burst failure for metajob '
416             .$metajob->getJobid().': '
417             .$E->text()
418             );
419             } else {
420 0           $self->logMsg($metajob,
421             LOG_ERR,
422             'Metajob burst failure for metajob '
423             .$metajob->getJobid().': '
424             .$E
425             );
426             }
427             };
428             }
429             # END CODE Copyright (C) 2011-2012 by Andrew Johnson.
430              
431              
432             =head1 ACCESSOR METHODS
433              
434             These accessors will be needed by subclasses of Helios::Service.
435              
436             get/setConfig()
437             get/setHostname()
438             get/setIniFile()
439             get/setJob()
440             get/setJobType()
441             get/setAltJobTypes(), addAltJobType()
442             get/setJobTypeid()
443             get/setAltJobtypeids(), addAltJobtypeid()
444             errstr()
445             debug()
446              
447             Most of these are handled behind the scenes simply by calling the prep() method.
448              
449             After calling prep(), calling getConfig() will return a hashref of all the configuration parameters
450             relevant to this service class on this host.
451              
452             If debug mode is enabled (the HELIOS_DEBUG env var is set to 1), debug() will return a true value,
453             otherwise, it will be false. Some of the Helios::Service methods will honor this value and log
454             extra debugging messages either to the console or the Helios log (helios_log_tb table). You can
455             also use it within your own service classes to enable/disable debugging messages or behaviors.
456              
457             =cut
458              
459 0     0 0   sub setJob { $_[0]->{job} = $_[1]; }
460 0     0 0   sub getJob { return $_[0]->{job}; }
461              
462             # need for helios.pl logging
463 0     0 0   sub setJobType { $_[0]->{jobType} = $_[1]; }
464 0     0 0   sub getJobType { return $_[0]->{jobType}; }
465              
466             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
467             # [LH] [2013-10-04] Virtual jobtype code.
468             sub setAltJobTypes {
469 0     0 0   my $self = shift;
470 0           $self->{altJobTypes} = [@_];
471             }
472             sub getAltJobTypes {
473 0 0   0 0   if ( defined $_[0]->{altJobTypes} ) {
474 0           return @{ $_[0]->{altJobTypes} };
  0            
475             } else {
476 0           return undef;
477             }
478             }
479             sub addAltJobType {
480 0     0 0   push(@{ $_[0]->{altJobTypes} }, $_[1]);
  0            
481             }
482              
483             # [LH] [2013-10-04] Virtual jobtype code.
484 0     0 0   sub setJobtypeid { $_[0]->{jobtypeid} = $_[1]; }
485 0     0 0   sub getJobtypeid { return $_[0]->{jobtypeid}; }
486              
487             sub setAltJobtypeids {
488 0     0 0   my $self = shift;
489 0           $self->{altJobtypeids} = [@_];
490             }
491             sub getAltJobtypeids {
492 0 0   0 0   if ( defined $_[0]->{altJobtypeids} ) {
493 0           return @{ $_[0]->{altJobtypeids} };
  0            
494             } else {
495 0           return undef;
496             }
497             }
498             sub addAltJobtypeid {
499 0     0 0   push(@{ $_[0]->{altJobtypeids} }, $_[1]);
  0            
500             }
501             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
502              
503              
504 0     0 0   sub setConfig { $_[0]->{config} = $_[1]; }
505 0     0 0   sub getConfig { return $_[0]->{config}; }
506              
507             # [LH] [2013-10-04]: Virtual jobtypes. Changed set/getFuncid() for
508             # compatibility with set/getJobtypeid(). Set/getFuncid() is DEPRECATED;
509             # retained for now for backward compatibility with Helios 2.6x and earlier.
510 0     0 0   sub setFuncid { $_[0]->{jobtypeid} = $_[1]; }
511 0     0 0   sub getFuncid { return $_[0]->{jobtypeid}; }
512              
513 0     0 0   sub setIniFile { $_[0]->{inifile} = $_[1]; }
514 0     0 0   sub getIniFile { return $_[0]->{inifile}; }
515              
516 0     0 0   sub setHostname { $_[0]->{hostname} = $_[1]; }
517 0     0 0   sub getHostname { return $_[0]->{hostname}; }
518              
519             # BEGIN CODE Copyright (C) 2012 by Andrew Johnson.
520             # these are class methods!
521             sub setDriver {
522 0     0 0   $DRIVER = $_[1];
523             }
524             sub getDriver {
525 0     0 0   initDriver(@_);
526             }
527             # END CODE Copyright Andrew Johnson.
528              
529 0 0   0 0   sub errstr { my $self = shift; @_ ? $self->{errstr} = shift : $self->{errstr}; }
  0            
530 0 0   0 0   sub debug { my $self = shift; @_ ? $self->{debug} = shift : $self->{debug}; }
  0            
531              
532             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
533             # [LH] [2013-10-04] New constructor initializes attributes in the underlying
534             # object structure and can only be called as a class method.
535              
536             =head1 CONSTRUCTOR
537              
538             =head2 new()
539              
540             The new() method creates a new service class instance. It initializes all of
541             the underlying attribute values and sets the instance's jobType to the name of
542             the class.
543              
544             =cut
545              
546             sub new {
547 0     0 1   my $cl = shift;
548 0           my $self = {
549             'jobType' => undef,
550             'altJobTypes' => undef,
551             'jobtypeid' => undef,
552             'altJobtypeids' => undef,
553             'hostname' => undef,
554             'inifile' => undef,
555             'job' => undef,
556            
557             'config' => undef,
558             'debug' => undef,
559             'errstr' => undef,
560             };
561 0           bless $self, $cl;
562              
563             # init fields
564 0           my $jobtype = $cl;
565 0           $self->setJobType($jobtype);
566              
567 0           return $self;
568             }
569             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
570              
571              
572             =head1 INTERNAL SERVICE CLASS METHODS
573              
574             When writing normal Helios services, the methods listed in this section will
575             have already been dealt with before your run() method is called. If you are
576             extending Helios itself or instantiating a Helios service outside of Helios
577             (for example, to retrieve a service's config params), you may be interested in
578             some of these, primarily the prep() method.
579              
580             =head2 prep()
581              
582             The prep() method is designed to call all the various setup routines needed to
583             get the service ready to do useful work. It:
584              
585             =over 4
586              
587             =item *
588              
589             Pulls in the contents of the HELIOS_DEBUG and HELIOS_INI env vars, and sets the appropriate
590             instance variables if necessary.
591              
592             =item *
593              
594             Calls the getConfigFromIni() method to read the appropriate configuration parameters from the
595             INI file.
596              
597             =item *
598              
599             Calls the getConfigFromDb() method to read the appropriate configuration parameters from the
600             Helios database.
601              
602             =back
603              
604             Normally it returns a true value if successful, but if one of the getConfigFrom*() methods throws
605             an exception, that exception will be raised to your calling routine.
606              
607             =cut
608              
609             # BEGIN CODE Copyright (C) 2012 by Andrew Johnson.
610              
611             sub prep {
612 0     0 1   my $self = shift;
613 0           my %params = @_;
614 0           my $cached_config;
615             my $driver;
616 0           my $loggers;
617 0           my $inifile;
618              
619             # if we were explicitly given setup information, use that
620             # instead of setting up new ones
621 0 0         if ( defined($params{CACHED_CONFIG}) ) {
622 0           $cached_config = $params{CACHED_CONFIG};
623             }
624 0 0         if ( defined($params{DRIVER}) ) {
625 0           $driver = $params{DRIVER};
626             }
627 0 0 0       if ( defined($params{LOGGERS}) && keys(%{$params{LOGGERS}}) ) {
  0            
628 0           $loggers = $params{LOGGERS};
629             }
630 0 0         if ( defined($params{INIFILE}) ) {
631 0           $inifile = $params{INIFILE};
632             }
633              
634             # pull other parameters from environment
635              
636             # END CODE Copyright (C) 2012 by Andrew Johnson.
637             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
638             # If hostname value is not set,
639             # 1) use the cached value if we have one, or
640             # 2) go ahead and call hostname() (and cache it for later)
641 0 0         if ( !defined($self->getHostname()) ) {
642             # [LH] [2013-10-18] Changed hostname handling to reduce hostname lookups.
643 0 0         if ( $CACHED_HOSTNAME ) {
644 0           $self->setHostname($CACHED_HOSTNAME);
645             } else {
646 0           $CACHED_HOSTNAME = hostname();
647 0           $self->setHostname($CACHED_HOSTNAME);
648             }
649             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
650             # BEGIN CODE Copyright (C) 2012 by Andrew Johnson.
651             }
652            
653 0 0         if ( defined($ENV{HELIOS_DEBUG}) ) {
654 0           $self->debug($ENV{HELIOS_DEBUG});
655             }
656             SWITCH: {
657             # explicitly giving an inifile to prep() overrides everything
658 0 0         if ( defined($inifile) ) { $self->setIniFile($inifile); last SWITCH; }
  0            
  0            
  0            
659             # if inifile is already set, we'll leave it alone
660 0 0         if ( defined($self->getIniFile()) ) { last SWITCH; }
  0            
661             # we'll pull in the HELIOS_INI environment variable
662 0 0         if ( defined($ENV{HELIOS_INI}) ) { $self->setIniFile($ENV{HELIOS_INI}); }
  0            
663             }
664            
665 0 0         if ( defined($cached_config) ) {
666 0           $self->setConfig($cached_config);
667 0           return 1;
668             } else {
669             # initialize config module if it isn't already initialized
670 0 0         unless ($INIT_CONFIG_CLASS) {
671 0           $INIT_CONFIG_CLASS = $self->initConfig();
672             }
673 0           my $conf = $INIT_CONFIG_CLASS->parseConfig();
674              
675 0           $self->setConfig($conf);
676             }
677              
678             # use the given D::OD driver if we were given one
679             # otherwise call getDriver() to make sure we have one
680 0 0         if ( defined($driver) ) {
681 0           $self->setDriver($driver);
682             } else {
683 0           $self->getDriver();
684             }
685            
686             # make sure loggers are init()ed
687 0 0         unless ( defined($loggers) ) {
688 0           $self->initLoggers();
689             }
690              
691 0           return 1;
692             }
693             # END Code Copyright Andrew Johnson.
694              
695             =head2 getConfigFromIni([$inifile]) DEPRECATED
696              
697             The getConfigFromIni() method opens the helios.ini file, grabs global params and config params relevant to
698             the current service class, and returns them in a hash to the calling routine. It also sets the class's
699             internal {config} hashref, so the config parameters are available via the getConfig() method.
700              
701             Typically service classes will call this once near the start of processing to pick up any relevant
702             parameters from the helios.ini file. However, calling the prep() method takes care of this for
703             you, and is the preferred method.
704              
705             =cut
706              
707             sub getConfigFromIni {
708 0     0 1   my $self = shift;
709 0           my $inifile = shift;
710              
711             # BEGIN CODE Copyright (C) 2012 by Logical Helion, LLC.
712             # getConfigFromIni() is no longer necessary.
713            
714 0 0         unless ($INIT_CONFIG_CLASS) {
715 0 0         if ( defined($inifile) ) { $self->setIniFile($inifile); }
  0            
716 0           $INIT_CONFIG_CLASS = $self->initConfig();
717             }
718 0           my $conf = $INIT_CONFIG_CLASS->parseConfFile();
719 0           $self->setConfig($conf);
720 0           return %{$conf};
  0            
721             # END CODE Copyright (C) 2012 by Logical Helion, LLC.
722              
723             }
724              
725              
726             =head2 getConfigFromDb() DEPRECATED
727              
728             The getConfigFromDb() method connects to the Helios database, retrieves config params relevant to the
729             current service class, and returns them in a hash to the calling routine. It also sets the class's
730             internal {config} hashref, so the config parameters are available via the getConfig() method.
731              
732             Typically service classes will call this once near the start of processing to pick up any relevant
733             parameters from the helios.ini file. However, calling the prep() method takes care of this for
734             you.
735              
736             There's an important subtle difference between getConfigFromIni() and getConfigFromDb():
737             getConfigFromIni() erases any previously set parameters from the class's internal {config} hash,
738             while getConfigFromDb() merely updates it. This is due to the way helios.pl uses the methods:
739             the INI file is only read once, while the database is repeatedly checked for configuration
740             updates. For individual service classes, the best thing to do is just call the prep() method; it
741             will take care of things for the most part.
742              
743             =cut
744              
745             sub getConfigFromDb {
746 0     0 1   my $self = shift;
747 0           my $params = $self->getConfig();
748              
749             # BEGIN CODE Copyright (C) 2012 by Logical Helion, LLC.
750             # getConfigFromDb() method is no longer necessary.
751              
752 0 0         unless ($INIT_CONFIG_CLASS) {
753 0           $INIT_CONFIG_CLASS = $self->initConfig();
754             }
755 0           my $dbconf = $INIT_CONFIG_CLASS->parseConfDb();
756 0           while (my ($key, $value) = each %$dbconf ) {
757 0           $params->{$key} = $value;
758             }
759 0           $self->setConfig($params);
760 0           return %{$params};
  0            
761             # END CODE Copyright (C) 2012 by Logical Helion, LLC.
762              
763             }
764              
765              
766             =head2 getFuncidFromDb() [DEPRECATED]
767              
768             Queries the collective database for the funcid of the service class and
769             returns it to the calling routine. The service name used in the query is the
770             value returned from the getJobType() accessor method.
771              
772             This method is most commonly used by helios.pl to get the funcid associated
773             with a particular service class, so it can scan the job table for waiting jobs.
774             If their are jobs for the service waiting, helios.pl may launch new worker
775             processes to perform these jobs.
776              
777             As of Helios 2.80, getFuncidFromDb() has been replaced by lookupJobtypeid().
778             This method is thus deprecated.
779              
780             =cut
781              
782             sub getFuncidFromDb {
783 0     0 1   my $self = shift;
784 0           my $params = $self->getConfig();
785 0           my $jobtype = $self->getJobType();
786 0           my @funcids;
787              
788 0 0         if ($self->debug) { print "Retrieving funcid for ".$self->getJobType()."\n"; }
  0            
789              
790             eval {
791 0           my $driver = $self->getDriver();
792             # also get the funcid
793 0           my @funcids = $driver->search('TheSchwartz::FuncMap' => { funcname => $jobtype });
794 0 0         if ( scalar(@funcids) > 0 ) {
795 0           $self->setFuncid( $funcids[0]->funcid() );
796             }
797 0           1;
798 0 0         } or do {
799 0           my $E = $@;
800 0           Helios::Error::DatabaseError->throw("$E");
801             };
802              
803 0           return $self->getFuncid();
804             }
805              
806              
807             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
808             # [LH] [2013-10-04] Virtual jobtype code.
809             =head2 lookupAltJobtypeids(@jobtypenames)
810              
811             The lookupAltJobtypeids() method uses the lookupJobtypeid() method to determine
812             the jobtypeids of all of the service instance's alternate jobtypes. If given
813             a list of jobtype names, these will override any jobtypes previously set with
814             the setAltJobTypes() or addAltJobType() methods.
815              
816             Usually, "alternate" jobtypes and jobtypes specified on the helios.pl
817             command line using the --jobtypes option. The "primary" jobtype is the jobtype
818             matching the service class's name.
819              
820             =cut
821              
822             sub lookupAltJobtypeids {
823 0     0 0   my $self = shift;
824 0   0       my @jobtypes = @_ || $self->getAltJobTypes();
825 0           my $config = $self->getConfig();
826 0           my @ids;
827            
828 0           for (@jobtypes) {
829 0           my $jtid = $self->lookupJobtypeid($_);
830 0 0         unless ($jtid) { Helios::Error::JobTypeError->throw("lookupAltJobtypeids(): $_ cannot be found in collective database."); }
  0            
831 0           push(@ids, $jtid);
832 0           $self->addAltJobtypeid($jtid);
833             }
834 0           return @ids;
835             }
836              
837              
838             =head2 lookupJobtypeid($jobtypename)
839              
840             Given the name of a jobtype, lookupJobtypeid() uses the Helios::JobType class
841             to find the jobtypeid of the jobtype and returns it to the calling routine.
842             If the jobtype does not exist, the method returns undef.
843              
844             =cut
845              
846             sub lookupJobtypeid {
847 0     0 1   my $self = shift;
848 0           my $jt = shift;
849              
850 0           my $jobtype = Helios::JobType->lookup(name => $jt, config => $self->getConfig());
851 0 0         if ($jobtype) {
852 0           return $jobtype->getJobtypeid();
853             } else {
854 0           return undef;
855             }
856             }
857             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
858              
859              
860             =head2 jobsWaiting()
861              
862             Scans the job queue for jobs that are ready to run. Returns the number of jobs
863             waiting. Only meant for use with the helios.pl service daemon.
864              
865             =cut
866              
867             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
868             # [LH] [2013-10-04] jobsWaiting() replaced with new version for virtual
869             # jobtypes.
870             sub jobsWaiting {
871 0     0 1   my $self = shift;
872 0           my $num_of_jobs = 0;
873 0           my $primary_jobtypeid = $self->getJobtypeid();
874 0           my @alt_jobtypeids;
875             my $sth;
876             eval {
877 0           my $dbh = $self->dbConnect();
878 0 0         unless ( defined($primary_jobtypeid) ) {
879 0           $primary_jobtypeid = $self->lookupJobtypeid($self->getJobType);
880 0           $self->setJobtypeid($primary_jobtypeid);
881             }
882 0 0         if ( $self->getAltJobTypes() ) {
883 0 0         if ( $self->getAltJobtypeids() ) {
884 0           @alt_jobtypeids = $self->getAltJobtypeids();
885             } else {
886 0           @alt_jobtypeids = $self->lookupAltJobtypeids();
887             }
888             }
889            
890 0 0         if (@alt_jobtypeids) {
891 0           my @plhrs = ('?'); # one for the primary
892 0           for (@alt_jobtypeids) { push(@plhrs,'?'); }
  0            
893 0           my $plhrs_str = join(',' => @plhrs);
894              
895 0           $sth = $dbh->prepare_cached("SELECT COUNT(*) FROM job WHERE funcid IN($plhrs_str) AND (run_after < ?) AND (grabbed_until < ?)");
896 0           $sth->execute($primary_jobtypeid, @alt_jobtypeids, time(), time());
897             } else {
898 0           $sth = $dbh->prepare_cached('SELECT COUNT(*) FROM job WHERE funcid = ? AND (run_after < ?) AND (grabbed_until < ?)');
899 0           $sth->execute($primary_jobtypeid, time(), time());
900             }
901 0           my $r = $sth->fetchrow_arrayref();
902 0           $sth->finish();
903 0           $num_of_jobs = $r->[0];
904            
905 0           1;
906 0 0         } or do {
907 0           my $E = $@;
908 0           Helios::Error::DatabaseError->throw("$E");
909             };
910            
911 0           return $num_of_jobs;
912             }
913             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
914              
915              
916             # BEGIN CODE Copyright (C) 2012 by Andrew Johnson.
917              
918             =head2 initDriver()
919              
920             Creates a Data::ObjectDriver object connected to the Helios database and
921             returns it to the calling routine. Normally called by getDriver() if an
922             D::OD object has not already been initialized.
923              
924             The initDriver() method calls setDriver() to cache the D::OD
925             object for use by other methods. This will greatly reduce the number of open
926             connections to the Helios database.
927              
928             =cut
929              
930             sub initDriver {
931 0     0 1   my $self = shift;
932 0           my $config = $self->getConfig();
933 0 0         if ($self->debug) { print $config->{dsn},$config->{user},$config->{password},"\n"; }
  0            
934             my $driver = Helios::ObjectDriver::DBI->new(
935             dsn => $config->{dsn},
936             username => $config->{user},
937             password => $config->{password}
938 0           );
939 0 0         if ($self->debug) { print "initDriver() DRIVER: ",$driver,"\n"; }
  0            
940 0           $self->setDriver($driver);
941 0           return $driver;
942             }
943             # END CODE Copyright (C) 2012 by Andrew Johnson.
944              
945             =head2 shouldExitOverdrive()
946              
947             Determine whether or not to exit if OVERDRIVE mode is enabled. The config
948             params will be checked for HOLD, HALT, or OVERDRIVE values. If HALT is defined
949             or HOLD == 1 this method will return a true value, indicating the worker
950             process should exit().
951              
952             This method is used by helios.pl and Helios::Service->work(). Normal Helios
953             services do not need to use this method directly.
954              
955             =cut
956              
957             sub shouldExitOverdrive {
958 0     0 1   my $self = shift;
959 0           my $params = $self->getConfig();
960 0 0         if ( defined($params->{HALT}) ) { return 1; }
  0            
961 0 0 0       if ( defined($params->{HOLD}) && $params->{HOLD} == 1) { return 1; }
  0            
962 0 0 0       if ( defined($params->{WORKER_MAX_TTL}) && $params->{WORKER_MAX_TTL} > 0 &&
      0        
963             time() > $WORKER_START_TIME + $params->{WORKER_MAX_TTL} ) {
964 0           return 1;
965             }
966 0           return 0;
967             }
968              
969              
970              
971             =head1 METHODS AVAILABLE TO SERVICE SUBCLASSES
972              
973             The methods in this section are available for use by Helios services. They
974             allow your service to interact with the Helios environment.
975              
976             =cut
977              
978             # BEGIN CODE Copyright (C) 2012 by Logical Helion, LLC.
979              
980             =head2 dbConnect($dsn, $user, $password, $options)
981              
982             Method to connect to a database in a "safe" way. If the connection parameters
983             are not specified, a connection to the Helios collective database will be
984             returned. If a connection to the given database already exists, dbConnect()
985             will return a database handle to the existing connection rather than create a
986             new connection.
987              
988             The dbConnect() method uses the DBI->connect_cached() method to reuse database
989             connections and thus reduce open connections to your database (often important
990             when you potentially have hundreds of active worker processes working in a
991             Helios collective). It "tags" the connections it creates with the current PID
992             to prevent reusing a connection that was established by a parent process.
993             That, combined with helios.pl clearing connections after the fork() to create
994             a worker process, should allow for safe database connection/disconnection in
995             a forking environment.
996              
997             =cut
998              
999             sub dbConnect {
1000 0     0 1   my $self = shift;
1001 0           my $dsn = shift;
1002 0           my $user = shift;
1003 0           my $password = shift;
1004 0           my $options = shift;
1005 0           my $params = $self->getConfig();
1006 0           my $connect_to_heliosdb = 0;
1007              
1008             # if we weren't given params,
1009             # we'll default to the Helios collective database
1010 0 0         unless ( defined($dsn) ) {
1011 0           $dsn = $params->{dsn};
1012 0           $user = $params->{user};
1013 0           $password = $params->{password};
1014 0           $options = $params->{options};
1015 0           $connect_to_heliosdb = 1;
1016             }
1017              
1018 0           my $dbh;
1019             my $o;
1020              
1021             eval {
1022              
1023             # if we were given options, parse them into a hashref
1024             # throw a config error if this fails
1025 0 0         if ($options) {
1026 0           $o = eval "{$options}";
1027 0 0         Helios::Error::ConfigError->throw($@) if $@;
1028             }
1029            
1030             # if we're connecting to the collective db,
1031             # we _must_ force certain options to make sure the "new" connection
1032             # doesn't disrupt Helios operations
1033             # (Previous dbConnect() code didn't properly handle connection creation
1034             # because it effectively ignored the "options" config param
1035 0 0         if ( $connect_to_heliosdb ) {
1036 0           $o->{RaiseError} = 1;
1037 0           $o->{AutoCommit} = 1;
1038             }
1039             # ALL db connections created by dbConnect() get a "tag"
1040             # this is to generally make sure if a fork has happened,
1041             # we don't allow DBI to reuse a connection the parent made
1042             # (helios.pl should be clearing those now, though)
1043 0           $o->{'private_heliconn_dbconnect_'.$$} = $$;
1044            
1045             # debug
1046 0 0         if ($self->debug) {
1047 0           print "dbConnect():\n\tdsn=$dsn\n";
1048 0 0         if ( defined($user) ) { print "\tuser=$user\n"; }
  0            
1049 0 0         if ( defined($options)) { print "\toptions=$options\n"; }
  0            
1050             }
1051              
1052             # make the connection!
1053 0           $dbh = DBI->connect_cached($dsn, $user, $password, $o);
1054              
1055             # if we *didn't* get a database connection, we have to throw an error
1056 0 0         unless ( defined($dbh) ) {
1057 0           Helios::Error::DatabaseError->throw($DBI::errstr);
1058             }
1059              
1060 0           1;
1061 0 0         } or do {
1062             # whatever exception was thrown,
1063             # we're going to cast it into a DatabaseError
1064 0           my $E = $@;
1065 0           Helios::Error::DatabaseError->throw("$E");
1066             };
1067            
1068 0           return $dbh;
1069             }
1070             # END CODE Copyright (C) 2012 by Logical Helion, LLC.
1071              
1072              
1073             =head2 logMsg([$job,] [$priority_level,] $message)
1074              
1075             Given a message to log, an optional priority level, and an optional Helios::Job
1076             object, logMsg() will record the message in the logging systems that have been
1077             configured. The internal Helios logging system is the only system enabled by
1078             default.
1079              
1080             In addition to the log message, there are two optional parameters:
1081              
1082             =over 4
1083              
1084             =item $job
1085              
1086             The current Helios::Job object being processed. If specified, the jobid will
1087             be logged in the database along with the message.
1088              
1089             =item $priority
1090              
1091             The priority level of the message as defined by Helios::LogEntry::Levels.
1092             These are really integers, but if you import Helios::LogEntry::Levels (with the
1093             :all tag) into your namespace, your logMsg() calls will be much more readable.
1094             There are 8 log priority levels, corresponding (for historical reasons) to
1095             the log priorities defined by Sys::Syslog:
1096              
1097             name priority
1098             LOG_EMERG 0
1099             LOG_ALERT 1
1100             LOG_CRIT 2
1101             LOG_ERR 3
1102             LOG_WARNING 4
1103             LOG_NOTICE 5
1104             LOG_INFO 6
1105             LOG_DEBUG 7
1106            
1107             LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, and LOG_ERR are the most common
1108             used by Helios itself; LOG_INFO is the default.
1109              
1110             =back
1111              
1112             The host, process id, and service class are automatically recorded with your log
1113             message. If you supplied either a Helios::Job object or a priority level, these
1114             will also be recorded with your log message.
1115              
1116             This method returns a true value if successful and throws a
1117             Helios::Error::LoggingError if errors occur.
1118              
1119             =head3 LOGGING SYSTEM CONFIGURATION
1120              
1121             Several parameters are available to configure Helios logging. Though these
1122             options can be set either in helios.ini or in the Ctrl Panel, it is B
1123             recommended these options only be set in helios.ini. Changing logging
1124             configurations on-the-fly could potentially cause a Helios service (and
1125             possibly your whole collective) to become unstable!
1126              
1127             The following options can be set in either a [global] section or in an
1128             application section of your helios.ini file.
1129              
1130             =head4 loggers
1131              
1132             loggers=HeliosX::Logger::Syslog,HeliosX::Logger::Log4perl
1133              
1134             A comma delimited list of interface classes to external logging systems. Each
1135             of these classes should implement (or otherwise extend) the Helios::Logger
1136             class. Each class will have its own configuration parameters to
1137             set; consult the documentation for the interface class you're trying to
1138             configure.
1139              
1140             =head4 internal_logger
1141              
1142             internal_logger=on|off
1143              
1144             Whether to enable the internal Helios logging system as well as the loggers
1145             specified with the 'loggers=' line above. The default is on. If set to off,
1146             the only logging your service will do will be to the external logging systems.
1147              
1148             =head4 log_priority_threshold
1149              
1150             log_priority_threshold=1|2|3|4|5|6
1151              
1152             You can specify a logging threshold to better control the
1153             logging of your service on-the-fly. Unlike the above parameters,
1154             log_priority_threshold can be safely specified in your Helios Ctrl Panel.
1155             Specifying a 'log_priority_threshold' config parameter in your helios.ini or
1156             Ctrl Panel will cause log messages of a lower priority (higher numeric value)
1157             to be discarded. For example, a line in your helios.ini like:
1158              
1159             log_priority_threshold=6
1160              
1161             will cause any log messages of priority 7 (LOG_DEBUG) to be discarded.
1162              
1163             This configuration option is supported by the internal Helios logger
1164             (Helios::Logger::Internal). Other Helios::Logger systems may or may not
1165             support it; check the documentation of the logging module you plan to use.
1166              
1167             If anything goes wrong with calling the configured loggers' logMsg() methods,
1168             this method will attempt to catch the error and log it to the
1169             Helios::Logger::Internal internal logger. It will then rethrow the error
1170             as a Helios::Error::LoggingError exception.
1171              
1172             =cut
1173              
1174             # BEGIN CODE Copyright (C) 2009-12 by Andrew Johnson.
1175             sub logMsg {
1176 0     0 1   my $self = shift;
1177 0           my @args = @_;
1178 0           my $job;
1179             my $level;
1180 0           my $msg;
1181 0           my @loggers;
1182              
1183              
1184             # were we called with 3 params? ($job, $level, $msg)
1185             # 2 params? ($level, $msg) or ($job, $msg)
1186             # or just 1? ($msg)
1187              
1188             # is the first arg is a Helios::Job object?
1189 0 0 0       if ( ref($args[0]) && $args[0]->isa('Helios::Job') ) {
1190 0           $job = shift @args;
1191             }
1192              
1193             # if there are 2 params remaining, the first is level, second msg
1194             # if only one, it's just the message
1195 0 0 0       if ( defined($args[0]) && defined($args[1]) ) {
1196 0           $level = $args[0];
1197 0           $msg = $args[1];
1198             } else {
1199 0           $level = LOG_INFO; # default the level to LOG_INFO
1200 0           $msg = $args[0];
1201             }
1202              
1203             # the loggers should already know these,
1204             # but in case of emergency we'll need them
1205 0           my $config = $self->getConfig();
1206 0           my $jobType = $self->getJobType();
1207 0           my $hostname = $self->getHostname();
1208 0           my $driver = $self->getDriver();
1209              
1210 0           foreach my $logger (keys %INIT_LOG_CLASSES) {
1211             eval {
1212 0           $logger->logMsg($job, $level, $msg);
1213 0           1;
1214 0 0         } or do {
1215 0           my $E = $@;
1216 0           print "$E\n";
1217 0           Helios::Logger::Internal->setConfig($config);
1218 0           Helios::Logger::Internal->setJobType($jobType);
1219 0           Helios::Logger::Internal->setHostname($hostname);
1220 0           Helios::Logger::Internal->setDriver($driver);
1221 0           Helios::Logger::Internal->init();
1222 0           Helios::Logger::Internal->logMsg(undef, LOG_EMERG, $logger.' LOGGING FAILURE: '.$E);
1223             };
1224             }
1225            
1226 0           return 1;
1227             }
1228             # END CODE Copyright (C) 2009-12 by Andrew Johnson.
1229              
1230              
1231             # BEGIN CODE Copyright (C) 2012 by Logical Helion, LLC.
1232              
1233             =head2 initConfig()
1234              
1235             The initConfig() method is called to initialize the configuration parsing
1236             class. This method is normally called by the prep() method before a service's
1237             run() method is called; most Helios application developers do not need to
1238             worry about this method.
1239              
1240             The normal Helios config parsing class is Helios::Config. This can be
1241             changed by specifying another config class with the ConfigClass() method in
1242             your service.
1243              
1244             This method will throw a Helios::Error::ConfigError if anything goes wrong
1245             with config class initialization.
1246              
1247             =cut
1248              
1249             sub initConfig {
1250 0     0 1   my $self = shift;
1251 0 0         my $config_class = $self->ConfigClass() ? $self->ConfigClass() : 'Helios::Config';
1252            
1253             # only initialize the config system once
1254 0 0         unless( defined($INIT_CONFIG_CLASS) ) {
1255              
1256             # if ( $config_class !~ /^[A-Za-z]([A-Za-z0-9_\-]|:{2})*[A-Za-z0-9_\-]$/ ) {
1257             # Helios::Error::ConfigError->throw("Requested Config class name is invalid: ".$config_class);
1258             # }
1259             #
1260             # # attempt class load if it hasn't been already
1261             # unless ( $config_class->can('init') ) {
1262             # eval "require $config_class";
1263             # Helios::Error::ConfigError->throw($@) if $@;
1264             # }
1265              
1266 0           $self->_require_module($config_class, 'Helios::Config');
1267            
1268 0           $config_class->init(
1269             CONF_FILE => $self->getIniFile(),
1270             SERVICE => $self->getJobType(),
1271             HOSTNAME => $self->getHostname(),
1272             DEBUG => $self->debug()
1273             );
1274 0           $INIT_CONFIG_CLASS = $config_class;
1275             }
1276 0           return $config_class;
1277             }
1278              
1279             # END CODE Copyright (C) 2012 by Logical Helion, LLC.
1280              
1281              
1282             =head2 initLoggers()
1283              
1284             The initLoggers() method is called to initialize all of the configured
1285             Helios::Logger classes. This method is normally called by the prep() method
1286             before a service's run() method is called.
1287              
1288             This method sets up the Helios::Logger subclass's configuration by calling
1289             setConfig(), setHostname(), setJobType(), and setDriver(). It then calls the
1290             logger's init() method to finish the initialization phase of the logging class.
1291              
1292             This method will throw a Helios::Error::Logging error if anything goes wrong
1293             with the initialization of a logger class. It will also attempt to fall back
1294             to the Helios::Logger::Internal logger to attempt to log the initialization
1295             error.
1296              
1297             =cut
1298              
1299             # BEGIN CODE Copyright (C) 2012 by Andrew Johnson.
1300              
1301             sub initLoggers {
1302 0     0 1   my $self = shift;
1303 0           my $config = $self->getConfig();
1304 0           my $jobType = $self->getJobType();
1305 0           my $hostname = $self->getHostname();
1306 0           my $driver = $self->getDriver();
1307 0           my $debug = $self->debug();
1308 0           my @loggers;
1309              
1310             # grab the names of all the configured loggers to try
1311 0 0         if ( defined($config->{loggers}) ) {
1312 0           @loggers = split(/,/, $config->{loggers});
1313             }
1314            
1315             # inject the internal logger automatically
1316             # UNLESS it has been specifically turned off
1317 0 0 0       unless ( defined($config->{internal_logger}) &&
      0        
1318             ( $config->{internal_logger} eq 'off' || $config->{internal_logger} eq '0') ) {
1319 0           unshift(@loggers, 'Helios::Logger::Internal');
1320             }
1321              
1322              
1323 0           foreach my $logger (@loggers) {
1324             # init the logger if it hasn't been initialized yet
1325 0 0         unless ( defined($INIT_LOG_CLASSES{$logger}) ) {
1326             # if ( $logger !~ /^[A-Za-z]([A-Za-z0-9_\-]|:{2})*[A-Za-z0-9_\-]$/ ) {
1327             # Helios::Error::LoggingError->throw("Sorry, requested Logger name is invalid: ".$logger);
1328             # }
1329             # # attempt to init the class
1330             # unless ( $logger->can('init') ) {
1331             # eval "require $logger";
1332             # throw Helios::Error::LoggingError($@) if $@;
1333             # }
1334 0           $self->_require_module($logger,'Helios::Logger');
1335 0           $logger->setConfig($config);
1336 0           $logger->setJobType($jobType);
1337 0           $logger->setHostname($hostname);
1338 0           $logger->setDriver($driver);
1339             # $logger->debug($debug);
1340             eval {
1341 0           $logger->init();
1342 0           1;
1343 0 0         } or do {
1344             # our only resort is to use the internal logger
1345 0           my $E = $@;
1346 0           print "$E\n";
1347 0           Helios::Logger::Internal->setConfig($config);
1348 0           Helios::Logger::Internal->setJobType($jobType);
1349 0           Helios::Logger::Internal->setHostname($hostname);
1350 0           Helios::Logger::Internal->setDriver($driver);
1351 0           Helios::Logger::Internal->init();
1352 0           Helios::Logger::Internal->logMsg(undef, LOG_EMERG, $logger.' CONFIGURATION ERROR: '.$E);
1353             # we need to go ahead and rethrow the error to stop the init process
1354 0           Helios::Error::LoggingError->throw($E);
1355             };
1356 0           $INIT_LOG_CLASSES{$logger} = $logger;
1357 0 0         if ($self->debug) { print "Initialized Logger: $logger\n"; }
  0            
1358             }
1359             }
1360             }
1361             # END CODE Copyright (C) 2012 by Andrew Johnson.
1362              
1363              
1364             =head2 getJobArgs($job)
1365              
1366             Given a Helios::Job object, getJobArgs() returns a hashref representing the
1367             parsed job argument XML. It actually calls the Helios::Job object's parseArgs()
1368             method and returns its value.
1369              
1370             =cut
1371              
1372             sub getJobArgs {
1373 0     0 1   my $self = shift;
1374 0           my $job = shift;
1375 0 0         return $job->getArgs() ? $job->getArgs() : $job->parseArgs();
1376             }
1377              
1378              
1379             =head1 JOB COMPLETION METHODS
1380              
1381             These methods should be called in your Helios service class's run() method to
1382             mark a job as successfully completed, failed, or failed permanently. They
1383             actually call the appropriate methods of the given Helios::Job object.
1384              
1385             =head2 completedJob($job)
1386              
1387             Marks $job as completed successfully.
1388              
1389             =cut
1390              
1391             sub completedJob {
1392 0     0 1   my $self = shift;
1393 0           my $job = shift;
1394 0           return $job->completed();
1395             }
1396              
1397              
1398             =head2 failedJob($job [, $error][, $exitstatus])
1399              
1400             Marks $job as failed. Allows job to be retried if your subclass supports that
1401             (see max_retries()).
1402              
1403             =cut
1404              
1405             sub failedJob {
1406 0     0 1   my $self = shift;
1407 0           my $job = shift;
1408 0           my $error = shift;
1409 0           my $exitstatus = shift;
1410 0           return $job->failed($error, $exitstatus);
1411             }
1412              
1413              
1414             =head2 failedJobPermanent($job [, $error][, $exitstatus])
1415              
1416             Marks $job as permanently failed (no more retries allowed).
1417              
1418             =cut
1419              
1420             sub failedJobPermanent {
1421 0     0 1   my $self = shift;
1422 0           my $job = shift;
1423 0           my $error = shift;
1424 0           my $exitstatus = shift;
1425 0           return $job->failedNoRetry($error, $exitstatus);
1426             }
1427              
1428              
1429             =head2 deferredJob($job)
1430              
1431             Defers processing of a job until its grabbed_until interval expires (default
1432             is 60 minutes). This feature requires TheSchwartz 1.10.
1433              
1434             =cut
1435              
1436             sub deferredJob {
1437 0     0 1   my $self = shift;
1438 0           my $job = shift;
1439 0           return $job->deferred();
1440             }
1441              
1442             =head2 burstJob($metajob)
1443              
1444             Given a metajob, burstJob bursts it into its constituent jobs for other Helios workers to process.
1445             Normally Helios::Service's internal methods will take care of bursting jobs, but the method can be
1446             overridden if a job service needs special bursting capabilities.
1447              
1448             =cut
1449              
1450             sub burstJob {
1451 0     0 1   my $self = shift;
1452 0           my $job = shift;
1453 0           my $jobnumber = $job->burst();
1454 0           return $jobnumber;
1455             }
1456              
1457              
1458             =head1 SERVICE CLASS DEFINITION
1459              
1460             These are the basic methods that define your Helios service. The run() method
1461             is the only one required.
1462              
1463             =head2 run($job)
1464              
1465             This is a default run method for class completeness. You have to override it
1466             in your own Helios service class.
1467              
1468             =cut
1469              
1470             sub run {
1471 0     0 1   throw Helios::Error::FatalNoRetry($_[0]->getJobType.': run() method not implemented!');
1472             }
1473              
1474             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
1475              
1476             =head2 MaxRetries(), RetryInterval(), and JobLockInterval()
1477              
1478             The MaxRetries(), RetryInterval(), and JobLockInterval() methods specify to
1479             Helios the number of reattempts it should make at running a job and the
1480             frequency of those attempts. If you don't define these, jobs will not be
1481             retried if they fail.
1482              
1483             MaxRetries() is straightforward; set it to the number of times you want a job
1484             to be retried if it fails.
1485              
1486             RetryInterval() is the amount of time (in seconds) to wait after a job fails
1487             before a job is available to try again.
1488              
1489             JobLockInterval() is the amount of time (in seconds) a job is locked for
1490             processing. This amount of time should be enough time to make sure a job can
1491             be completed or at marked as failed. The default is 3600 sec (1 hour).
1492              
1493             RetryInterval() and JobLockInterval() can interact in an odd way: for example,
1494             if you want to retry a job every 60 secs, you can add:
1495              
1496             sub RetryInterval { 60 }
1497              
1498             to your service class. However, your jobs will still be locked for an hour,
1499             because 3600 is the JobLockInterval() default. If you want to retry jobs
1500             more frequently than a hour, you need to add a JobLockInterval() method to
1501             your service class as well as a RetryInterval() method. So, to retry jobs
1502             every 60 secs, add both of the following methods to your service class:
1503              
1504             sub RetryInterval { 60 }
1505             sub JobLockInterval { 60 }
1506              
1507             Keep in mind this will reduce the amount of time available for your service to
1508             mark a job as completed or failed. If it has not done so by the time the
1509             JobLockInterval() value has expired, the job will be seen by the Helios system
1510             as available for processing again, and another worker process will pick up and
1511             attempt to run the job. So always make sure your JobLockInterval() allows
1512             enough time to actually complete a job. Another rule of thumb is to set
1513             RetryInterval() and JobLockInterval() to the same value if RetryInterval() is
1514             less than 3600.
1515              
1516             =cut
1517              
1518             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
1519              
1520 0     0 1   sub MaxRetries { return undef; }
1521 0     0 1   sub RetryInterval { return undef; }
1522             # BEGIN CODE Copyright (C) 2013 by Logical Helion, LLC.
1523 0     0 1   sub JobLockInterval { undef }
1524             # END CODE Copyright (C) 2013 by Logical Helion, LLC.
1525              
1526             =head2 JobClass()
1527              
1528             Defines which job class to instantiate the job as. The default is Helios::Job,
1529             which should be fine for most purposes. If necessary, however, you can create
1530             a subclass of Helios::Job and set your JobClass() method to return that
1531             subclass's name. The service's work() method will instantiate the job as an
1532             instance of the class you specified rather than the base Helios::Job.
1533              
1534             NOTE: Please remember that "jobs" in Helios are most often only used to convey
1535             arguments to services, and usually only contain enough logic to properly parse
1536             those arguments and mark jobs as completed. It should be rare to need to
1537             extend the Helios::Job object. OTOH, if you are attempting to extend Helios
1538             itself to provide new abilities and not just writing a normal Helios
1539             application, you can use JobClass() to use your extended job class rather than
1540             the default.
1541              
1542             =cut
1543              
1544 0     0 1   sub JobClass { return undef; }
1545              
1546              
1547             # BEGIN CODE Copyright (C) 2012 by Logical Helion, LLC.
1548              
1549             =head2 ConfigClass()
1550              
1551             Defines which configuration class to use to parse your service's
1552             configuration. The default is Helios::Config, which should work fine for most
1553             applications. If necessary, you can create a subclass of Helios::Config and
1554             set your ConfigClass() method to return that subclass's name. The service's
1555             prep() method will initialize your custom config class and use it to parse your
1556             service's configuration information.
1557              
1558             See the L documentation for more information about creating
1559             custom config classes.
1560              
1561             =cut
1562              
1563 0     0 1   sub ConfigClass { return undef; }
1564              
1565             # END CODE Copyright (C) 2012 by Logical Helion, LLC.
1566              
1567              
1568             # BEGIN CODE Copyright (C) 2012 by Logical Helion, LLC.
1569              
1570             sub _require_module {
1571 0     0     my $self = shift;
1572 0           my $class = shift;
1573 0           my $requested_superclass = shift;
1574            
1575 0 0         if ( $class !~ /^[A-Za-z]([A-Za-z0-9_\-]|:{2})*[A-Za-z0-9_\-]$/ ) {
1576 0           Helios::Error::ConfigError->throw("Requested module name is invalid: $class");
1577             }
1578 0 0         unless ( $class->can('init') ) {
1579             eval {
1580 0           my $class_file = $class;
1581 0           $class_file .= '.pm';
1582 0           $class_file =~ s/::/\//g;
1583 0           require $class_file;
1584 0           1;
1585 0 0         } or do {
1586 0           my $E = $@;
1587 0           Helios::Error::ConfigError->throw("Requested module $class could not be loaded: $E");
1588             };
1589             }
1590 0 0 0       if ($requested_superclass && !$class->isa($requested_superclass)) {
1591 0           Helios::Error::ConfigError->throw("$class is not a subclass of $requested_superclass.");
1592             }
1593 0           return 1;
1594             }
1595              
1596             # END CODE Copyright (C) 2012 by Logical Helion, LLC.
1597              
1598              
1599             1;
1600             __END__