Coverage Report - org.kuali.rice.core.impl.config.property.ConfigParserImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ConfigParserImpl
0%
0/107
0%
0/42
5.25
ConfigParserImpl$1
N/A
N/A
5.25
ConfigParserImpl$SystemPropertiesDelegatingStrLookup
0%
0/11
0%
0/4
5.25
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.core.impl.config.property;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.apache.commons.lang.text.StrLookup;
 20  
 import org.apache.commons.lang.text.StrSubstitutor;
 21  
 import org.apache.log4j.Logger;
 22  
 import org.kuali.rice.core.api.util.RiceUtilities;
 23  
 import org.kuali.rice.core.api.util.xml.XmlException;
 24  
 import org.kuali.rice.core.api.util.xml.XmlJotter;
 25  
 import org.w3c.dom.Document;
 26  
 import org.w3c.dom.Element;
 27  
 import org.w3c.dom.Node;
 28  
 import org.w3c.dom.NodeList;
 29  
 import org.xml.sax.SAXException;
 30  
 
 31  
 import javax.xml.parsers.DocumentBuilderFactory;
 32  
 import javax.xml.parsers.ParserConfigurationException;
 33  
 import java.io.IOException;
 34  
 import java.io.InputStream;
 35  
 import java.util.LinkedHashMap;
 36  
 import java.util.Map;
 37  
 import java.util.Random;
 38  
 
 39  
 /**
 40  
  * ConfigParser implementation that supports a hierarchy of configs, in which 
 41  
  * configs can include other configs.  Variable tokens are resolved at parse time,
 42  
  * in the order in which they are encountered.  This class relies on Spring for resource
 43  
  * loading and Apache Commons Lang for variable replacement.
 44  
  *
 45  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 46  
  * @deprecated This is an old hand-rolled Rice configuration parser which has since been replaced by the superior JAXBConfigImpl.
 47  
  *             This old implementation only exists to support PluginConfig which supports ancient KEW plugin configs
 48  
  */
 49  
 @Deprecated
 50  0
 public class ConfigParserImpl implements ConfigParser {
 51  
     // keep the same random
 52  0
     private static final Random RANDOM = new Random();
 53  
 
 54  0
     private static final Logger LOG = Logger.getLogger(ConfigParserImpl.class);
 55  
     private static final String IMPORT_NAME = "config.location";
 56  
     private static final String PARAM_NAME= "param";
 57  
     private static final String NAME_ATTR = "name";
 58  
     private static final String OVERRIDE_ATTR = "override";
 59  
     private static final String RANDOM_ATTR = "random";
 60  
     private static final String INDENT = "  ";
 61  
     
 62  
     public static final String ALTERNATE_BUILD_LOCATION_KEY = "alt.build.location";
 63  
 
 64  
     /**
 65  
      * A StrLookup implementation that delegates to System properties if the key is not
 66  
      * found in the supplied map.
 67  
      * @author Kuali Rice Team (rice.collab@kuali.org)
 68  
      */
 69  0
     private static class SystemPropertiesDelegatingStrLookup extends StrLookup {
 70  
         private final Map map;
 71  0
         private SystemPropertiesDelegatingStrLookup(Map map) {
 72  0
             this.map = map;
 73  0
         }
 74  
         @Override
 75  
         public String lookup(String key) {
 76  0
             Object o = map.get(key);
 77  0
             if (o != null) {
 78  0
                 return String.valueOf(o);
 79  
             } else {
 80  0
                 String s = System.getProperty(key);
 81  0
                 if (s != null) {
 82  0
                     return s;
 83  
                 } else {
 84  
                     // implement behavior for missing property here...e.g. return ""
 85  
                     // returning null will result in the substitutor not substituting
 86  0
                     return "";
 87  
                 }
 88  
             }
 89  
         }
 90  
     }
 91  
 
 92  
     /**
 93  
      * @see org.kuali.rice.core.api.config.ConfigParser#parse(java.lang.String[])
 94  
      */
 95  
     public void parse(Map props, String[] locations) throws IOException {
 96  0
         LinkedHashMap params = new LinkedHashMap();
 97  0
         params.putAll(props);
 98  0
         parse(params, locations);
 99  0
         props.putAll(params);
 100  0
     }
 101  
 
 102  
     /**
 103  
      * Parses a list of locations
 104  
      * @param params the current parameter map
 105  
      * @param locations a list of locations to parse
 106  
      * @throws IOException
 107  
      */
 108  
     protected void parse(LinkedHashMap<String, Object> params, String[] locations) throws IOException {
 109  0
         StrSubstitutor subs = new StrSubstitutor(new SystemPropertiesDelegatingStrLookup(params));
 110  0
         for (String location: locations) {
 111  0
             parse(params, location, subs, 0);
 112  
         }
 113  0
     }
 114  
 
 115  
     /**
 116  
      * Parses a single config location
 117  
      * @param params the current parameter map
 118  
      * @param location the location to parse
 119  
      * @param subs a StrSubstitutor used to substitute variable tokens
 120  
      * @throws IOException
 121  
      */
 122  
     protected void parse(LinkedHashMap<String, Object> params, String location, StrSubstitutor subs, int depth) throws IOException {
 123  0
         InputStream configStream = RiceUtilities.getResourceAsStream(location);
 124  0
         if (configStream == null) {
 125  0
             LOG.warn("###############################");
 126  0
             LOG.warn("#");
 127  0
             LOG.warn("# Configuration file '" + location + "' not found!");
 128  0
             LOG.warn("#");
 129  0
             LOG.warn("###############################");
 130  0
             return;
 131  
         }
 132  
         
 133  0
         final String prefix = StringUtils.repeat(INDENT, depth); 
 134  0
         LOG.info(prefix + "+ Parsing config: " + location);
 135  
 
 136  
         Document doc;
 137  
         try {
 138  0
             doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(configStream);
 139  0
             if (LOG.isDebugEnabled()) {
 140  0
                 LOG.debug("Contents of config " + location + ": \n" + XmlJotter.jotNode(doc, true));
 141  
             }
 142  0
         } catch (SAXException se) {
 143  0
             IOException ioe = new IOException("Error parsing config resource: " + location);
 144  0
             ioe.initCause(se);
 145  0
             throw ioe;
 146  0
         } catch (ParserConfigurationException pce) {
 147  0
             IOException ioe = new IOException("Unable to obtain document builder");
 148  0
             ioe.initCause(pce);
 149  0
             throw ioe;
 150  
         } finally {
 151  0
             configStream.close();
 152  0
         }
 153  
 
 154  0
         Element root = doc.getDocumentElement();
 155  
         // ignore the actual type of the document element for now
 156  
         // so that plugin descriptors can be parsed
 157  0
         NodeList list = root.getChildNodes();
 158  0
         StringBuilder content = new StringBuilder();
 159  0
         for (int i = 0; i < list.getLength(); i++) {
 160  0
             Node node = list.item(i);
 161  0
             if (node.getNodeType() != Node.ELEMENT_NODE)
 162  0
                 continue;
 163  0
             if (!PARAM_NAME.equals(node.getNodeName())) {
 164  0
                 LOG.warn("Encountered non-param config node: " + node.getNodeName());
 165  0
                 continue;
 166  
             }
 167  0
             Element param = (Element) node;
 168  0
             String name = param.getAttribute(NAME_ATTR);
 169  0
             if (name == null) {
 170  0
                 LOG.error("Unnamed parameter in config resource '" + location + "': " + XmlJotter.jotNode(param));
 171  0
                 continue;
 172  
             }
 173  0
             Boolean override = Boolean.TRUE;
 174  0
             String overrideVal = param.getAttribute(OVERRIDE_ATTR);
 175  0
             if (!StringUtils.isEmpty(overrideVal)) {
 176  0
                 override = Boolean.valueOf(overrideVal);
 177  
             }
 178  
 
 179  0
             content.setLength(0);
 180  
             // accumulate all content (preserving any XML content)
 181  0
             getNodeValue(name, location, param, content);
 182  0
             String value = subs.replace(content);
 183  0
             if (LOG.isDebugEnabled()) {
 184  0
                 LOG.debug(prefix + INDENT + "* " + name + "=[" + ConfigLogger.getDisplaySafeValue(name, value) + "]");
 185  
             }
 186  
 
 187  0
             if (IMPORT_NAME.equals(name)) {
 188  
                 // what is this...we don't follow a string with this substring in it? i.e. if the value does not
 189  
                 // resolve then don't try to follow it (it won't find it anyway; this is the case for any path
 190  
                 // with unresolved params...)?
 191  0
                 if (!value.contains(ALTERNATE_BUILD_LOCATION_KEY)) {
 192  0
                     parse(params, value, subs, depth + 1);
 193  
                 }
 194  
             } else {
 195  0
                 if (Boolean.valueOf(param.getAttribute(RANDOM_ATTR))) {
 196  
                     // this is a special type of property whose value is a randomly generated number in the range specified
 197  0
                     value = String.valueOf(generateRandomInteger(value));   
 198  
                 }
 199  0
                 setParam(params, override, name, value, prefix + INDENT);
 200  
             }
 201  
         }
 202  0
         LOG.info(prefix + "- Parsed config: " + location);
 203  0
     }
 204  
     
 205  
     /**
 206  
      * Generates a random integer in the range specified by the specifier, in the format: min-max
 207  
      * @param rangeSpec a range specification, 'min-max'
 208  
      * @return a random integer in the range specified by the specifier, in the format: min-max
 209  
      */
 210  
     protected int generateRandomInteger(String rangeSpec) {
 211  0
         String[] range = rangeSpec.split("-");
 212  0
         if (range.length != 2) {
 213  0
             throw new RuntimeException("Invalid range specifier: " + rangeSpec);
 214  
         }
 215  0
         int from = Integer.parseInt(range[0].trim());
 216  0
         int to = Integer.parseInt(range[1].trim());
 217  0
         if (from > to) {
 218  0
             int tmp = from;
 219  0
             from = to;
 220  0
             to = tmp;
 221  
         }
 222  
         int num;
 223  
         // not very random huh...
 224  0
         if (from == to) {
 225  0
             num = from;
 226  
         } else {
 227  0
             num = from + RANDOM.nextInt((to - from) + 1);
 228  
         }
 229  0
         return num;
 230  
     }
 231  
 
 232  
     /**
 233  
      * @param name name of the node
 234  
      * @param location config file location
 235  
      * @param n the node
 236  
      * @param sb a StringBuilder into which to set contents of the node, preserving any XML content
 237  
      * @throws IOException
 238  
      */
 239  
     protected void getNodeValue(String name, String location, Node n, StringBuilder sb) throws IOException {
 240  0
         NodeList children = n.getChildNodes();
 241  
         // accumulate all content (preserving any XML content)
 242  
         try {
 243  0
             sb.setLength(0);
 244  0
             for (int j = 0; j < children.getLength(); j++) {
 245  0
                 sb.append(XmlJotter.jotNode(children.item(j), true));
 246  
             }
 247  0
         } catch (XmlException te) {
 248  0
             IOException ioe = new IOException("Error obtaining parameter '" + name + "' from config resource: " + location);
 249  0
             ioe.initCause(te);
 250  0
             throw ioe;
 251  0
         }
 252  0
     }
 253  
 
 254  
     /**
 255  
      * Sets a parameter in the parameter map, based on the override setting and whether a parameter of the same
 256  
      * name is already present 
 257  
      * @param params the current parameter map
 258  
      * @param override whether to override a previous parameter definition
 259  
      * @param name the parameter name
 260  
      * @param value the parameter value
 261  
      */
 262  
     private void setParam(Map params, Boolean override, String name, String value, String indent) {
 263  0
         if (value == null || "null".equals(value)) {
 264  0
             LOG.warn("Not adding property [" + name + "] because it is null - most likely no token could be found for substituion.");
 265  0
             return;
 266  
         }
 267  0
         if (override) {
 268  
             final String message;
 269  0
             Object existingValue = params.get(name);
 270  0
             if (existingValue != null) {
 271  
                 //if (!existingValue.equals(value)) {
 272  0
                     message = indent + "Overriding property " + name + "=[" + existingValue + "] with " + name + "=[" + value + "]"; 
 273  
                 //}
 274  0
                 params.remove(name);
 275  
             } else {
 276  0
                 message = indent + "Defining property " + name + "=[" + value + "]";
 277  
             }
 278  0
             LOG.debug(message);
 279  0
             params.put(name, value);
 280  0
         } else if (!params.containsKey(name)) {
 281  0
             LOG.debug(indent + "Defining property " + name + "=[" + value + "]");
 282  0
             params.put(name, value);
 283  
         } else {
 284  0
             LOG.debug(indent + "Not overriding existing parameter: " + name + " '" + params.get(name) + "'");
 285  
         }
 286  0
     }
 287  
 }