1 package OpenSRF::Utils::Config::Section;
5 use vars qw/@ISA $AUTOLOAD/;
6 push @ISA, qw/OpenSRF::Utils/;
8 use OpenSRF::Utils (':common');
9 use Net::Domain qw/hostfqdn/;
11 our $VERSION = "1.000";
16 #use overload '""' => \&OpenSRF::Utils::Config::dump_ini;
20 return $sec->__id(@_);
25 my $class = ref($self) || $self;
27 $self = bless {}, $class;
29 $self->_sub_builder('__id');
30 # Hard-code this to match old bootstrap.conf section name
31 # This hardcoded value is later overridden if the config is loaded
32 # with the 'base_path' option
33 $self->__id('bootstrap');
35 my $bootstrap = shift;
37 foreach my $key (sort keys %$bootstrap) {
38 $self->_sub_builder($key);
39 $self->$key($bootstrap->{$key});
45 package OpenSRF::Utils::Config;
47 use vars qw/@ISA $AUTOLOAD $VERSION $OpenSRF::Utils::ConfigCache/;
48 push @ISA, qw/OpenSRF::Utils/;
52 use OpenSRF::Utils (':common');
53 use OpenSRF::Utils::Logger;
54 use Net::Domain qw/hostfqdn/;
56 #use overload '""' => \&OpenSRF::Utils::Config::dump_ini;
60 my $config_file = shift;
62 return unless $config_file;
64 $class->load( config_file => $config_file);
72 if ($self->isa('OpenSRF::Utils::Config')) {
73 if (UNIVERSAL::isa(scalar(caller()), 'OpenSRF::Utils::Config' )) {
76 $string = "# Main File: " . $self->FILE . "\n\n" . $string;
79 for my $section ( ('__id', grep { $_ ne '__id' } sort keys %$self) ) {
80 next if ($section eq 'env' && $self->isa('OpenSRF::Utils::Config'));
81 if ($section eq '__id') {
82 $string .= '['.$self->SECTION."]\n" if ($self->isa('OpenSRF::Utils::Config::Section'));
83 } elsif (ref($self->$section)) {
84 if (ref($self->$section) =~ /ARRAY/o) {
85 $string .= "list:$section = ". join(', ', @{$self->$section}) . "\n";
86 } elsif (UNIVERSAL::isa($self->$section,'OpenSRF::Utils::Config::Section')) {
87 if ($self->isa('OpenSRF::Utils::Config::Section')) {
88 $string .= "subsection:$section = " . $self->$section->SECTION . "\n";
91 next if ($self->$section->{__sub} && !$included);
92 $string .= $self->$section . "\n";
94 } elsif (UNIVERSAL::isa($self->$section,'OpenSRF::Utils::Config')) {
95 $string .= $self->$section . "\n";
98 next if $section eq '__sub';
99 $string .= "$section = " . $self->$section . "\n";
103 $string =~ s/^/## /gm;
104 $string = "# Subfile: " . $self->FILE . "\n#" . '-'x79 . "\n".'#include "'.$self->FILE."\"\n". $string;
112 OpenSRF::Utils::Config
117 use OpenSRF::Utils::Config;
119 my $config_obj = OpenSRF::Utils::Config->load( config_file => '/config/file.cnf' );
121 my $attrs_href = $config_obj->bootstrap();
123 $config_obj->bootstrap->loglevel(0);
125 open FH, '>'.$config_obj->FILE() . '.new';
126 print FH $config_obj;
131 This module is mainly used by other OpenSRF modules to load an OpenSRF
132 configuration file. OpenSRF configuration files are XML files that
133 contain a C<< <config> >> root element and an C<< <opensrf> >> child
134 element (in XPath notation, C</config/opensrf/>). Each child element
135 is converted into a hash key=>value pair. Elements that contain other
136 XML elements are pushed into arrays and added as an array reference to
137 the hash. Scalar values have whitespace trimmed from the left and
142 Given an OpenSRF configuration file named F<opensrf_core.xml> with the
145 <?xml version='1.0'?>
148 <router_name>router</router_name>
151 <router>localhost</router>
152 <router>otherhost</router>
155 <logfile>/var/log/osrfsys.log</logfile>
159 ... calling C<< OpenSRF::Utils::Config->load(config_file =>
160 'opensrf_core.xml') >> will create a hash with the following
164 router_name => 'router',
165 routers => ['localhost', 'otherhost'],
166 logfile => '/var/log/osrfsys.log'
169 You can retrieve any of these values by name from the bootstrap
170 section of C<$config_obj>; for example:
172 $config_obj->bootstrap->router_name
176 For compatibility with previous versions of the OpenSRF configuration
177 files, the C<load()> method by default loads the C</config/opensrf>
178 section with the hardcoded name of B<bootstrap>.
180 However, it is possible to load child elements of C<< <config> >> other
181 than C<< <opensrf> >> by supplying a C<base_path> argument which specifies
182 the node you wish to begin loading from (in XPath notation). Doing so
183 will also replace the hardcoded C<bootstrap> name with the node name of
184 the last member of the given path. For example:
186 my $config_obj = OpenSRF::Utils::Config->load(
187 config_file => '/config/file.cnf'
188 base_path => '/config/shared'
191 my $attrs_href = $config_obj->shared();
193 While it may be possible to load the entire file in this fashion (by
194 specifying an empty C<base_path>), doing so will break compatibility with
195 existing code which expects to find a C<bootstrap> member. Future
196 iterations of this module may extend its ability to parse the entire
197 OpenSRF configuration file in one pass while providing multiple base
198 sections named after the sibling elements of C</config/opensrf>.
200 Hashrefs of sections can be returned by calling a method of the object
201 of the same name as the section. They can be set by passing a hashref
202 back to the same method. Sections will B<NOT> be autovivicated,
210 =head2 OpenSRF::Utils::Config->load( config_file => '/some/config/file.cnf' )
212 Returns a OpenSRF::Utils::Config object representing the config file
213 that was loaded. The most recently loaded config file (hopefully the
214 only one per app) is stored at $OpenSRF::Utils::ConfigCache. Use
215 OpenSRF::Utils::Config::current() to get at it.
221 $pkg = ref($pkg) || $pkg;
225 (my $new_pkg = $args{config_file}) =~ s/\W+/_/g;
226 $new_pkg .= "::$pkg";
227 $new_section_pkg .= "${new_pkg}::Section";
233 sub section_pkg { return '$new_section_pkg'; }
235 package $new_section_pkg;
236 use base "${pkg}::Section";
241 return $new_pkg->_load( %args );
246 $pkg = ref($pkg) || $pkg;
251 if ((exists $$self{config_file} and OpenSRF::Utils::Config->current) and (OpenSRF::Utils::Config->current->FILE eq $$self{config_file}) and (!$self->{force})) {
252 delete $$self{force};
253 return OpenSRF::Utils::Config->current();
256 $self->_sub_builder('__id');
257 $self->FILE($$self{config_file});
258 delete $$self{config_file};
259 return undef unless ($self->FILE);
262 if (exists $$self{base_path}) { # blank != non-existent for this setting
263 $load_args{base_path} = $$self{base_path};
265 $self->load_config(%load_args);
267 $self->mangle_dirs();
268 $self->mangle_logs();
270 $OpenSRF::Utils::ConfigCache = $self unless $self->nocache;
271 delete $$self{nocache};
272 delete $$self{force};
273 delete $$self{base_path};
281 my @parts = (grep { UNIVERSAL::isa($_,'OpenSRF::Utils::Config::Section') } values %$self);
283 my $must_match = scalar(keys %filters);
285 foreach my $part (@parts) {
287 for my $fkey (keys %filters) {
288 $part_count++ if ($part->$key eq $filters{$key});
290 push @ok_parts, $part if ($part_count == $must_match);
298 return $OpenSRF::Utils::ConfigCache;
302 return shift()->__id(@_);
307 my $host = $ENV{'OSRF_HOSTNAME'} || hostfqdn();
309 $$self{env} = $self->section_pkg->new;
310 $$self{env}{hostname} = $host;
315 return unless ($self->logs && $self->dirs && $self->dirs->log_dir);
316 for my $i ( keys %{$self->logs} ) {
317 next if ($self->logs->$i =~ /^\//);
318 $self->logs->$i($self->dirs->log_dir."/".$self->logs->$i);
324 return unless ($self->dirs && $self->dirs->base_dir);
325 for my $i ( keys %{$self->dirs} ) {
326 if ( $i ne 'base_dir' ) {
327 next if ($self->dirs->$i =~ /^\//);
328 my $dir_tmp = $self->dirs->base_dir."/".$self->dirs->$i;
329 $dir_tmp =~ s#//#/#go;
330 $dir_tmp =~ s#/$##go;
331 $self->dirs->$i($dir_tmp);
338 my $parser = XML::LibXML->new();
341 # Hash of config values
344 # Return an XML::LibXML::Document object
345 my $config = $parser->parse_file($self->FILE);
348 OpenSRF::Utils::Logger->error("Could not open ".$self->FILE.": $!\n");
349 die "Could not open ".$self->FILE.": $!\n";
352 # For backwards compatibility, we default to /config/opensrf
354 if (exists $args{base_path}) { # allow for empty to import entire file
355 $base_path = $args{base_path};
357 $base_path = '/config/opensrf';
359 # Return an XML::LibXML::NodeList object matching all child elements
361 my $osrf_cfg = $config->findnodes("$base_path/child::*");
363 # Iterate through the nodes to pull out key=>value pairs of config settings
364 foreach my $node ($osrf_cfg->get_nodelist()) {
367 # This will be overwritten if it's a scalar setting
368 $bootstrap{$node->nodeName()} = [];
370 foreach my $child_node ($node->childNodes) {
371 # from libxml/tree.h: nodeType 1 = ELEMENT_NODE
372 next if $child_node->nodeType() != 1;
374 # If the child node is an element, this element may
375 # have multiple values; therefore, push it into an array
376 my $content = OpenSRF::Utils::Config::extract_child($child_node);
377 push(@{$bootstrap{$node->nodeName()}}, $content) if $content;
381 $bootstrap{$node->nodeName()} = OpenSRF::Utils::Config::extract_text($node->textContent);
385 my $section = $self->section_pkg->new(\%bootstrap);
386 # if the Config was loaded with a 'base_path' option, overwrite the
387 # hardcoded 'bootstrap' name with something more reasonable
388 if (exists $$self{base_path}) { # blank != non-existent for this setting
389 # name root node to reflect last member of base_path, or default to root
390 my $root = (split('/', $$self{base_path}))[-1] || 'root';
391 $section->__id($root);
393 my $sub_name = $section->SECTION;
394 $self->_sub_builder($sub_name);
395 $self->$sub_name($section);
400 use OpenSRF::Utils::SettingsParser;
401 return OpenSRF::Utils::SettingsParser::XML2perl($node);
406 $self =~ s/^\s*([.*?])\s*$//m;
410 #------------------------------------------------------------------------------------------------------------------------------------
418 Elements containing heterogeneous child elements are treated as though they have the same element name;
421 <router>localhost</router>
422 <furniture>chair</furniture>
425 ... will simply generate a key=>value pair of C<< routers => ['localhost', 'chair'] >>.
429 No known bugs, but report any to open-ils-dev@list.georgialibraries.org or mrylander@gmail.com.
431 =head1 COPYRIGHT AND LICENSING
433 Copyright (C) 2000-2007, Mike Rylander
434 Copyright (C) 2007, Laurentian University, Dan Scott <dscott@laurentian.ca>
436 The OpenSRF::Utils::Config module is free software. You may distribute under the terms
437 of the GNU General Public License version 2 or greater.