File Coverage

blib/lib/Helios/Service.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


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