line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HPPPM::ErrorHandler; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
3201
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
26
|
|
4
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
16
|
|
5
|
1
|
|
|
1
|
|
167
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
use Pod::Usage; |
7
|
|
|
|
|
|
|
use Log::Log4perl; |
8
|
|
|
|
|
|
|
use Data::Dumper; |
9
|
|
|
|
|
|
|
use Getopt::Long; |
10
|
|
|
|
|
|
|
use LWP::UserAgent; |
11
|
|
|
|
|
|
|
use Error::TryCatch; |
12
|
|
|
|
|
|
|
use namespace::autoclean; |
13
|
|
|
|
|
|
|
use English qw( -no_match_vars ); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
has 'input_parsed_xml' => ( |
18
|
|
|
|
|
|
|
is => 'rw', |
19
|
|
|
|
|
|
|
isa => 'XML::Simple', |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#Checks if the input fields that will be used to construct the SOAP |
23
|
|
|
|
|
|
|
#message have all the reqd. (per operation) types present.Both inputs |
24
|
|
|
|
|
|
|
#fields and the reqd types are mandatory inputs.Returns True if the reqd. |
25
|
|
|
|
|
|
|
#types are present |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub _check_reqd_types { |
28
|
|
|
|
|
|
|
my $self = shift; |
29
|
|
|
|
|
|
|
my $fields = shift || confess "No fields to check properties"; |
30
|
|
|
|
|
|
|
my $reqd_types = shift || confess "No types to check"; |
31
|
|
|
|
|
|
|
my $operation = $self->current_operation(); |
32
|
|
|
|
|
|
|
my $logger = Log::Log4perl->get_logger( $PROGRAM_NAME ); |
33
|
|
|
|
|
|
|
my (@present_types, $msg, $present, $reqd); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
@present_types = grep { exists $fields->{$_} } @{ $reqd_types }; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
return 1 if @present_types == @{ $reqd_types }; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
$reqd = join " ",@{ $reqd_types }; |
40
|
|
|
|
|
|
|
$present = join " ",@present_types; |
41
|
|
|
|
|
|
|
$msg = "Properties present donot match the min. no of properties"; |
42
|
|
|
|
|
|
|
$msg .= "needed for $operation operation.Properties present:$present"; |
43
|
|
|
|
|
|
|
$msg .= " Properties required:$reqd Exiting!"; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
$logger->logconfess($msg); |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
#Read and return file contents as a single string |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub _get_file_content { |
52
|
|
|
|
|
|
|
my $self = shift; |
53
|
|
|
|
|
|
|
my $fname = shift || confess "No filename to read content from"; |
54
|
|
|
|
|
|
|
my $logger = Log::Log4perl->get_logger( $PROGRAM_NAME ); |
55
|
|
|
|
|
|
|
my $fields; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
try { |
58
|
|
|
|
|
|
|
$logger->debug("About to read fields containing req fields"); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
open my $fhandle, "<", $fname |
61
|
|
|
|
|
|
|
|| throw new Error::Unhandled -text => $OS_ERROR; |
62
|
|
|
|
|
|
|
local $INPUT_RECORD_SEPARATOR = undef; |
63
|
|
|
|
|
|
|
($fields = <$fhandle>) =~ s/\\n//g; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
catch Error::Unhandled with { |
66
|
|
|
|
|
|
|
print "Unable to read $fname..Exiting! $OS_ERROR"; |
67
|
|
|
|
|
|
|
$logger->logcroak("Unable to read $fname $OS_ERROR"); |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
$logger->debug("$fname read! content: $fields"); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
return $fields; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub validate_read_cmdargs { |
77
|
|
|
|
|
|
|
my $self = shift; |
78
|
|
|
|
|
|
|
my $p = new Getopt::Long::Parser; |
79
|
|
|
|
|
|
|
my ($oper, $fields, $log_cfg, $ret, $logger, |
80
|
|
|
|
|
|
|
$user, $pawd, $help, $oper_exists); |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
$p->getoptions( |
83
|
|
|
|
|
|
|
'operation=s'=> \$oper, |
84
|
|
|
|
|
|
|
'user=s' => \$user, |
85
|
|
|
|
|
|
|
'password=s' => \$pawd, |
86
|
|
|
|
|
|
|
'fields=s' => \$fields, |
87
|
|
|
|
|
|
|
'config=s' => \$log_cfg, |
88
|
|
|
|
|
|
|
'help|?' => \$help, |
89
|
|
|
|
|
|
|
) || confess pod2usage(-verbose => 2, -noperldoc => 1, |
90
|
|
|
|
|
|
|
-msg => 'Command line options parsing failed!'); |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
#validate command line args |
93
|
|
|
|
|
|
|
pod2usage(-verbose => 2, -noperldoc => 1) if $help; |
94
|
|
|
|
|
|
|
confess pod2usage(-verbose => 2, -noperldoc => 1, -msg => 'Insufficient Args!') |
95
|
|
|
|
|
|
|
if ! ($oper || $user || $pawd || $fields || $log_cfg); |
96
|
|
|
|
|
|
|
confess pod2usage(-verbose => 2, -noperldoc => 1, -msg => "$log_cfg missing!") |
97
|
|
|
|
|
|
|
if ! (-f $log_cfg || -s $log_cfg); |
98
|
|
|
|
|
|
|
#Most important, initialize the logger first |
99
|
|
|
|
|
|
|
Log::Log4perl->init($log_cfg); |
100
|
|
|
|
|
|
|
$logger = Log::Log4perl->get_logger( $PROGRAM_NAME ); |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
$oper_exists = grep { /$oper/ } $self->get_supported_ops(); |
103
|
|
|
|
|
|
|
$logger->info("Current operation: $oper") if $oper_exists; |
104
|
|
|
|
|
|
|
$logger->logconfess("Unsupported operation: $oper") if ! $oper_exists; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
#set current oper, user and password |
107
|
|
|
|
|
|
|
$self->current_operation($oper); |
108
|
|
|
|
|
|
|
$self->user($user); |
109
|
|
|
|
|
|
|
$self->password($pawd); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
#If $fields points to a file, slurp it |
112
|
|
|
|
|
|
|
$fields = $self->_get_file_content($fields) if( -f $fields and -s $fields ); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
return $fields; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub validate_inputs { |
119
|
|
|
|
|
|
|
my $self = shift; |
120
|
|
|
|
|
|
|
my $fields = shift || confess "No args to validate"; |
121
|
|
|
|
|
|
|
my $ignore_types = shift; |
122
|
|
|
|
|
|
|
my $operation = $self->current_operation(); |
123
|
|
|
|
|
|
|
my $logger = Log::Log4perl->get_logger( $PROGRAM_NAME ); |
124
|
|
|
|
|
|
|
my %ops_inputs_reqd = %{$self->ops_inputs_reqd}; |
125
|
|
|
|
|
|
|
my (@reqd_types, $ret, $url); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
#Lookup & localize reqd types needed perform the op |
128
|
|
|
|
|
|
|
@reqd_types = @{ $ops_inputs_reqd{ $operation } }; |
129
|
|
|
|
|
|
|
$ret |
130
|
|
|
|
|
|
|
= $self->_check_reqd_types($fields, \@reqd_types); |
131
|
|
|
|
|
|
|
$logger->debug("Reqd. Types for Current Oper Present!") if $ret; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
return 1; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub validate_tokens { |
138
|
|
|
|
|
|
|
my $self = shift; |
139
|
|
|
|
|
|
|
my $fields = shift; |
140
|
|
|
|
|
|
|
my $operation = $self->current_operation(); |
141
|
|
|
|
|
|
|
my $logger = Log::Log4perl->get_logger( $PROGRAM_NAME ); |
142
|
|
|
|
|
|
|
my %ops_inputs_reqd = %{ $self->ops_inputs_reqd }; |
143
|
|
|
|
|
|
|
my (@tokens, $has_tokens, @illegal_tokens, $illegal); |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
$logger->info("No token tag in input fields!") |
146
|
|
|
|
|
|
|
if ! $fields; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
@tokens = ($fields =~ /\b((?:REQD|REQ|UD|T)\.[A-Z\._0-9]+)\b/gc); |
149
|
|
|
|
|
|
|
@illegal_tokens |
150
|
|
|
|
|
|
|
= grep {! /(^(?:REQD|REQ|UD|T)\.?(?:VP|P)?\.[A-Z_0-9]+?)$/} @tokens; |
151
|
|
|
|
|
|
|
if(@illegal_tokens) { |
152
|
|
|
|
|
|
|
$illegal = join " ",@illegal_tokens; |
153
|
|
|
|
|
|
|
$logger->logconfess("Illegal Token names: $illegal Exiting!"); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
return 1; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub check_url_availability { |
161
|
|
|
|
|
|
|
my $self = shift; |
162
|
|
|
|
|
|
|
my $service_url = shift || confess "No url to check availability"; |
163
|
|
|
|
|
|
|
my $timeout = shift || 60; |
164
|
|
|
|
|
|
|
my $ua = LWP::UserAgent->new('timeout' => $timeout); |
165
|
|
|
|
|
|
|
my $logger = Log::Log4perl->get_logger( $PROGRAM_NAME ); |
166
|
|
|
|
|
|
|
my ($resp, $msg); |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
try { |
169
|
|
|
|
|
|
|
$resp = $ua->get($service_url); |
170
|
|
|
|
|
|
|
throw new Error::Unhandled -text => $resp->status_line |
171
|
|
|
|
|
|
|
if ! $resp->is_success; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
catch Error::Unhandled with { |
174
|
|
|
|
|
|
|
$logger->logcroak($resp->status_line); |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
return 1; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub extract { |
182
|
|
|
|
|
|
|
my $self = shift; |
183
|
|
|
|
|
|
|
my $resp = shift || confess "No response to extract from"; |
184
|
|
|
|
|
|
|
my $to_extract = shift || confess "Nothing to extract"; |
185
|
|
|
|
|
|
|
my $logger = Log::Log4perl->get_logger( $PROGRAM_NAME ); |
186
|
|
|
|
|
|
|
my ($xml_parser, $xml_ref, %details, $key, $code, $string, %tag_vals); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
$logger->debug("Extracting values in tags ". join ' ', @{ $to_extract }); |
189
|
|
|
|
|
|
|
#$resp =~ s/^.+(\<?xml.*)$/$1/i if $resp =~ /^.+(\<?xml.*)$/; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
try { |
192
|
|
|
|
|
|
|
require XML::Simple |
193
|
|
|
|
|
|
|
|| throw new Error::Unhandled -text => 'XML::Simple not found'; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
$xml_parser = XML::Simple->new(); |
196
|
|
|
|
|
|
|
$xml_ref = $xml_parser->XMLin($resp); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
$logger->debug("Extracting tag values using the neat XML Parsing"); |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
for my $key (keys %{$xml_ref}) { |
201
|
|
|
|
|
|
|
next if $key !~ /\:body$/i; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
%details = %{$xml_ref->{$key}}; |
204
|
|
|
|
|
|
|
#if ( $key =~ /\:fault$/i ) { |
205
|
|
|
|
|
|
|
# $tag_vals{$_} ||= $details{$_} for @{ $to_extract }; |
206
|
|
|
|
|
|
|
#} |
207
|
|
|
|
|
|
|
#($key) = keys %details; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
$tag_vals{$_} = $details{$key}->{$_} for @{ $to_extract }; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
catch Error::Unhandled with { |
213
|
|
|
|
|
|
|
$logger->debug("Extraction Failed..."); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
if (! %tag_vals ) { |
217
|
|
|
|
|
|
|
$logger->debug("Trying to extract fault with regexp..."); |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
for my $tag ( @{ $to_extract } ) { |
220
|
|
|
|
|
|
|
$tag_vals{$tag} = $1 |
221
|
|
|
|
|
|
|
if $resp =~ /<$tag>(.+)<\/$tag>/isx; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
$logger->debug("TAGS -> VALUES: ", %tag_vals); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
return \%tag_vals; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
1; # End of HPPPM::ErrorHandler |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
__END__ |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head1 NAME |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
HPPPM::ErrorHandler - Error Handling Base class for all HPPPM Classes |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head1 VERSION |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Version 0.01 |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head1 SYNOPSIS |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
Error Handling Base class for all HPPPM Classes.Performs command line parsing, |
247
|
|
|
|
|
|
|
validation of arguments and error extraction.Desginwise, this class is meant to |
248
|
|
|
|
|
|
|
be subclassed and used transparently by HPPPM classes, however it can be |
249
|
|
|
|
|
|
|
instantiated directly. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
$hpppm = HPPPM::Demand::Management->new(); |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
$fields = $hpppm->validate_read_cmdargs(@ARGV); |
254
|
|
|
|
|
|
|
$tags = $hpppm->get_inputs($hpppm->get_current_oper()); |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
$inputs = FieldParser::parser($fields, $tags); |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
$ret = $hpppm->validate_inputs($inputs); |
259
|
|
|
|
|
|
|
$ret = $hpppm->validate_tokens($inputs->{'fields'}) |
260
|
|
|
|
|
|
|
if grep /^fields$/, @{ $tags }; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
$ret = $hpppm->extract($res, ['faultcode', 'faultstring', |
264
|
|
|
|
|
|
|
'exception:detail', 'id', 'return']); |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=head1 DESCRIPTION |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Error Handling Base class for all HPPPM Classes.Performs command line parsing, |
270
|
|
|
|
|
|
|
validation of arguments and error extraction.Desginwise, this class is meant to |
271
|
|
|
|
|
|
|
be subclassed and used transparently by HPPPM classes, however it can be |
272
|
|
|
|
|
|
|
instantiated directly. |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
The class performs validation at various levels: |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
1. Validating the presence of filenames(with data) passed as cmd args. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
2. Web service operation being performed is legal and supported. |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
3. Before posting Check if the Web Service is up and accessible or not. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
4. Validate data that will be used to create Web service request(optional). |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
The class also provides in-detail execption extraction. |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head1 METHODS |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head2 validate_read_cmdargs |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
perl bin/hpppm_demand.pl -o createRequest -u user -p password -f data/createRequest.data -c cfg/logging.conf |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
-o or --operation is the webservice operation being performed |
296
|
|
|
|
|
|
|
-u or --user user authorized to perform web service operation |
297
|
|
|
|
|
|
|
-p or --password user's password |
298
|
|
|
|
|
|
|
-f or --fields location of file containing input fields that will be used to create |
299
|
|
|
|
|
|
|
the web service request.Instead of a path this can also be a string |
300
|
|
|
|
|
|
|
containing the input fields.A sample data file for each web service |
301
|
|
|
|
|
|
|
operation has been bundled along with distribution under data dir. |
302
|
|
|
|
|
|
|
-c or --logconfig location to the configuration file that drives logging behavior. |
303
|
|
|
|
|
|
|
-h or --help or -? display help. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=head2 validate_inputs |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
Checks if the required types need in order to perform |
308
|
|
|
|
|
|
|
the operation successfully are present in the input data or not. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head2 validate_tokens |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Checks if the operation being performed supports tokens or not. If no |
313
|
|
|
|
|
|
|
tokens are needed the method returns 0.Performs the following checks on |
314
|
|
|
|
|
|
|
tokens as well -All field tokens must be all caps. Token prefixes |
315
|
|
|
|
|
|
|
(REQD, REQ, UD, T, VP, P) must be one of the specified types.All tokens |
316
|
|
|
|
|
|
|
can contain only alphanumeric characters and _ (underscore).Input is |
317
|
|
|
|
|
|
|
input fields and output is Success or Failure |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=head2 check_url_availability |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
Tests service URL for accessibility.Input is url to test and return |
322
|
|
|
|
|
|
|
Success or Failure |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=head2 extract |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Extracts the value(s) which are valid tags in the response received |
327
|
|
|
|
|
|
|
in response to the request posted to the webservice.The value(s)/tag(s) |
328
|
|
|
|
|
|
|
must be passed in as a array ref.The return value is a hash ref with |
329
|
|
|
|
|
|
|
key as the tag and value as its extracted value. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=head1 AUTHOR |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Varun Juyal, <varunjuyal123@yahoo.com> |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=head1 BUGS |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Please report any bugs or feature requests to C<bug-hpppm-demand-management at rt.cpan.org>, or through |
338
|
|
|
|
|
|
|
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=HPPPM-Demand-Management>. I will be notified, and then you'll |
339
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head1 SUPPORT |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
perldoc HPPPM::ErrorHandler |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
You can also look for information at: |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=over 4 |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=HPPPM-Demand-Management> |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
L<http://annocpan.org/dist/HPPPM-Demand-Management> |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item * CPAN Ratings |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
L<http://cpanratings.perl.org/d/HPPPM-Demand-Management> |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=item * Search CPAN |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/HPPPM-Demand-Management/> |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=back |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
Copyright 2012 Varun Juyal. |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
379
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
380
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=cut |
386
|
|
|
|
|
|
|
|