New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
Runner.pm in vendors/lib/FCM/Admin – NEMO

source: vendors/lib/FCM/Admin/Runner.pm @ 10669

Last change on this file since 10669 was 10669, checked in by nicolasmartin, 5 years ago

Import latest FCM release from Github into the repository for testing

File size: 8.9 KB
Line 
1# ------------------------------------------------------------------------------
2# (C) British Crown Copyright 2006-17 Met Office.
3#
4# This file is part of FCM, tools for managing and building source code.
5#
6# FCM is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# FCM 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# You should have received a copy of the GNU General Public License
17# along with FCM. If not, see <http://www.gnu.org/licenses/>.
18# ------------------------------------------------------------------------------
19
20use strict;
21use warnings;
22
23package FCM::Admin::Runner;
24
25use IO::Handle;
26use POSIX qw{strftime};
27
28# The default values of the attributes
29my %DEFAULT = (
30    exceptions     => [],
31    max_attempts   => 3,
32    retry_interval => 5,
33    stderr_handle  => \*STDERR,
34    stdout_handle  => \*STDOUT,
35);
36
37my $INSTANCE;
38
39# ------------------------------------------------------------------------------
40# Returns a unique instance of this class. Creates the instance on first call.
41sub instance {
42    my ($class) = @_;
43    if (!defined($INSTANCE)) {
44        $INSTANCE = bless({%DEFAULT}, $class);
45    }
46    return $INSTANCE;
47}
48
49# ------------------------------------------------------------------------------
50# Adds a new exception to the list of exceptions.
51sub _add_exception {
52    my ($self, $exception) = @_;
53    push(@{$self->get_exceptions()}, $exception);
54}
55
56# ------------------------------------------------------------------------------
57# Returns the list of exceptions (or a reference to the list in scalar context).
58sub get_exceptions {
59    my ($self) = @_;
60    return (wantarray() ? @{$self->{exceptions}} : $self->{exceptions});
61}
62
63# ------------------------------------------------------------------------------
64# Returns the latest exception in the exception list.
65sub get_latest_exception {
66    my ($self) = @_;
67    if (exists($self->get_exceptions()->[-1])) {
68        return $self->get_exceptions()->[-1];
69    }
70    else {
71        return;
72    }
73}
74
75# ------------------------------------------------------------------------------
76# Returns the maximum number of attempts for the "run_with_retries" method.
77sub get_max_attempts {
78    my ($self) = @_;
79    return $self->{max_attempts};
80}
81
82# ------------------------------------------------------------------------------
83# Returns the retry interval for the "run_with_retries" method.
84sub get_retry_interval {
85    my ($self) = @_;
86    return $self->{retry_interval};
87}
88
89# ------------------------------------------------------------------------------
90# Returns the file handle for STDERR.
91sub get_stderr_handle {
92    my ($self) = @_;
93    if (!IO::Handle::opened($self->{stderr_handle})) {
94        $self->{stderr_handle} = $DEFAULT{stderr_handle};
95    }
96    return $self->{stderr_handle};
97}
98
99# ------------------------------------------------------------------------------
100# Returns the file handle for STDOUT.
101sub get_stdout_handle {
102    my ($self) = @_;
103    if (!IO::Handle::opened($self->{stdout_handle})) {
104        $self->{stdout_handle} = $DEFAULT{stdout_handle};
105    }
106    return $self->{stdout_handle};
107}
108
109# ------------------------------------------------------------------------------
110# Runs $sub_ref->(@arguments) with a diagnostic $message. Dies on error.
111sub run {
112    my ($self, $message, $sub_ref, @arguments) = @_;
113    printf(
114        {$self->get_stdout_handle()}
115        qq{%s: %s\n}, strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()), $message,
116    );
117    eval {
118        if (!$sub_ref->(@arguments)) {
119            die(qq{\n});
120        }
121    };
122    if ($@) {
123        my $e = $@;
124        chomp($e);
125        my $exception
126            = sprintf(qq{ERROR %s%s\n}, $message, ($e ? qq{ - $e} : qq{}));
127        $self->_add_exception($exception);
128        die($exception);
129    }
130    return 1;
131}
132
133# ------------------------------------------------------------------------------
134# Runs $sub_ref->(@arguments) with a diagnostic $message. Warns on error.
135sub run_continue {
136    my ($self, $message, $sub_ref, @arguments) = @_;
137    my $rc;
138    eval {
139        $rc = $self->run($message, $sub_ref, @arguments);
140    };
141    if ($@) {
142        print({$self->get_stderr_handle()} $@);
143        return;
144    }
145    return $rc;
146}
147
148# ------------------------------------------------------------------------------
149# Runs $sub_ref->(@arguments) with a diagnostic $message. Retries on error.
150sub run_with_retries {
151    my ($self, $message, $sub_ref, @arguments) = @_;
152    for my $i_attempt (1 .. $self->get_max_attempts()) {
153        my $attempt_message = sprintf(
154            qq{%s, attempt %d of %d},
155            $message, $i_attempt, $self->get_max_attempts(),
156        );
157        if ($i_attempt == $self->get_max_attempts()) {
158            return $self->run($attempt_message, $sub_ref, @arguments);
159        }
160        else {
161            if ($self->run_continue($attempt_message, $sub_ref, @arguments)) {
162                return 1;
163            }
164            sleep($self->get_retry_interval());
165        }
166    }
167}
168
169# ------------------------------------------------------------------------------
170# Sets the maximum number of attempts for the "run_with_retries" method.
171sub set_max_attempts {
172    my ($self, $value) = @_;
173    $self->{max_attempts} = $value;
174}
175
176# ------------------------------------------------------------------------------
177# Sets the retry interval for the "run_with_retries" method.
178sub set_retry_interval {
179    my ($self, $value) = @_;
180    $self->{retry_interval} = $value;
181}
182
183# ------------------------------------------------------------------------------
184# Sets the file handle for STDERR.
185sub set_stderr_handle {
186    my ($self, $value) = @_;
187    if (defined($value) && IO::Handle::opened($value)) {
188        $self->{stderr_handle} = $value;
189    }
190}
191
192# ------------------------------------------------------------------------------
193# Sets the file handle for STDOUT.
194sub set_stdout_handle {
195    my ($self, $value) = @_;
196    if (defined($value) && IO::Handle::opened($value)) {
197        $self->{stdout_handle} = $value;
198    }
199}
200
2011;
202__END__
203
204=head1 NAME
205
206FCM::Admin::Runner
207
208=head1 SYNOPSIS
209
210    $runner = FCM::Admin::Runner->instance();
211    $runner->run($message, sub { ... });
212
213=head1 DESCRIPTION
214
215Provides a simple way to run a piece of code with a time-stamped diagnostic
216message.
217
218=head1 METHODS
219
220=over 4
221
222=item FCM::Admin::Runner->instance()
223
224Returns a unique instance of FCM::Admin::Runner.
225
226=item $runner->get_exceptions()
227
228Returns a list containing all the exceptions captured by the previous
229invocations of the $runner->run() method. In SCALAR context, returns a reference
230to the list.
231
232=item $runner->get_latest_exception()
233
234Returns the latest exception captured by the $runner->run() method. Returns
235undef if there is no captured exception in the list.
236
237=item $runner->get_max_attempts()
238
239Returns the number of maximum retries for the
240$runner->run_with_retries($message,$sub_ref,@arguments) method. (Default: 3)
241
242=item $runner->get_retry_interval()
243
244Returns the interval (in seconds) between retries for the
245$runner->run_with_retries($message,$sub_ref,@arguments) method. (Default: 5)
246
247=item $runner->get_stderr_handle()
248
249Returns the file handle for standard error output. (Default: \*STDERR)
250
251=item $runner->get_stdout_handle()
252
253Returns the file handle for standard output. (Default: \*STDOUT)
254
255=item $runner->run($message,$sub_ref,@arguments)
256
257Prints the diagnostic $message and runs $sub_ref (with extra @arguments).
258Returns true if $sub_ref returns true. die() with a message that looks like
259"ERROR $message\n" if $sub_ref returns false or die().
260
261=item $runner->run_continue($message,$sub_ref,@arguments)
262
263Same as $runner->run($message,$sub_ref,@arguments), but only issue a warning
264(and returns false) if $sub_ref returns false or die().
265
266=item $runner->run_with_retries($message,$sub_ref,@arguments)
267
268Attempts $runner->run($message,$sub_ref,@arguments) for a number of times up to
269$runner->get_max_attempts(), with a delay of $runner->get_retry_interval()
270between each attempt. die() if $sub_ref still returns false in the final
271attempt. Returns true on success.
272
273=item $runner->set_max_attempts($value)
274
275Sets the maximum number of attempts in the
276$runner->run_with_retries($message,$sub_ref,@arguments) method.
277
278=item $runner->set_retry_interval($value)
279
280Sets the interval (in seconds) between retries for the
281$runner->run_with_retries($message,$sub_ref,@arguments) method.
282
283=item $runner->set_stderr_handle($value)
284
285Sets the file handle for standard error output to an alternate file handle. The
286$value must be a valid file descriptor.
287
288=item $runner->set_stdout_handle($value)
289
290Sets the file handle for standard output to an alternate file handle. The $value
291must be a valid file descriptor.
292
293=back
294
295=head1 COPYRIGHT
296
297E<169> Crown copyright Met Office. All rights reserved.
298
299=cut
Note: See TracBrowser for help on using the repository browser.