File Coverage

File:blib/lib/OpenSRF/Utils/Config.pm
Coverage:19.2%

linestmtbrancondsubpodtimecode
1package OpenSRF::Utils::Config::Section;
2
3
13
13
13
70
57
82
no strict 'refs';
4
5
13
13
13
83
53
95
use vars qw/@ISA $AUTOLOAD/;
6push @ISA, qw/OpenSRF::Utils/;
7
8
13
13
13
144
58
139
use OpenSRF::Utils (':common');
9
13
13
13
186
55
162
use Net::Domain qw/hostfqdn/;
10
11our $VERSION = "1.000";
12
13my %SECTIONCACHE;
14my %SUBSECTION_FIXUP;
15
16#use overload '""' => \&OpenSRF::Utils::Config::dump_ini;
17
18sub SECTION {
19
0
0
        my $sec = shift;
20
0
0
        return $sec->__id(@_);
21}
22
23sub new {
24
0
0
        my $self = shift;
25
0
0
        my $class = ref($self) || $self;
26
27
0
0
        $self = bless {}, $class;
28
29
0
0
        $self->_sub_builder('__id');
30        # Hard-code this to match old bootstrap.conf section name
31
0
0
        $self->__id('bootstrap');
32
33
0
0
        my $bootstrap = shift;
34
35
0
0
        foreach my $key (sort keys %$bootstrap) {
36
0
0
                $self->_sub_builder($key);
37
0
0
                $self->$key($bootstrap->{$key});
38        }
39
40
0
0
        return $self;
41}
42
43package OpenSRF::Utils::Config;
44
45
13
13
13
99
50
126
use vars qw/@ISA $AUTOLOAD $VERSION $OpenSRF::Utils::ConfigCache/;
46push @ISA, qw/OpenSRF::Utils/;
47
48
13
13
13
92
49
154
use FileHandle;
49
13
13
13
197
67
90
use XML::LibXML;
50
13
13
13
95
50
148
use OpenSRF::Utils (':common');
51
13
13
13
112
145
161
use OpenSRF::Utils::Logger;
52
13
13
13
95
50
99
use Net::Domain qw/hostfqdn/;
53
54#use overload '""' => \&OpenSRF::Utils::Config::dump_ini;
55
56sub import {
57
79
418
        my $class = shift;
58
79
325
        my $config_file = shift;
59
60
79
410
        return unless $config_file;
61
62
0
        $class->load( config_file => $config_file);
63}
64
65sub dump_ini {
66
13
13
13
95
47
110
        no warnings;
67
0
        my $self = shift;
68
0
        my $string;
69
0
        my $included = 0;
70
0
        if ($self->isa('OpenSRF::Utils::Config')) {
71
0
                if (UNIVERSAL::isa(scalar(caller()), 'OpenSRF::Utils::Config' )) {
72
0
                        $included = 1;
73                } else {
74
0
                        $string = "# Main File: " . $self->FILE . "\n\n" . $string;
75                }
76        }
77
0
0
        for my $section ( ('__id', grep { $_ ne '__id' } sort keys %$self) ) {
78
0
                next if ($section eq 'env' && $self->isa('OpenSRF::Utils::Config'));
79
0
                if ($section eq '__id') {
80
0
                        $string .= '['.$self->SECTION."]\n" if ($self->isa('OpenSRF::Utils::Config::Section'));
81                } elsif (ref($self->$section)) {
82
0
                        if (ref($self->$section) =~ /ARRAY/o) {
83
0
0
                                $string .= "list:$section = ". join(', ', @{$self->$section}) . "\n";
84                        } elsif (UNIVERSAL::isa($self->$section,'OpenSRF::Utils::Config::Section')) {
85
0
                                if ($self->isa('OpenSRF::Utils::Config::Section')) {
86
0
                                        $string .= "subsection:$section = " . $self->$section->SECTION . "\n";
87
0
                                        next;
88                                } else {
89
0
                                        next if ($self->$section->{__sub} && !$included);
90
0
                                        $string .= $self->$section . "\n";
91                                }
92                        } elsif (UNIVERSAL::isa($self->$section,'OpenSRF::Utils::Config')) {
93
0
                                $string .= $self->$section . "\n";
94                        }
95                } else {
96
0
                        next if $section eq '__sub';
97
0
                        $string .= "$section = " . $self->$section . "\n";
98                }
99        }
100
0
        if ($included) {
101
0
                $string =~ s/^/## /gm;
102
0
                $string = "# Subfile: " . $self->FILE . "\n#" . '-'x79 . "\n".'#include "'.$self->FILE."\"\n". $string;
103        }
104
105
0
        return $string;
106}
107
108 - 201
=head1 NAME
 
OpenSRF::Utils::Config
 

=head1 SYNOPSIS

  use OpenSRF::Utils::Config;

  my $config_obj = OpenSRF::Utils::Config->load( config_file   => '/config/file.cnf' );

  my $attrs_href = $config_obj->bootstrap();

  $config_obj->bootstrap->loglevel(0);

  open FH, '>'.$config_obj->FILE() . '.new';
  print FH $config_obj;
  close FH;

=head1 DESCRIPTION

This module is mainly used by other OpenSRF modules to load an OpenSRF
configuration file.  OpenSRF configuration files are XML files that
contain a C<< <config> >> root element and an C<< <opensrf> >> child
element (in XPath notation, C</config/opensrf/>). Each child element
is converted into a hash key=>value pair. Elements that contain other
XML elements are pushed into arrays and added as an array reference to
the hash. Scalar values have whitespace trimmed from the left and
right sides.

Child elements of C<< <config> >> other than C<< <opensrf> >> are
currently ignored by this module.

=head1 EXAMPLE

Given an OpenSRF configuration file named F<opensrf_core.xml> with the
following content:

  <?xml version='1.0'?>
  <config>
    <opensrf>
      <router_name>router</router_name>

      <routers> 
	<router>localhost</router>
	<router>otherhost</router>
      </routers>

      <logfile>/var/log/osrfsys.log</logfile>
    </opensrf>
  </config>

... calling C<< OpenSRF::Utils::Config->load(config_file =>
'opensrf_core.xml') >> will create a hash with the following
structure:

  {
    router_name => 'router',
    routers => ['localhost', 'otherhost'],
    logfile => '/var/log/osrfsys.log'
  }

You can retrieve any of these values by name from the bootstrap
section of C<$config_obj>; for example:

  $config_obj->bootstrap->router_name

=head1 NOTES

For compatibility with a previous version of OpenSRF configuration
files, the F</config/opensrf/> section has a hardcoded name of
B<bootstrap>. However, future iterations of this module may extend the
ability of the module to parse the entire OpenSRF configuration file
and provide sections named after the sibling elements of
C</config/opensrf>.

Hashrefs of sections can be returned by calling a method of the object
of the same name as the section.  They can be set by passing a hashref
back to the same method.  Sections will B<NOT> be autovivicated,
though.


=head1 METHODS



=head2 OpenSRF::Utils::Config->load( config_file => '/some/config/file.cnf' )

Returns a OpenSRF::Utils::Config object representing the config file
that was loaded.  The most recently loaded config file (hopefully the
only one per app) is stored at $OpenSRF::Utils::ConfigCache. Use
OpenSRF::Utils::Config::current() to get at it.

=cut
202
203sub load {
204
0
        my $pkg = shift;
205
0
        $pkg = ref($pkg) || $pkg;
206
207
0
        my %args = @_;
208
209
0
        (my $new_pkg = $args{config_file}) =~ s/\W+/_/g;
210
0
        $new_pkg .= "::$pkg";
211
0
        $new_section_pkg .= "${new_pkg}::Section";
212
213
0
0
        { eval <<" PERL";
214
215                        package $new_pkg;
216                        use base $pkg;
217                        sub section_pkg { return '$new_section_pkg'; }
218
219                        package $new_section_pkg;
220                        use base "${pkg}::Section";
221
222                PERL
223        }
224
225
0
        return $new_pkg->_load( %args );
226}
227
228sub _load {
229
0
        my $pkg = shift;
230
0
        $pkg = ref($pkg) || $pkg;
231
0
        my $self = {@_};
232
0
        bless $self, $pkg;
233
234
13
13
13
110
57
71
        no warnings;
235
0
        if ((exists $$self{config_file} and OpenSRF::Utils::Config->current) and (OpenSRF::Utils::Config->current->FILE eq $$self{config_file}) and (!$self->{force})) {
236
0
                delete $$self{force};
237
0
                return OpenSRF::Utils::Config->current();
238        }
239
240
0
        $self->_sub_builder('__id');
241
0
        $self->FILE($$self{config_file});
242
0
        delete $$self{config_file};
243
0
        return undef unless ($self->FILE);
244
245
0
        $self->load_config();
246
0
        $self->load_env();
247
0
        $self->mangle_dirs();
248
0
        $self->mangle_logs();
249
250
0
        $OpenSRF::Utils::ConfigCache = $self unless $self->nocache;
251
0
        delete $$self{nocache};
252
0
        delete $$self{force};
253
0
        return $self;
254}
255
256sub sections {
257
0
        my $self = shift;
258
0
        my %filters = @_;
259
260
0
0
        my @parts = (grep { UNIVERSAL::isa($_,'OpenSRF::Utils::Config::Section') } values %$self);
261
0
        if (keys %filters) {
262
0
                my $must_match = scalar(keys %filters);
263
0
                my @ok_parts;
264
0
                foreach my $part (@parts) {
265
0
                        my $part_count = 0;
266
0
                        for my $fkey (keys %filters) {
267
0
                                $part_count++ if ($part->$key eq $filters{$key});
268                        }
269
0
                        push @ok_parts, $part if ($part_count == $must_match);
270                }
271
0
                return @ok_parts;
272        }
273
0
        return @parts;
274}
275
276sub current {
277
0
        return $OpenSRF::Utils::ConfigCache;
278}
279
280sub FILE {
281
0
        return shift()->__id(@_);
282}
283
284sub load_env {
285
0
        my $self = shift;
286
0
        my $host = $ENV{'OSRF_HOSTNAME'} || hostfqdn();
287
0
        chomp $host;
288
0
        $$self{env} = $self->section_pkg->new;
289
0
        $$self{env}{hostname} = $host;
290}
291
292sub mangle_logs {
293
0
        my $self = shift;
294
0
        return unless ($self->logs && $self->dirs && $self->dirs->log_dir);
295
0
0
        for my $i ( keys %{$self->logs} ) {
296
0
                next if ($self->logs->$i =~ /^\//);
297
0
                $self->logs->$i($self->dirs->log_dir."/".$self->logs->$i);
298        }
299}
300
301sub mangle_dirs {
302
0
        my $self = shift;
303
0
        return unless ($self->dirs && $self->dirs->base_dir);
304
0
0
        for my $i ( keys %{$self->dirs} ) {
305
0
                if ( $i ne 'base_dir' ) {
306
0
                        next if ($self->dirs->$i =~ /^\//);
307
0
                        my $dir_tmp = $self->dirs->base_dir."/".$self->dirs->$i;
308
0
                        $dir_tmp =~ s#//#/#go;
309
0
                        $dir_tmp =~ s#/$##go;
310
0
                        $self->dirs->$i($dir_tmp);
311                }
312        }
313}
314
315sub load_config {
316
0
        my $self = shift;
317
0
        my $parser = XML::LibXML->new();
318
319        # Hash of config values
320
0
        my %bootstrap;
321
322        # Return an XML::LibXML::Document object
323
0
        my $config = $parser->parse_file($self->FILE);
324
325
0
        unless ($config) {
326
0
                OpenSRF::Utils::Logger->error("Could not open ".$self->FILE.": $!\n");
327
0
                die "Could not open ".$self->FILE.": $!\n";
328        }
329
330        # Return an XML::LibXML::NodeList object matching all child elements
331        # of <config><opensrf>...
332
0
        my $osrf_cfg = $config->findnodes('/config/opensrf/child::*');
333
334        # Iterate through the nodes to pull out key=>value pairs of config settings
335
0
        foreach my $node ($osrf_cfg->get_nodelist()) {
336
0
                my $child_state = 0;
337
338                # This will be overwritten if it's a scalar setting
339
0
                $bootstrap{$node->nodeName()} = [];
340
341
0
                foreach my $child_node ($node->childNodes) {
342                        # from libxml/tree.h: nodeType 1 = ELEMENT_NODE
343
0
                        next if $child_node->nodeType() != 1;
344
345                        # If the child node is an element, this element may
346                        # have multiple values; therefore, push it into an array
347
0
            my $content = OpenSRF::Utils::Config::extract_child($child_node);
348
0
0
                        push(@{$bootstrap{$node->nodeName()}}, $content) if $content;
349
0
                        $child_state = 1;
350                }
351
0
                if (!$child_state) {
352
0
                        $bootstrap{$node->nodeName()} = OpenSRF::Utils::Config::extract_text($node->textContent);
353                }
354        }
355
356
0
        my $section = $self->section_pkg->new(\%bootstrap);
357
0
        my $sub_name = $section->SECTION;
358
0
        $self->_sub_builder($sub_name);
359
0
        $self->$sub_name($section);
360
361}
362sub extract_child {
363
0
    my $node = shift;
364
13
13
13
177
57
203
    use OpenSRF::Utils::SettingsParser;
365
0
    return OpenSRF::Utils::SettingsParser::XML2perl($node);
366}
367
368sub extract_text {
369
0
        my $self = shift;
370
0
        $self =~ s/^\s*([.*?])\s*$//m;
371
0
        return $self;
372}
373
374#------------------------------------------------------------------------------------------------------------------------------------
375
376 - 403
=head1 SEE ALSO

	OpenSRF::Utils

=head1 LIMITATIONS

Elements containing heterogeneous child elements are treated as though they have the same element name;
for example:
  <routers>
    <router>localhost</router>
    <furniture>chair</furniture>
  </routers>

... will simply generate a key=>value pair of C<< routers => ['localhost', 'chair'] >>.

=head1 BUGS

No known bugs, but report any to open-ils-dev@list.georgialibraries.org or mrylander@gmail.com.

=head1 COPYRIGHT AND LICENSING

Copyright (C) 2000-2007, Mike Rylander
Copyright (C) 2007, Laurentian University, Dan Scott <dscott@laurentian.ca>

The OpenSRF::Utils::Config module is free software. You may distribute under the terms
of the GNU General Public License version 2 or greater.

=cut
404
405
4061;