4abffcfc91f9b553ed4f128235384337e9478152
[Evergreen.git] / Open-ILS / src / support-scripts / action_trigger_runner.pl
1 #!/usr/bin/perl
2 # ---------------------------------------------------------------
3 # Copyright (C) 2009 Equinox Software, Inc
4 # Author: Bill Erickson <erickson@esilibrary.com>
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 # ---------------------------------------------------------------
16 use strict;
17 use warnings;
18 use Getopt::Long;
19 use OpenSRF::System;
20 use OpenSRF::AppSession;
21 use OpenSRF::Utils::JSON;
22 use OpenSRF::EX qw(:try);
23 use OpenILS::Utils::Fieldmapper;
24
25 my $opt_lockfile = '/tmp/action-trigger-LOCK';
26 my $opt_osrf_config = '/openils/conf/opensrf_core.xml';
27 my $opt_custom_filter = '/openils/conf/action_trigger_filters.json';
28 my $opt_max_sleep = 3600;  # default to 1 hour
29 my $opt_run_pending = 0;
30 my $opt_debug_stdout = 0;
31 my $opt_help = 0;
32 my $opt_hooks;
33 my $opt_process_hooks = 0;
34 my $opt_granularity = undef;
35
36 GetOptions(
37     'osrf-config=s' => \$opt_osrf_config,
38     'run-pending' => \$opt_run_pending,
39     'hooks=s' => \$opt_hooks,
40     'granularity=s' => \$opt_granularity,
41     'process-hooks' => \$opt_process_hooks,
42     'max-sleep' => \$opt_max_sleep,
43     'debug-stdout' => \$opt_debug_stdout,
44     'custom-filters=s' => \$opt_custom_filter,
45     'lock-file=s' => \$opt_lockfile,
46     'help' => \$opt_help,
47 );
48
49 my $max_sleep = $opt_max_sleep;
50
51 # typical passive hook filters
52 my $hook_handlers = {
53
54     # default overdue circulations
55     'checkout.due' => {
56         context_org => 'circ_lib',
57         filter => {
58             checkin_time => undef, 
59             '-or' => [
60                 {stop_fines => ['MAXFINES', 'LONGOVERDUE']}, 
61                 {stop_fines => undef}
62             ]
63         }
64     }
65 };
66
67 if ($opt_custom_filter) {
68     if (open FILTERS, $opt_custom_filter) {
69         $hook_handlers = OpenSRF::Utils::JSON->JSON2perl(join('',(<FILTERS>)));
70         close FILTERS;
71     } else {
72         die "Cannot read filter file '$opt_custom_filter'";
73     }
74 }
75
76 sub help {
77     print <<HELP;
78
79 $0 : Create and process action/trigger events
80
81     --osrf-config=<config_file>
82         OpenSRF core config file.  Defaults to:
83             /openils/conf/opensrf_core.xml
84
85     --custom-filters=<filter_file>
86         File containing a JSON Object which describes any hooks that should
87         use a user-defined filter to find their target objects.  Defaults to:
88             /openils/conf/action_trigger_filters.json
89
90     --run-pending
91         Run pending events
92
93     --process-hooks
94         Create hook events
95
96     --max-sleep=<seconds>
97         When in process-hooks mode, wait up to <seconds> for the lock file to
98         go away.  Defaults to 3600 (1 hour).
99
100     --hooks=hook1[,hook2,hook3,...]
101         Define which hooks to create events for.  If none are defined,
102         it defaults to the list of hooks defined in the --custom-filters option.
103
104     --granularity=<label>
105         Run events with {label} granularity setting, or no granularity setting
106
107     --debug-stdout
108         Print server responses to stdout (as JSON) for debugging
109
110     --lock-file=<file_name>
111         Lock file
112
113     --help
114         Show this help
115
116     Examples:
117
118         # To run all pending events.  This is what you tell CRON to run at
119         # regular intervals
120         perl $0 --osrf-config /openils/conf/opensrf_core.xml --run-pending
121
122         # To batch create all "checkout.due" events
123         perl $0 --osrf-config /openils/conf/opensrf_core.xml --hooks checkout.due
124
125 HELP
126 }
127
128
129 # create events for the specified hooks using the configured filters and context orgs
130 sub process_hooks {
131     return unless $opt_process_hooks;
132
133     my @hooks = ($opt_hooks) ? split(',', $opt_hooks) : keys(%$hook_handlers);
134     my $ses = OpenSRF::AppSession->create('open-ils.trigger');
135
136     for my $hook (@hooks) {
137     
138         my $config = $$hook_handlers{$hook} or next;
139         my $method = 'open-ils.trigger.passive.event.autocreate.batch';
140         $method =~ s/passive/active/ if $config->{active};
141         
142         my $req = $ses->request($method, $hook, $config->{context_org}, $config->{filter}, $opt_granularity);
143         while(my $resp = $req->recv(timeout => 1800)) {
144             if($opt_debug_stdout) {
145                 print OpenSRF::Utils::JSON->perl2JSON($resp->content) . "\n";
146             }
147         }
148     }
149 }
150
151 sub run_pending {
152     return unless $opt_run_pending;
153     my $ses = OpenSRF::AppSession->create('open-ils.trigger');
154     my $req = $ses->request('open-ils.trigger.event.run_all_pending' => $opt_granularity);
155
156     my $check_lockfile = 1;
157     while(my $resp = $req->recv(timeout => 7200)) {
158         if ($check_lockfile && -e $opt_lockfile) {
159             open LF, $opt_lockfile;
160             my $contents = <LF>;
161             close LF;
162             unlink $opt_lockfile if ($contents == $$);
163             $check_lockfile = 0;
164         }
165         if($opt_debug_stdout) {
166             print OpenSRF::Utils::JSON->perl2JSON($resp->content) . "\n";
167         }
168     }
169 }
170
171 help() and exit if $opt_help;
172 help() and exit unless ($opt_run_pending or $opt_process_hooks);
173
174 # check the lockfile
175 if (-e $opt_lockfile) {
176     die "I'm already running with lockfile $opt_lockfile\n" if (!$opt_process_hooks);
177     # sleeping loop if we're in --process-hooks mode
178     do { last unless ( -e $opt_lockfile ); $max_sleep--; } while ($max_sleep >= 0 && sleep(1));
179 }
180
181 # there's a tiny race condition here ... oh well
182 die "Someone else has been holding the lockfile $opt_lockfile for at least $opt_max_sleep. Giving up now ...\n" if (-e $opt_lockfile);
183
184 # set the lockfile
185 open(F, ">$opt_lockfile") or die "Unable to open lockfile $opt_lockfile for writing\n";
186 print F $$;
187 close F;
188
189 try {
190         OpenSRF::System->bootstrap_client(config_file => $opt_osrf_config);
191         Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
192     process_hooks();
193     run_pending();
194 } otherwise {
195     my $e = shift;
196     warn "$e\n";
197 };
198
199 if (-e $opt_lockfile) {
200     open LF, $opt_lockfile;
201     my $contents = <LF>;
202     close LF;
203     unlink $opt_lockfile if ($contents == $$);
204 }
205