]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Utils/Cronscript.pm
Patch from Joe Atzberger that does several things:
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Utils / Cronscript.pm
1 package OpenILS::Utils::Cronscript;
2
3 # ---------------------------------------------------------------
4 # Copyright (C) 2010 Equinox Software, Inc
5 # Author: Joe Atzberger <jatzberger@esilibrary.com>
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 # ---------------------------------------------------------------
17
18 # The purpose of this module is to consolidate the common aspects
19 # of various cron tasks that all need the same things:
20 #    ~ non-duplicative processing, i.e. lockfiles and lockfile checking
21 #    ~ opensrf_core.xml file location 
22 #    ~ common options like help and debug
23
24 use strict;
25 use warnings;
26
27 use Getopt::Long;
28 use OpenSRF::System;
29 use OpenSRF::AppSession;
30 use OpenSRF::Utils::JSON;
31 use OpenSRF::EX qw(:try);
32 use OpenILS::Utils::Fieldmapper;
33 use OpenILS::Utils::Lockfile;
34
35 use File::Basename qw/fileparse/;
36
37 use Data::Dumper;
38
39 our @extra_opts = (     # additional keys are stored here
40     # 'addlopt'
41 );
42
43 our $debug = 0;
44
45 sub _default_self {
46     return {
47     #   opts       => {},
48     #   opts_clean => {},
49     #   default_opts_clean => {},
50         default_opts       => {
51             'lock-file=s'   => OpenILS::Utils::Lockfile::default_filename,
52             'osrf-config=s' => '/openils/conf/opensrf_core.xml',   # TODO: packaging needs a make variable like @@EG_CONF_DIR@@
53             'debug'         => 0,
54             'verbose+'      => 0,
55             'help'          => 0,
56             'internal_var'  => 'XYZ',
57         },
58     #   lockfile => undef,
59     }
60 }
61
62 sub is_clean {
63     my $key = shift   or  return 1;
64     $key =~ /[=:].*$/ and return 0;
65     $key =~ /[+!]$/   and return 0;
66     return 1;
67 }
68
69 sub clean {
70     my $key = shift or return;
71     $key =~ s/[=:].*$//;
72     $key =~ s/[+!]$//;
73     return $key;
74 }
75
76 sub fuzzykey {                      # when you know the hash you want from, but not the exact key
77     my $self = shift or return;
78     my $key  = shift or return;
79     my $target = @_ ? shift : 'opts_clean';
80     foreach (map {clean($_)} keys %{$self->{default_opts}}) {  # TODO: cache
81         $key eq $_ and return $self->{$target}->{$_};
82     }
83 }
84
85 # MyGetOptions
86 # A wrapper around GetOptions
87 # {opts} does two things for GetOptions (see Getopt::Long)
88 #  (1) maps command-line options to the *other* variables where values are stored (in opts_clean)
89 #  (2) provides hashspace for the rest of the arbitrary options from the command-line
90 #
91 # TODO: allow more options to be passed here, maybe mimic Getopt::Long::GetOptions style
92
93 sub MyGetOptions {
94     my $self = shift;
95     my @keys = sort {is_clean($b) <=> is_clean($a)} keys %{$self->{default_opts}};
96     $debug and print "KEYS: ", join(", ", @keys), "\n";
97     foreach (@keys) {
98         my $clean = clean($_);
99         $self->{opts_clean}->{$clean} = $self->{default_opts_clean}->{$clean};  # prepopulate default
100         $self->{opts}->{$_} = \$self->{opts_clean}->{$clean};                   # pointer for GetOptions
101     }
102     GetOptions($self->{opts}, @keys);
103     foreach (@keys) {
104         delete $self->{opts}->{$_};     # now remove the mappings from (1) so we just have (2)
105     }
106     $self->clean_mirror('opts');        # populate clean_opts w/ cleaned versions of (2), plus everything else
107
108     print $self->help() and exit if $self->{opts_clean}->{help};
109     $debug and $OpenILS::Utils::Lockfile::debug = $debug;
110
111     unless ($self->{opts_clean}->{nolockfile} || $self->{default_opts_clean}->{nolockfile}) {
112         $self->{lockfile_obj} = OpenILS::Utils::Lockfile->new($self->first_defined('lock-file'));
113         $self->{lockfile}     = $self->{lockfile_obj}->filename;
114     }
115 }
116
117 sub first_defined {
118     my $self = shift;
119     my $key  = shift or return;
120     foreach (qw(opts_clean opts default_opts_clean default_opts)) {
121         defined $self->{$_}->{$key} and return $self->{$_}->{$key};
122     }
123     return;
124 }
125
126 sub clean_mirror {
127     my $self  = shift;
128     my $dirty = @_ ? shift : 'default_opts';
129     foreach (keys %{$self->{$dirty}}) {
130         defined $self->{$dirty}->{$_} or next;
131         $self->{$dirty . '_clean'}->{clean($_)} = $self->{$dirty}->{$_};
132     }
133 }
134
135 sub new {
136     my $class = shift;
137     my $self  = _default_self;
138     bless ($self, $class);
139     $self->init(@_);
140     $debug and print "new obj: ", Dumper($self);
141     return $self;
142 }
143
144 sub add_and_purge {
145     my $self = shift;
146     my $key  = shift;
147     my $val  = shift;
148     my $clean = clean($key);
149     my @others = grep {/$clean/ and $_ ne $key} keys %{$self->{default_opts}};
150     foreach (@others) {
151         $debug and print "variant of $key => $_\n";
152         if ($key ne $clean) {    # if it is a dirtier key, delete the clean one
153             delete $self->{default_opts}->{$_};
154             $self->{default_opts}->{$key} = $val;
155         } else {                 # else update the dirty one
156             $self->{default_opts}->{$_} = $val;
157         }
158     }
159 }
160
161 sub init {      # not INIT
162     my $self = shift;
163     my $opts  = @_ ? shift : {};    # user can specify more default options to constructor
164 # TODO: check $opts is hashref; then check verbose/debug first.  maybe check negations e.g. "no-verbose" ?
165     @extra_opts = keys %$opts;
166     foreach (@extra_opts) {        # add any other keys w/ default values
167         $self->add_and_purge($_, $opts->{$_});
168     }
169     $self->clean_mirror;
170     return $self;
171 }
172
173 sub usage {
174     my $self = shift;
175     return "\nUSAGE: $0 [OPTIONS]";
176 }
177
178 sub options_help {
179     my $self = shift;
180     my $chunk = @_ ? shift : '';
181     return <<HELP
182
183 OPTIONS:
184     --osrf-config </path/to/config_file>  Default: $self->{default_opts_clean}->{'osrf-config'}
185                  Specify OpenSRF core config file.
186
187     --lock-file </path/to/file_name>      Default: $self->{default_opts_clean}->{'lock-file'}
188                  Specify lock file.     
189
190 HELP
191     . $chunk . <<HELP;
192     --debug      Print server responses to STDOUT for debugging
193     --verbose    Set verbosity
194     --help       Show this help message
195 HELP
196 }
197
198 sub help {
199     my $self = shift;
200     return $self->usage() . "\n" . $self->options_help(@_) . $self->example();
201 }
202
203 sub example {
204     return "\n\nEXAMPLES:\n\n    $0 --osrf-config /my/other/opensrf_core.xml\n";
205 }
206
207 sub session {
208     my $self = shift or return;
209     return ($self->{session} ||= OpenSRF::AppSession->create(@_));
210 }
211
212 sub bootstrap {
213     my $self = shift or return;
214     try {
215         $debug and print "bootstrap lock-file  : ", $self->first_defined('lock-file'), "\n";
216         $debug and print "bootstrap osrf-config: ", $self->first_defined('osrf-config'), "\n";
217         OpenSRF::System->bootstrap_client(config_file => $self->first_defined('osrf-config'));
218         Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
219     } otherwise {
220         warn shift;
221     };
222 }
223
224 1;