File Coverage

blib/lib/Google/Ads/GoogleAds/Client.pm
Criterion Covered Total %
statement 107 121 88.4
branch 18 30 60.0
condition 28 57 49.1
subroutine 18 20 90.0
pod 3 6 50.0
total 174 234 74.3


line stmt bran cond sub pod time code
1             # Copyright 2019, Google LLC
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14             #
15             # The main interface to the Google Ads API. It takes care of handling your API
16             # credentials, and exposes all of the underlying services that make up the
17             # Google Ads API.
18              
19              
20             use strict;
21 11     11   20028 use warnings;
  11         72  
  11         258  
22 11     11   45 use version;
  11         13  
  11         248  
23 11     11   3720 our $VERSION = qv("13.1.0");
  11         16481  
  11         51  
24              
25             use Google::Ads::GoogleAds::OAuth2ApplicationsHandler;
26 11     11   5015 use Google::Ads::GoogleAds::OAuth2ServiceAccountsHandler;
  11         41  
  11         349  
27 11     11   4213 use Google::Ads::GoogleAds::Logging::GoogleAdsLogger;
  11         27  
  11         321  
28 11     11   4407  
  11         40  
  11         382  
29             use Class::Std::Fast;
30 11     11   91  
  11         20  
  11         92  
31             use constant OAUTH2_APPLICATIONS_HANDLER => "OAUTH2_APPLICATIONS_HANDLER";
32 11     11   1368 use constant OAUTH2_SERVICE_ACCOUNTS_HANDLER =>
  11         21  
  11         594  
33 11         525 "OAUTH2_SERVICE_ACCOUNTS_HANDLER";
34 11     11   58 use constant AUTH_HANDLERS_ORDER =>
  11         22  
35 11         14944 (OAUTH2_APPLICATIONS_HANDLER, OAUTH2_SERVICE_ACCOUNTS_HANDLER);
36 11     11   59  
  11         17  
37             # Class::Std-style attributes. Most values are read from googleads.properties file.
38             # These need to go in the same line for older Perl interpreters to understand.
39             my %developer_token_of : ATTR(:name<developer_token> :default<>);
40             my %login_customer_id_of : ATTR(:name<login_customer_id> :default<>);
41             my %linked_customer_id_of : ATTR(:name<linked_customer_id> :default<>);
42             my %service_address_of : ATTR(:name<service_address> :default<>);
43             my %user_agent_of : ATTR(:name<user_agent> :default<>);
44             my %proxy_of : ATTR(:name<proxy> :default<>);
45             my %http_timeout_of : ATTR(:name<http_timeout> :default<>);
46             my %http_retry_timing_of : ATTR(:name<http_retry_timing> :default<>);
47             my %version_of : ATTR(:name<version> :default<>);
48             my %die_on_faults_of : ATTR(:name<die_on_faults> :default<0>);
49              
50             my %properties_file_of : ATTR(:init_arg<properties_file> :default<>);
51             my %services_of : ATTR(:name<services> :default<{}>);
52             my %auth_handlers_of : ATTR(:name<auth_handlers> :default<>);
53             my %__enabled_auth_handler_of : ATTR(:name<__enabled_auth_handler> :default<>);
54              
55             # Runtime statistics.
56             my %last_request_of : ATTR(:name<last_request> :default<>);
57             my %last_response_of : ATTR(:name<last_response> :default<>);
58              
59             # Automatically called by Class::Std after the values for all the attributes
60             # have been populated but before the constructor returns the new object.
61             my ($self, $ident) = @_;
62              
63 8     8 0 7826 # If the properties file path is not specified during instantiation, load it
64             # from the environment variable or set it to the default value.
65             $properties_file_of{$ident} ||=
66             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_CONFIGURATION_FILE_PATH};
67             $properties_file_of{$ident} ||=
68 8   100     40 Google::Ads::GoogleAds::Constants::DEFAULT_PROPERTIES_FILE;
69 8   100     27  
70             my %properties = ();
71             # If there's a valid properties file, parse it and read the config values.
72 8         13 if ($properties_file_of{$ident} && -e $properties_file_of{$ident}) {
73             %properties = __parse_properties_file($properties_file_of{$ident});
74 8 100 66     263 $developer_token_of{$ident} ||= $properties{developerToken};
75 6         38 $login_customer_id_of{$ident} ||= $properties{loginCustomerId};
76 6   33     42 $linked_customer_id_of{$ident} ||= $properties{linkedCustomerId};
77 6   66     30 $service_address_of{$ident} ||= $properties{serviceAddress};
78 6   33     28 $user_agent_of{$ident} ||= $properties{userAgent};
79 6   33     33 $proxy_of{$ident} ||= $properties{proxy};
80 6   33     27 }
81 6   33     24  
82             # Provide default values for below attributes if they weren't set by
83             # parameters to new() nor in the properties file.
84             $service_address_of{$ident} ||=
85             Google::Ads::GoogleAds::Constants::DEFAULT_SERVICE_ADDRESS;
86 8   100     26 $service_address_of{$ident} .= "/"
87             if substr($service_address_of{$ident}, -1) ne "/";
88              
89 8 100       32 $user_agent_of{$ident} ||=
90             Google::Ads::GoogleAds::Constants::DEFAULT_USER_AGENT;
91 8   50     37  
92             $http_timeout_of{$ident} ||=
93             Google::Ads::GoogleAds::Constants::DEFAULT_HTTP_TIMEOUT;
94 8   50     34 $http_retry_timing_of{$ident} ||=
95             Google::Ads::GoogleAds::Constants::DEFAULT_HTTP_RETRY_TIMING;
96 8   50     27 $version_of{$ident} ||=
97             Google::Ads::GoogleAds::Constants::DEFAULT_API_VERSION;
98 8   50     32  
99             # Setup of auth handlers.
100             my %auth_handlers = ();
101              
102 8         11 my $auth_handler = Google::Ads::GoogleAds::OAuth2ApplicationsHandler->new();
103             $auth_handler->initialize($self, \%properties);
104 8         70 $auth_handlers{OAUTH2_APPLICATIONS_HANDLER} = $auth_handler;
105 8         13175  
106 8         53 $auth_handler = Google::Ads::GoogleAds::OAuth2ServiceAccountsHandler->new();
107             $auth_handler->initialize($self, \%properties);
108 8         40 $auth_handlers{OAUTH2_SERVICE_ACCOUNTS_HANDLER} = $auth_handler;
109 8         1650  
110 8         66 $auth_handlers_of{$ident} = \%auth_handlers;
111              
112 8         17 # Initialize the logger module with the default log4perl.conf file or the default
113             # values if the file is not found.
114             # The logger module can only be initialized once, so the log settings in the
115             # code before current method call will override the default settings.
116             Google::Ads::GoogleAds::Logging::GoogleAdsLogger::initialize_logging();
117              
118 8         30 # Enable STDOUT output in utf8.
119             binmode(STDOUT, ":utf8");
120             }
121 8         162  
122             # Automatically called by Class::Std when an unknown method is invoked on an
123             # instance of this class. It is used to handle creating singletons (local to
124             # each Google::Ads::GoogleAds::Client instance) of all the services. The names
125             # of the services may change and shouldn't be hardcoded.
126             my ($self, $ident) = @_;
127             my $service_name = $_;
128              
129 5     5 0 2982 if ($service_name =~ /^\w+Service$/) {
130 5         11 if ($self->get_services()->{$service_name}) {
131             # To emulate a singleton, return the existing instance of the service if
132 5 100       28 # we already have it. The return value of AUTOMETHOD must be a sub
133 3 50       8 # reference which is then invoked, so wrap the service in sub { }.
134             return sub {
135             return $self->get_services()->{$service_name};
136             };
137             } else {
138 0     0   0 my $version = $self->get_version();
139 0         0  
140             # Check to see if the module with that service name exists.
141 3         21 # - is Google::Ads::GoogleAds::LongRunning::OperationService
142             # - under Google::Ads::GoogleAds::$version
143             # If not we warn and return nothing.
144             my $module_name =
145             $service_name eq
146             Google::Ads::GoogleAds::Constants::OPERATION_SERVICE_NAME
147 3 100       23 ? Google::Ads::GoogleAds::Constants::OPERATION_SERVICE_CLASS_NAME
148             : sprintf
149             Google::Ads::GoogleAds::Constants::GOOGLE_ADS_SERVICES_CLASS_NAME,
150             $version, $service_name;
151              
152             eval("require $module_name"); # require module name
153             if ($@) {
154             warn("Module $module_name was not found.");
155 3         161 return;
156 3 50       14 } else {
157 0         0 # Pass in this API client, so each service has access to the
158 0         0 # current properties as developer_token, login_customer_id,
159             # die_on_faults and these may change dynamically during runtime.
160             my $service = $module_name->new({api_client => $self});
161             $self->get_services()->{$service_name} = $service;
162              
163 3         26 return sub {
164 3         27 return $self->get_services()->{$service_name};
165             };
166             }
167 1     1   7 }
168 3         22 }
169             }
170              
171             # Protected method to retrieve the proper enabled authorization handler.
172             my $self = shift;
173              
174             # Check if we have cached the enabled auth_handler.
175             if ($self->get___enabled_auth_handler()) {
176 0     0   0 return $self->get___enabled_auth_handler();
177             }
178              
179 0 0       0 my $auth_handlers = $self->get_auth_handlers();
180 0         0  
181             foreach my $handler_id (AUTH_HANDLERS_ORDER) {
182             if ($auth_handlers->{$handler_id}->is_auth_enabled()) {
183 0         0 $self->set___enabled_auth_handler($auth_handlers->{$handler_id});
184             last;
185 0         0 }
186 0 0       0 }
187 0         0  
188 0         0 return $self->get___enabled_auth_handler();
189             }
190              
191             # Private method to parse values in a properties file.
192 0         0 my ($properties_file) = @_;
193             my %properties;
194              
195             # glob() to expand any metacharacters.
196             ($properties_file) = glob($properties_file);
197 6     6   18  
198 6         12 if (open(PROP_FILE, $properties_file)) {
199             # The data in the file should be in the following format:
200             # key1=value1
201 6         184 # key2=value2
202             while (my $line = <PROP_FILE>) {
203 6 50       199 chomp($line);
204              
205             # Skip comments.
206             next if ($line =~ /^#/ || $line =~ /^\s*$/);
207 6         621 my ($key, $value) = split(/=/, $line, 2);
208 54         82 $properties{$key} = $value;
209             }
210             close(PROP_FILE);
211 54 100 66     213 } else {
212 48         138 die("Couldn't open properties file $properties_file for reading: $!\n");
213 48         190 }
214             return %properties;
215 6         66 }
216              
217 0         0 my $self = shift;
218              
219 6         65 return $self->get_auth_handlers()->{OAUTH2_APPLICATIONS_HANDLER};
220             }
221              
222             my $self = shift;
223 10     10 0 6458  
224             return $self->get_auth_handlers()->{OAUTH2_APPLICATIONS_HANDLER};
225 10         18 }
226              
227             my ($self) = @_;
228              
229 1     1 1 1 return $self->get_auth_handlers()->{OAUTH2_SERVICE_ACCOUNTS_HANDLER};
230             }
231 1         3  
232             # Loads the environment variables to configure this API client.
233             my $self = shift;
234             my $ident = ident $self;
235 1     1 1 3  
236             $developer_token_of{$ident} =
237 1         11 $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_DEVELOPER_TOKEN}
238             || $developer_token_of{$ident};
239             $login_customer_id_of{$ident} =
240             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_LOGIN_CUSTOMER_ID}
241             || $login_customer_id_of{$ident};
242 1     1 1 10 $linked_customer_id_of{$ident} =
243 1         3 $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_LINKED_CUSTOMER_ID}
244             || $linked_customer_id_of{$ident};
245             $service_address_of{$ident} =
246             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_ENDPOINT}
247 1   33     7 || $service_address_of{$ident};
248             $user_agent_of{$ident} =
249             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_USER_AGENT}
250 1   33     4 || $user_agent_of{$ident};
251             $proxy_of{$ident} =
252             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_PROXY} || $proxy_of{$ident};
253 1   33     3  
254             my $auth_handler = $self->get_oauth2_applications_handler();
255             my $client_id = $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_CLIENT_ID};
256 1   33     4 my $client_secret =
257             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_CLIENT_SECRET};
258             my $refresh_token =
259 1   33     3 $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_REFRESH_TOKEN};
260              
261 1   33     2 $auth_handler->set_client_id($client_id) if $client_id;
262             $auth_handler->set_client_secret($client_secret) if $client_secret;
263 1         3 $auth_handler->set_refresh_token($refresh_token) if $refresh_token;
264 1         5  
265             $auth_handler = $self->get_oauth2_service_accounts_handler();
266 1         2 my $json_key_file_path =
267             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_JSON_KEY_FILE_PATH};
268 1         2 my $impersonated_email =
269             $ENV{Google::Ads::GoogleAds::Constants::ENV_VAR_IMPERSONATED_EMAIL};
270 1 50       2  
271 1 50       2 $auth_handler->set_json_key_file_path($json_key_file_path)
272 1 50       4 if $json_key_file_path;
273             $auth_handler->set_impersonated_email($impersonated_email)
274 1         5 if $impersonated_email;
275             }
276 1         6  
277             1;
278 1         7  
279             =pod
280 1 50       5  
281             =head1 NAME
282 1 50       5  
283             Google::Ads::GoogleAds::Client
284              
285             =head1 SYNOPSIS
286              
287             use Google::Ads::GoogleAds::Client;
288              
289             my $api_client = Google::Ads::GoogleAds::Client->new();
290              
291             my $customer_id = "1234567890";
292              
293             my $query = "SELECT campaign.id, campaign.name FROM campaign";
294              
295             my $response = $api_client->GoogleAdsService()->search({
296             customer_id => $customer_id,
297             query => $query,
298             pageSize => PAGE_SIZE
299             });
300              
301             foreach my $row (@{$response->{results}}) {
302             # Do something with the results
303             }
304              
305             =head1 DESCRIPTION
306              
307             Google::Ads::GoogleAds::Client is the main interface to the Google Ads API. It
308             takes care of handling your API credentials, and exposes all of the underlying
309             services that make up the Google Ads API.
310              
311             The C<Google::Ads::GoogleAds::Client> module should be loaded before other
312             C<Google::Ads::> modules. A warning will occur if modules are loaded in the
313             wrong order.
314              
315             =head1 ATTRIBUTES
316              
317             Each of these attributes can be set via Google::Ads::GoogleAds::Client->new().
318              
319             Alternatively, there is a get_ and set_ method associated with each attribute
320             for retrieving or setting them dynamically. For example, the set_login_customer_id()
321             allows you to change the value of the L</login_customer_id> attribute and
322             get_login_customer_id() returns the current value of the attribute.
323              
324             =head2 developer_token
325              
326             A string used to tie usage of the Google Ads API to a specific Google Ads manager
327             account.
328              
329             The value should be a character string assigned to you by Google. You can
330             apply for a Developer Token by following the instructions at
331             L<https://developers.google.com/google-ads/api/docs/first-call/dev-token>.
332              
333             =head2 login_customer_id
334              
335             This is the customer ID of the authorized customer to use in the request, without
336             hyphens. If your access to the customer account is through a manager account,
337             this attribute is required and must be set to the customer ID of the manager account.
338              
339             =head2 linked_customer_id
340              
341             This header is only required for methods that update the resources of an entity
342             when permissioned via Linked Accounts in the Google Ads UI (AccountLink resource
343             in the Google Ads API). Set this value to the customer ID of the data provider
344             that updates the resources of the specified customer ID. It should be set without
345             dashes.
346              
347             =head2 service_address
348              
349             The Google Ads API service address.
350              
351             =head2 user_agent
352              
353             The user-agent request header used to identify this application.
354              
355             =head2 proxy
356              
357             The proxy server URL to be used for internet connectivity.
358              
359             =head2 http_timeout
360              
361             The HTTP timeout value in seconds.
362              
363             =head2 http_retry_timing
364              
365             The HTTP retry timing for LWP::UserAgent::Determined. The string controls how
366             many times it should retry, and how long the pauses should be in seconds.
367              
368             =head2 version
369              
370             The version of the Google Ads API to use. The latest is the default.
371              
372             =head2 die_on_faults
373              
374             By default the client returns a L<Google::Ads::GoogleAds::GoogleAdsException> object
375             if an error has occurred at the server side. However if this flag is set to true,
376             the client will issue a die() command on received API faults.
377              
378             The default is "false".
379              
380             =head2 properties_file
381              
382             The path of the configuration file. The default value is F<googleads.properties>
383             file in the home directory.
384              
385             =head2 last_request
386              
387             The last HTTP request sent by this client.
388              
389             =head2 last_response
390              
391             The last HTTP response received by this client.
392              
393             =head1 METHODS
394              
395             =head2 new
396              
397             Initializes a new Google::Ads::GoogleAds::Client object.
398              
399             =head3 Parameters
400              
401             The new() method takes parameters as a hash reference. The attributes of this
402             object can be populated in a number of ways:
403              
404             =over
405              
406             =item *
407              
408             If the L</properties_file> parameter is given, then properties are read from that
409             file and the corresponding attributes are populated.
410              
411             =item *
412              
413             If no L</properties_file> parameter is given, then the code checks to see if there
414             is a file named F<googleads.properties> in the home directory of the current user.
415             If there is, then properties are read from there.
416              
417             =item *
418              
419             Any of the L</ATTRIBUTES> can be passed in as keys in the parameters hash reference.
420             If any attribute is explicitly passed in then it will override the value for that
421             attribute that might be in the properties file.
422              
423             =back
424              
425             =head3 Returns
426              
427             A new Google::Ads::GoogleAds::Client object with the appropriate attributes set.
428              
429             =head3 Exceptions
430              
431             If a L</properties_file> is passed in but the file cannot be read, the code will
432             die() with an error message describing the failure.
433              
434             =head3 Example
435              
436             # Basic use case. Attributes will be read from ~/googleads.properties file.
437             my $api_client = Google::Ads::GoogleAds::Client->new();
438              
439             # Most attributes from a custom properties file, but override login_customer_id.
440             eval {
441             my $api_client = Google::Ads::GoogleAds::Client->new({
442             properties_file => "/path/to/googleads.properties",
443             login_customer_id => "1234567890"
444             });
445             };
446             if ($@) {
447             # The properties file couldn't be read; handle error as appropriate.
448             }
449              
450             # Specify all attributes explicitly. The properties file will not override.
451             my $api_client = Google::Ads::GoogleAds::Client->new({
452             developer_token => "123xyzabc...",
453             login_customer_id => "1234567890"
454             });
455              
456             $api_client->get_oauth2_applications_handler()->set_refresh_token('1/Abc...');
457              
458             =head2 set_die_on_faults
459              
460             This module supports two approaches for handling API faults (i.e. errors
461             returned by the underlying REST API service).
462              
463             One approach is to issue a die() with a description of the error when an API
464             fault occurs. This die() would ideally be contained within an eval { }; block,
465             thereby emulating try { } / catch { } exception functionality in other
466             languages.
467              
468             A different approach is to require developers to explicitly check for API
469             faults being returned after each Google Ads API request. This approach requires
470             a bit more work, but has the advantage of exposing the full details of the API
471             fault, like the fault code.
472              
473             Refer to the object L<Google::Ads::GoogleAds::GoogleAdsException> for more details
474             on how faults get returned.
475              
476             The default value is false, i.e. you must explicitly check for faults.
477              
478             =head3 Parameters
479              
480             A true value will cause this module to die() when an API fault occurs.
481              
482             A false value will suppress this die(). This is the default behavior.
483              
484             =head3 Returns
485              
486             The input parameter is returned.
487              
488             =head3 Example
489              
490             # $api_client is a Google::Ads::GoogleAds::Client.
491              
492             # Enable die()ing on faults.
493             $api_client->set_die_on_faults(1);
494             eval { my $response = $api_client->AdGroupAdService()->mutate($mutate_request); };
495             if ($@) {
496             # Do something with the error information in $@.
497             }
498              
499             # Default behavior.
500             $api_client->set_die_on_faults(0);
501             my $response = $api_client->AdGroupAdService()->mutate($mutate_request);
502             if ($response->isa("Google::Ads::GoogleAds::GoogleAdsException")) {
503             # Do something with this GoogleAdsException object.
504             }
505              
506             =head2 get_die_on_faults
507              
508             =head3 Returns
509              
510             A true or false value indicating whether the L<Google::Ads::GoogleAds::Client>
511             instance is set to die() on API faults.
512              
513             =head2 {ServiceName}
514              
515             The client object contains a method for each service provided by the Google Ads
516             API. For example it can be invoked as $api_client->AdGroupService() and it will
517             return an object of type L<Google::Ads::GoogleAds::V11::Services::AdGroupService>
518             when using version V11 of the API.
519              
520             For a list of all the available services please refer to
521             L<https://developers.google.com/google-ads/api/docs> and for code samples on
522             how to invoke the services please refer to scripts in the examples folder.
523              
524             =head2 get_oauth2_applications_handler
525              
526             Returns the OAuth2 authorization handler for Web/Desktop applications
527             attached to the client, for programmatically setting/overriding its properties.
528              
529             $api_client->get_oauth2_applications_handler()->set_client_id('client-id');
530             $api_client->get_oauth2_applications_handler()->set_client_secret('client-secret');
531             $api_client->get_oauth2_applications_handler()->set_access_token('access-token');
532             $api_client->get_oauth2_applications_handler()->set_refresh_token('refresh-token');
533             $api_client->get_oauth2_applications_handler()->set_access_type('access-type');
534             $api_client->get_oauth2_applications_handler()->set_prompt('prompt');
535             $api_client->get_oauth2_applications_handler()->set_redirect_uri('redirect-url');
536              
537             Refer to L<Google::Ads::GoogleAds::OAuth2ApplicationsHandler> for more details.
538              
539             =head2 get_oauth2_service_accounts_handler
540              
541             Returns the OAuth2 authorization handler for service accounts, for
542             programmatically setting/overriding its properties.
543              
544             $api_client->get_oauth2_service_accounts_handler()->set_json_key_file_path('json-key-file-path');
545             $api_client->get_oauth2_service_accounts_handler()->set_impersonated_email('impersonated-email');
546             $api_client->get_oauth2_service_accounts_handler()->set_access_token('access-token');
547              
548             Refer to L<Google::Ads::GoogleAds::OAuth2ServiceAccountsHandler> for more details.
549              
550             =head2 configure_from_environment_variables
551              
552             Loads the environment variables to configure this API client.
553              
554             =head2 __parse_properties_file (Private)
555              
556             =head3 Parameters
557              
558             The path to a properties file on disk. The data in the file should be in the
559             following format:
560              
561             key1=value1
562             key2=value2
563              
564             =head3 Returns
565              
566             A hash corresponding to the keys and values in the properties file.
567              
568             =head3 Exceptions
569              
570             Issues a die() with an error message if the properties file could not be read.
571              
572             =head2 _get_auth_handler (Protected)
573              
574             Retrieves the active auth handler. All handlers are checked in the order.
575              
576             =head3 Returns
577              
578             An implementation of L<Google::Ads::GoogleAds::Common::AuthHandlerInterface>.
579              
580             =head1 LICENSE AND COPYRIGHT
581              
582             Copyright 2019 Google LLC
583              
584             Licensed under the Apache License, Version 2.0 (the "License");
585             you may not use this file except in compliance with the License.
586             You may obtain a copy of the License at
587              
588             http://www.apache.org/licenses/LICENSE-2.0
589              
590             Unless required by applicable law or agreed to in writing, software
591             distributed under the License is distributed on an "AS IS" BASIS,
592             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
593             See the License for the specific language governing permissions and
594             limitations under the License.
595              
596             =head1 REPOSITORY INFORMATION
597              
598             $Rev: $
599             $LastChangedBy: $
600             $Id: $
601              
602             =cut