Patch from Dan Scott to move perl OpenSRF core bootstrapping settings into
authormiker <miker@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 29 Jun 2007 03:55:37 +0000 (03:55 +0000)
committermiker <miker@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 29 Jun 2007 03:55:37 +0000 (03:55 +0000)
opensrf_core.xml.  This removes the dependency on the INI style bootstrap.conf
file:

Building on Nathan Eady's suggestion / intention from December
(http://list.georgialibraries.org/pipermail/open-ils-dev/2006-December/000177.html),
here is a patch that enables OpenSRF to avoid duplicating settings in
opensrf_core.xml and bootstrap.conf by having both Perl and C apps
read from opensrf_core.xml

The major limitation is that I've hardcoded /config/opensrf to appear as
the 'bootstrap' section of config for compatibility with the expectations of
the rest of OpenSRF. A broader patch would convert all of those other calls
from config->bootstrap->blah to config->opensrf->blah and make the
siblings of /config/opensrf visible in the config as well, as a more
generally useful approach.

Applied with minor changes to avoid API regressions and remove unneeded code.

git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@982 9efc2488-bf62-4759-914b-345cdb29e865

bin/osrf_ctl.sh
examples/opensrf_core.xml.example
src/perlmods/OpenSRF/Utils/Config.pm
src/perlmods/OpenSRF/Utils/Logger.pm

index 531d6df..374385d 100755 (executable)
@@ -1,8 +1,7 @@
 #!/bin/bash
 
 OPT_ACTION=""
-OPT_PERL_CONFIG=""
-OPT_C_CONFIG=""
+OPT_CONFIG=""
 OPT_PID_DIR=""
 
 # ---------------------------------------------------------------------------
@@ -11,12 +10,9 @@ OPT_PID_DIR=""
 [ $(whoami) != 'opensrf' ] && echo 'Must run as user "opensrf"' && exit;
 
 
-# NOTE: Eventually, there will be one OpenSRF config file format
-# When this happens, we will only need a single OPT_CONFIG variable
-
 function usage {
        echo "";
-       echo "usage: $0 -d <pid_dir> -p <perl_config> -c <c_config> -a <action>";
+       echo "usage: $0 -d <pid_dir> -c <c_config> -a <action>";
        echo "";
        echo "Actions include:"
        echo -e "\tstart_router"
@@ -36,7 +32,7 @@ function usage {
        echo -e "\trestart_all"
        echo "";
     echo "Example:";
-    echo "  $0 -p bootstrap.conf -c opensrf_core.xml -a restart_all";
+    echo "  $0 -c opensrf_core.xml -a restart_all";
     echo "";
        exit;
 }
@@ -45,11 +41,10 @@ function usage {
 # ---------------------------------------------------------------------------
 # Load the command line options and set the global vars
 # ---------------------------------------------------------------------------
-while getopts  "p:c:a:d:h" flag; do
+while getopts  "c:a:d:h" flag; do
        case $flag in   
                "a")            OPT_ACTION="$OPTARG";;
-               "c")            OPT_C_CONFIG="$OPTARG";;
-               "p")            OPT_PERL_CONFIG="$OPTARG";;
+               "c")            OPT_CONFIG="$OPTARG";;
                "d")            OPT_PID_DIR="$OPTARG";;
                "h"|*)  usage;;
        esac;
@@ -110,7 +105,7 @@ function do_action {
 
 function start_router {
        do_action "start" $PID_ROUTER "OpenSRF Router";
-       opensrf_router $OPT_C_CONFIG router
+       opensrf_router $OPT_CONFIG router
        pid=$(ps ax | grep "OpenSRF Router" | grep -v grep | awk '{print $1}')
        echo $pid > $PID_ROUTER;
        return 0;
@@ -123,7 +118,7 @@ function stop_router {
 
 function start_perl {
        do_action "start" $PID_OSRF_PERL "OpenSRF Perl";
-       perl -MOpenSRF::System="$OPT_PERL_CONFIG" -e 'OpenSRF::System->bootstrap()' & 
+       perl -MOpenSRF::System="$OPT_CONFIG" -e 'OpenSRF::System->bootstrap()' & 
        pid=$!;
        echo $pid > $PID_OSRF_PERL;
        sleep 5;
@@ -143,7 +138,7 @@ function start_c {
        fi;
 
        do_action "start" $PID_OSRF_C "OpenSRF C (host=$host)";
-       opensrf-c $host $OPT_C_CONFIG opensrf;
+       opensrf-c $host $OPT_CONFIG opensrf;
        pid=$(ps ax | grep "OpenSRF System-C" | grep -v grep | awk '{print $1}')
        echo $pid > "$PID_OSRF_C";
        return 0;
index 98875ac..708d4a6 100644 (file)
@@ -2,7 +2,7 @@
 
 <config>
 
-       <opensrf> <!-- bootstrap config for the C apps -->
+  <opensrf> <!-- bootstrap config for OpenSRF apps -->
 
                <!--  the routers's name on the network -->
                <!-- do not change this -->
@@ -17,7 +17,7 @@
       </routers>
 
       <domains>
-                       <!-- Our jabber domain, currenlty only one domain is supported -->
+      <!-- Our jabber domain, currently only one domain is supported -->
          <domain>localhost</domain>
       </domains>
 
 
                <!-- 0 None, 1 Error, 2 Warning, 3 Info, 4 debug, 5 Internal (Nasty) -->
       <loglevel>3</loglevel>
-   </opensrf>
 
+    <!-- config file for the services -->
+    <settings_config>/openils/conf/opensrf.xml</settings_config>
+  </opensrf>
 
        <!-- Update this if you use ChopChop -->
        <chopchop> <!-- Our jabber server -->
@@ -58,7 +60,6 @@
                <logfile>/openils/var/log/osrfsys.log</logfile>
        </chopchop>
 
-
        <!-- The section between <gateway>...</gateway> is a standard OpenSRF C stack config file -->
        <gateway>
 
        <!-- ======================================================================================== -->
 
 </config>
-
-
-
-
index 427c28d..9b21221 100755 (executable)
@@ -26,58 +26,17 @@ sub new {
 
        $self = bless {}, $class;
 
-       my $lines = shift;
-
-       for my $line (@$lines) {
-
-               #($line) = split(/\s+\/\//, $line);
-               #($line) = split(/\s+#/, $line);
-
-               if ($line =~ /^\s*\[([^\[\]]+)\]/) {
-                       $self->_sub_builder('__id');
-                       $self->__id( $1 );
-                       next;
-               }
+       $self->_sub_builder('__id');
+       # Hard-code this to match old bootstrap.conf section name
+       $self->__id('bootstrap');
 
-               my ($protokey,$value,$keytype,$key);
-               if ($line =~ /^([^=\s]+)\s*=\s*(.*)\s*$/s) {
-                       ($protokey,$value) = ($1,$2);
-                       ($keytype,$key) = split(/:/,$protokey);
-               }
-
-               $key = $protokey unless ($key);
-
-               if ($keytype ne $key) {
-                       $keytype = lc $keytype;
-                       if ($keytype eq 'list') {
-                               $value = [split /\s*,\s*/, $value];
-                       } elsif ($keytype eq 'bool') {
-                               $value = do{ $value =~ /^t|y|1/i ? 1 : 0; };
-                       } elsif ($keytype eq 'interval') {
-                               $value = interval_to_seconds($value);
-                       } elsif ($keytype eq 'subsection') {
-                               if (exists $SECTIONCACHE{$value}) {
-                                       $value = $SECTIONCACHE{$value};
-                               } else {
-                                       $SUBSECTION_FIXUP{$value}{$self->SECTION} = $key ;
-                                       next;
-                               }
-                       }
-               }
+       my $bootstrap = shift;
 
+       foreach my $key (sort keys %$bootstrap) {
                $self->_sub_builder($key);
-               $self->$key($value);
-       }
-
-       no warnings;
-       if (my $parent_def = $SUBSECTION_FIXUP{$self->SECTION}) {
-               my ($parent_section, $parent_key) = each %$parent_def;
-               $SECTIONCACHE{$parent_section}->{$parent_key} = $self;
-               delete $SUBSECTION_FIXUP{$self->SECTION};
+               $self->$key($bootstrap->{$key});
        }
 
-       $SECTIONCACHE{$self->SECTION} = $self;
-
        return $self;
 }
 
@@ -87,6 +46,7 @@ use vars qw/@ISA $AUTOLOAD $VERSION $OpenSRF::Utils::ConfigCache/;
 push @ISA, qw/OpenSRF::Utils/;
 
 use FileHandle;
+use XML::LibXML;
 use OpenSRF::Utils (':common');  
 use OpenSRF::Utils::Logger;
 use Net::Domain qw/hostfqdn/;
@@ -157,9 +117,9 @@ OpenSRF::Utils::Config
 
   my $config_obj = OpenSRF::Utils::Config->load( config_file   => '/config/file.cnf' );
 
-  my $attrs_href = $config_obj->attributes();
+  my $attrs_href = $config_obj->bootstrap();
 
-  $config_obj->attributes->no_db(0);
+  $config_obj->bootstrap->loglevel(0);
 
   open FH, '>'.$config_obj->FILE() . '.new';
   print FH $config_obj;
@@ -170,61 +130,56 @@ OpenSRF::Utils::Config
 =head1 DESCRIPTION
 
  
-This module is mainly used by other modules to load a configuration file.
+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>/openils/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 => '/openils/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.
 
-Here be a config file example, HAR!:
-
- [datasource]
- # backend=XMLRPC
- backend=DBI
- subsection:definition=devel_db
-
- [devel_db]
- dsn=dbi:Pg(RaiseError => 0, AutoCommit => 1):dbname=dcl;host=nsite-dev
- user=postgres
- pw=postgres
- #readonly=1
- [live_db]
- dsn=dbi:Pg(RaiseError => 0, AutoCommit => 1):dbname=dcl
- user=n2dcl
- pw=dclserver
- #readonly=1
-
- [devel_xmlrpc]
- subsection:definition=devel_rpc
- [logs]
- base=/var/log/nsite
- debug=debug.log
- error=error.log
- [debug]
- enabled=1
- level=ALL
- [devel_rpc]
- url=https://localhost:9000/
- proto=SSL
- SSL_cipher_list=ALL
- SSL_verify_mode=5
- SSL_use_cert=1
- SSL_key_file=client-key.pem
- SSL_cert_file=client-cert.pem
- SSL_ca_file=cacert.pem
- log_level=4
- [dirs]
- base_dir=/home/miker/cvs/NOC/monitor_core/
- cert_dir=certs/
 
 =head1 METHODS
 
@@ -358,60 +313,56 @@ sub mangle_dirs {
 
 sub load_config {
        my $self = shift;
-       my $config = new FileHandle $self->FILE, 'r';
+       my $parser = XML::LibXML->new();
+
+       # Hash of config values
+       my %bootstrap;
+       
+       # Return an XML::LibXML::Document object
+       my $config = $parser->parse_file($self->FILE);
+
        unless ($config) {
                OpenSRF::Utils::Logger->error("Could not open ".$self->FILE.": $!\n");
                die "Could not open ".$self->FILE.": $!\n";
        }
-       my @stripped_config = $self->__strip_comments($config) if (defined $config);
-
-       my $chunk = [];
-       for my $line (@stripped_config) {
-               no warnings;
-               next unless ($line);
 
-               if ($line =~ /^\s*\[/ and @$chunk) {
-                       my $section = $self->section_pkg->new($chunk);
+       # Return an XML::LibXML::NodeList object matching all child elements
+       # of <config><opensrf>...
+       my $osrf_cfg = $config->findnodes('/config/opensrf/child::*');
 
-                       my $sub_name = $section->SECTION;
-                       $self->_sub_builder($sub_name);
-                       $self->$sub_name($section);
+       # Iterate through the nodes to pull out key=>value pairs of config settings
+       foreach my $node ($osrf_cfg->get_nodelist()) {
+               my $child_state = 0;
 
-                       #$self->{$section->SECTION} = $section;
+               # This will be overwritten if it's a scalar setting
+               $bootstrap{$node->nodeName()} = [];
 
-                       $chunk = [];
-                       push @$chunk,$line;
-                       next;
-               } 
-               if ($line =~ /^#\s*include\s+"(\S+)"\s*$/o) {
-                        my $included_file = $1;
-                       my $section = OpenSRF::Utils::Config->load(config_file => $included_file, nocache => 1);
+               foreach my $child_node ($node->childNodes) {
+                       # from libxml/tree.h: nodeType 1 = ELEMENT_NODE
+                       next if $child_node->nodeType() != 1;
 
-                       my $sub_name = $section->FILE;
-                       $self->_sub_builder($sub_name);
-                       $self->$sub_name($section);
-
-                       for my $subsect (keys %$section) {
-                               next if ($subsect eq '__id');
-
-                               $self->_sub_builder($subsect);
-                               $self->$subsect($$section{$subsect});
-
-                               #$self->$subsect($section->$subsect);
-                               $self->$subsect->{__sub} = 1;
-                       }
-                       next;
+                       # If the child node is an element, this element may
+                       # have multiple values; therefore, push it into an array
+                       push @{$bootstrap{$node->nodeName()}}, OpenSRF::Utils::Config::extract_text($child_node->textContent);
+                       $child_state = 1;
+               }
+               if (!$child_state) {
+                       $bootstrap{$node->nodeName()} = OpenSRF::Utils::Config::extract_text($node->textContent);
                }
-
-               push @$chunk,$line;
        }
-       my $section = $self->section_pkg->new($chunk) if (@$chunk);
+
+       my $section = $self->section_pkg->new(\%bootstrap);
        my $sub_name = $section->SECTION;
        $self->_sub_builder($sub_name);
        $self->$sub_name($section);
 
 }
 
+sub extract_text {
+       my $self = shift;
+       $self =~ s/^\s*([.*?])\s*$//m;
+       return $self;
+}
 
 #------------------------------------------------------------------------------------------------------------------------------------
 
@@ -419,13 +370,25 @@ sub load_config {
 
        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 know bugs, but report any to mrylander@gmail.com.
+No known bugs, but report any to open-ils-dev@list.georgialibraries.org or mrylander@gmail.com.
 
 =head1 COPYRIGHT AND LICENSING
 
-Mike Rylander, Copyright 2000-2007
+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.
index bbeff7a..138aa01 100644 (file)
@@ -39,7 +39,6 @@ my $syslog_enabled = 0;                       # is syslog enabled?
 my $act_syslog_enabled = 0;    # is syslog enabled?
 my $logfile_enabled = 1;               # are we logging to a file?
 my $act_logfile_enabled = 1;   # are we logging to a file?
-my $logdir;                                                    # log file directory
 
 our $logger = "OpenSRF::Utils::Logger";
 
@@ -66,16 +65,14 @@ sub set_config {
                warn "*** Logger found no config.  Using STDERR ***\n";
        }
 
-       $loglevel =  $config->bootstrap->debug
-       if($loglevel =~ /error/i){ $loglevel = ERROR(); }
-       elsif($loglevel =~ /warn/i){ $loglevel = WARN(); }
-       elsif($loglevel =~ /info/i){ $loglevel = INFO(); }
-       elsif($loglevel =~ /debug/i){ $loglevel = DEBUG(); }
-       elsif($loglevel =~ /internal/i){ $loglevel = INTERNAL(); }
+       $loglevel =  $config->bootstrap->loglevel
+       if($loglevel = 1){ $loglevel = ERROR(); }
+       elsif($loglevel = 2){ $loglevel = WARN(); }
+       elsif($loglevel = 3){ $loglevel = INFO(); }
+       elsif($loglevel = 4){ $loglevel = DEBUG(); }
+       elsif($loglevel = 5){ $loglevel = INTERNAL(); }
        else{$loglevel= INFO(); }
 
-       my $logdir = $config->bootstrap->log_dir;
-
        $logfile = $config->bootstrap->logfile;
        if($logfile =~ /^syslog/) {
                $syslog_enabled = 1;
@@ -86,7 +83,7 @@ sub set_config {
                $facility = _fac_to_const($facility);
                openlog($service, 0, $facility);
 
-       } else { $logfile = "$logdir/$logfile"; }
+       } else { $logfile = "$logfile"; }
 
        $actfile = $config->bootstrap->actlog;
        if($actfile =~ /^syslog/) {
@@ -97,7 +94,7 @@ sub set_config {
                $actfile = undef;
                $actfac = _fac_to_const($actfac);
 
-       } else { $actfile = "$logdir/$actfile"; }
+       } else { $actfile = "$actfile"; }
 
        $isclient = (OpenSRF::Utils::Config->current->bootstrap->client =~ /^true$/iog) ?  1 : 0;
 }