Coverage Report - org.kuali.rice.core.config.JAXBConfigImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
JAXBConfigImpl
0%
0/236
0%
0/100
2.622
JAXBConfigImpl$ConfigNamespaceURIFilter
0%
0/9
0%
0/4
2.622
 
 1  
 /*
 2  
  * Copyright 2007-2010 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.config;
 17  
 
 18  
 import org.apache.commons.collections.CollectionUtils;
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.apache.log4j.Logger;
 21  
 import org.kuali.rice.core.config.xsd.Param;
 22  
 import org.kuali.rice.core.util.ImmutableProperties;
 23  
 import org.kuali.rice.core.util.RiceUtilities;
 24  
 import org.xml.sax.Attributes;
 25  
 import org.xml.sax.InputSource;
 26  
 import org.xml.sax.SAXException;
 27  
 import org.xml.sax.XMLFilter;
 28  
 import org.xml.sax.helpers.XMLFilterImpl;
 29  
 
 30  
 import javax.xml.bind.JAXBContext;
 31  
 import javax.xml.bind.Unmarshaller;
 32  
 import javax.xml.bind.UnmarshallerHandler;
 33  
 import javax.xml.parsers.SAXParserFactory;
 34  
 import java.io.IOException;
 35  
 import java.io.InputStream;
 36  
 import java.util.*;
 37  
 import java.util.regex.Matcher;
 38  
 import java.util.regex.Pattern;
 39  
 
 40  
 /**
 41  
  * This implementation of the Config interface uses JAXB to parse the config file and
 42  
  * maintains an internal copy of all properties in their "raw" form (without any nested
 43  
  * properties resolved).  This allows properties to be added in stages and still alter
 44  
  * values of properties previously read in.
 45  
  * It also has settings for whether system properties should override all properties or
 46  
  * only serve as default when the property has not been defined.
 47  
  * 
 48  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 49  
  *
 50  
  */
 51  
 public class JAXBConfigImpl extends AbstractBaseConfig {
 52  
 
 53  0
     private static final Logger LOG = Logger.getLogger(JAXBConfigImpl.class);
 54  
 
 55  
     private static final String CONFIG_CODED_DEFAULTS = "classpath:org/kuali/rice/core/config-coded-defaults.xml";
 56  
     private static final String IMPORT_NAME = "config.location";
 57  
     private static final String INDENT = "  ";
 58  
     private static final String PLACEHOLDER_REGEX = "\\$\\{([^{}]+)\\}";
 59  
 
 60  
     // keep the same random
 61  0
     private static final Random RANDOM = new Random();
 62  
 
 63  0
     private final List<String> fileLocs = new ArrayList<String>();
 64  
 
 65  0
     private final Map<String, Object> objects = new LinkedHashMap<String, Object>();
 66  0
     private final Properties rawProperties = new Properties();
 67  0
     private final Properties resolvedProperties = new Properties();
 68  
 
 69  
     // compile pattern for regex once
 70  0
     private final Pattern pattern = Pattern.compile(PLACEHOLDER_REGEX);
 71  
 
 72  0
     private boolean systemOverride = false;
 73  
     
 74  0
     public JAXBConfigImpl(){}
 75  
     
 76  0
     public JAXBConfigImpl(org.kuali.rice.core.config.Config config) {
 77  0
             this.copyConfig(config);
 78  0
     }
 79  
     
 80  0
     public JAXBConfigImpl(String fileLoc, org.kuali.rice.core.config.Config config) {
 81  0
             this.copyConfig(config);
 82  0
             this.fileLocs.add(fileLoc);
 83  0
     }
 84  
     
 85  0
         public JAXBConfigImpl(List<String> fileLocs, org.kuali.rice.core.config.Config config) {
 86  0
             this.copyConfig(config);
 87  0
             this.fileLocs.addAll(fileLocs);
 88  
             
 89  0
     }
 90  
     
 91  0
     public JAXBConfigImpl(String fileLoc) {
 92  0
         this.fileLocs.add(fileLoc);
 93  0
     }
 94  
 
 95  0
     public JAXBConfigImpl(List<String> fileLocs) {
 96  0
         this.fileLocs.addAll(fileLocs);
 97  0
     }
 98  
 
 99  0
     public JAXBConfigImpl(Properties properties) {               
 100  0
             this.putProperties(properties);
 101  0
     }
 102  
     
 103  0
     public JAXBConfigImpl(String fileLoc, Properties properties) {
 104  0
         this.fileLocs.add(fileLoc);    
 105  0
         this.putProperties(properties);
 106  0
     }
 107  
 
 108  0
     public JAXBConfigImpl(List<String> fileLocs, Properties properties) {
 109  0
         this.fileLocs.addAll(fileLocs);        
 110  0
         this.putProperties(properties);
 111  0
     }
 112  
     
 113  
     /*****************************************************/
 114  
 
 115  
     /*
 116  
      * We need the ability to take a config object and copy the raw + cached data into
 117  
      * this config object. 
 118  
      */
 119  
     private void copyConfig(Config config){
 120  0
             if(config == null) return;
 121  
             
 122  0
             if(config instanceof JAXBConfigImpl) {
 123  0
                     this.rawProperties.putAll(((JAXBConfigImpl) config).rawProperties);
 124  0
                     this.resolvedProperties.putAll(((JAXBConfigImpl) config).resolvedProperties);
 125  
             }else{                    
 126  
                     
 127  0
                     this.putProperties(config.getProperties());
 128  
                     
 129  
             }
 130  0
             if(config.getObjects() != null)
 131  0
                     this.objects.putAll(config.getObjects());
 132  0
     }
 133  
     
 134  
     public Object getObject(String key) {
 135  0
         return objects.get(key);
 136  
     }
 137  
 
 138  
     public Map<String, Object> getObjects() {
 139  0
             return Collections.unmodifiableMap(objects);
 140  
     }
 141  
 
 142  
     public Properties getProperties() {
 143  0
             return new ImmutableProperties(resolvedProperties);
 144  
     }
 145  
 
 146  
     public String getProperty(String key) {
 147  0
         return resolvedProperties.getProperty(key);
 148  
     }
 149  
 
 150  
     public void overrideProperty(String name, String value) {
 151  0
             this.putProperty(name, value);
 152  0
     }
 153  
    
 154  
     /**
 155  
      * 
 156  
      * This overrided the property. Takes the place of the now depricated overrideProperty
 157  
      * 
 158  
      * @see org.kuali.rice.core.config.Config#putProperty(java.lang.String, java.lang.Object)
 159  
      */
 160  
         public void putProperty(String key, String value) {
 161  0
         this.setProperty(key, replaceVariable(key, value));
 162  0
         resolveRawToCache();
 163  0
         }
 164  
 
 165  
         public void putProperties(Properties properties) {
 166  0
         if (properties != null) {
 167  0
             for(Object o : properties.keySet()) {
 168  0
                     this.setProperty((String)o, replaceVariable((String)o, properties.getProperty((String)o)));
 169  
             }
 170  
             
 171  0
                 resolveRawToCache();
 172  
         }
 173  0
     }
 174  
 
 175  
     public void parseConfig() throws IOException {
 176  
 
 177  0
         if (fileLocs.size() > 0) {
 178  
 
 179  0
             if (LOG.isInfoEnabled()) {
 180  0
                 LOG.info("Loading Rice configs: " + StringUtils.join(fileLocs, ", "));
 181  
             }
 182  
 
 183  
             JAXBContext jaxbContext;
 184  
             Unmarshaller unmarshaller;
 185  
 
 186  
             try {
 187  0
                 jaxbContext = JAXBContext.newInstance(org.kuali.rice.core.config.xsd.Config.class);
 188  0
                 unmarshaller = jaxbContext.createUnmarshaller();
 189  0
             } catch (Exception ex) {
 190  0
                 throw new ConfigurationException("Error initializing JAXB for config", ex);
 191  0
             }
 192  
 
 193  
             // add these first so they can be overridden
 194  0
             configureBuiltIns();
 195  
 
 196  
             // parse all config files, but do not resolve any right hand side variables
 197  0
             for (String s : fileLocs) {
 198  0
                 parseConfig(s, unmarshaller, 0);
 199  
             }
 200  
 
 201  
             // now that all properties have been loaded, resolve the right hand side from
 202  
             // the raw properties into the resolved properties.  This will also replace properties
 203  
             // defined in the files with system properties if systemOverride==true.
 204  0
             resolveRawToCache();
 205  
 
 206  0
             if (LOG.isInfoEnabled()) {
 207  0
                     final StringBuilder log = new StringBuilder();
 208  0
                     log.append("\n");
 209  0
                     log.append("####################################\n");
 210  0
                     log.append("#\n");
 211  0
                     log.append("# Properties used after config override/replacement\n");
 212  0
                     log.append("# " + StringUtils.join(fileLocs, ", ") + "\n");
 213  0
                     log.append("#\n");
 214  0
                     log.append("####################################\n");
 215  
                 //commented out to backport to java 5
 216  
                 //SortedSet<String> sorted = new TreeSet<String>(properties.stringPropertyNames());
 217  
                 
 218  0
                 SortedSet<String> sorted = new TreeSet<String>();
 219  0
                 CollectionUtils.addAll(sorted, rawProperties.propertyNames());
 220  
                 
 221  0
                 for (String s : sorted) {
 222  0
                         log.append("Using config Prop " + s + "=[" + ConfigLogger.getDisplaySafeValue(s, this.getProperty(s)) + "]\n");
 223  
                 }
 224  0
                 LOG.info(log);
 225  
             }
 226  
 
 227  0
         } else {
 228  0
             LOG.info("Loading Rice configs: No config files specified");
 229  
         }
 230  0
     }
 231  
 
 232  
     protected void parseConfig(String filename, Unmarshaller unmarshaller, int depth) throws IOException {
 233  
 
 234  0
         InputStream in = null;
 235  
 
 236  
         // have to check for empty filename because getResource will
 237  
         // return non-null if passed ""
 238  0
         if (StringUtils.isNotEmpty(filename)) {
 239  0
             in = RiceUtilities.getResourceAsStream(filename);
 240  
         }
 241  
 
 242  0
         if (in == null) {
 243  0
                 final StringBuilder log = new StringBuilder();
 244  0
                 log.append("\n");
 245  0
                 log.append("####################################\n");
 246  0
                 log.append("#\n");
 247  0
                 log.append("# Configuration file '" + filename + "' not found!\n");
 248  0
                 log.append("#\n");
 249  0
                 log.append("####################################\n");
 250  0
                 LOG.warn(log);
 251  0
         } else {
 252  
 
 253  0
             final String prefix = StringUtils.repeat(INDENT, depth);            
 254  0
             LOG.info(prefix + "+ Parsing config: " + filename);            
 255  
             org.kuali.rice.core.config.xsd.Config config;
 256  
 
 257  
             try {
 258  0
                 config = unmarshal(unmarshaller, in);
 259  0
             } catch (Exception ex) {
 260  0
                 throw new ConfigurationException("Error parsing config file: " + filename, ex);
 261  0
             }
 262  
 
 263  0
             for (Param p : config.getParamList()) {
 264  
 
 265  0
                 String name = p.getName();
 266  
                                 
 267  0
                 if (name.equals(IMPORT_NAME)) {
 268  0
                     String configLocation = parseValue(p.getValue(), new HashSet<String>());
 269  
                     // Remove new lines and white space.
 270  0
                     if(configLocation != null){
 271  0
                             configLocation = configLocation.trim();
 272  
                     }
 273  0
                     parseConfig(configLocation, unmarshaller, depth + 1);
 274  0
                 } else if(p.isSystem()){
 275  0
                         if (p.isOverride() || !(System.getProperty(name) != null)){
 276  0
                                 if(p.isRandom()){
 277  0
                                         String randStr = String.valueOf(generateRandomInteger(p.getValue()));
 278  0
                                         System.setProperty(name, randStr);
 279  0
                             this.setProperty(p.getName(), randStr); 
 280  0
                                 if(LOG.isInfoEnabled())
 281  
                                 {        
 282  0
                                         LOG.info("generating random string " + randStr + " for system property " + p.getName());
 283  
                                 }    
 284  0
                                 }else{
 285  
                                         // resolve and set system params immediately so they can override
 286  
                             // existing system params. Add to rawProperties resolved as well to
 287  
                             // prevent possible mismatch
 288  0
                             HashSet<String> set = new HashSet<String>();
 289  0
                             set.add(p.getName());
 290  0
                             String value = parseValue(p.getValue(), set);
 291  0
                             System.setProperty(name, value);
 292  0
                             this.setProperty(name, value);
 293  0
                                 }
 294  
                         }
 295  
                 }
 296  0
                 else if (p.isOverride() || !rawProperties.containsKey(name)) {
 297  
 
 298  0
                         if (p.isRandom()) {
 299  
                     
 300  0
                             String randStr = String.valueOf(generateRandomInteger(p.getValue()));
 301  0
                         this.setProperty(p.getName(), randStr); 
 302  0
                             if(LOG.isInfoEnabled())
 303  
                             {        
 304  0
                                     LOG.info("generating random string " + randStr + " for property " + p.getName());
 305  
                             }
 306  0
                     } else {
 307  
                             
 308  
                             /*
 309  
                              * myProp = dog
 310  
                              * We have a case where you might want myProp = ${myProp}:someOtherStuff:${foo}
 311  
                              * This would normally overwrite the existing myProp with ${myProp}:someOtherStuff:${foo}
 312  
                              * but what we want is:
 313  
                              * myProp = dog:someOtherStuff:${foo}
 314  
                              * so that we put the existing value of myProp into the new value. Basically how path works.
 315  
                              */
 316  0
                             String value = replaceVariable(name, p.getValue());                       
 317  
                             
 318  0
                             this.setProperty(name, value);                            
 319  
                     }
 320  
                 }
 321  0
             }
 322  
 
 323  0
             LOG.info(prefix + "- Parsed config: " + filename);
 324  
         }
 325  0
     }
 326  
     
 327  
     /*
 328  
      * This will set the property. No logic checking so what you pass in gets set.
 329  
      * We use this as a focal point for debugging the raw config changes.
 330  
      */
 331  
     protected void setProperty(String name, String value){
 332  0
             if(LOG.isInfoEnabled()){
 333  0
                     String oldProp = rawProperties.getProperty(name);
 334  0
                     if(oldProp != null && !oldProp.equals(value)){
 335  0
                             LOG.info("Raw Config Override: " + name + "=[" + ConfigLogger.getDisplaySafeValue(name,oldProp) +"]->[" + ConfigLogger.getDisplaySafeValue(name,value) +"]");
 336  
                     }
 337  
             }
 338  0
             rawProperties.setProperty(name, value);
 339  0
     }    
 340  
 
 341  
     protected String resolve(String key) {
 342  0
             return resolve(key, null);
 343  
     }
 344  
     
 345  
     /**
 346  
      * This method will determine the value for a property by looking it up in the raw properties.  If the
 347  
      * property value contains a nested property (foo=${nested}) it will start the recursion by
 348  
      * calling parseValue().
 349  
      * It will also check for a system property of the same name and, based on the value of systemOverride,
 350  
      * 'override with' the system property or 'default to' the system property if not found in the raw properties.
 351  
      * This method only determines the resolved value, it does not modify the properties in the resolved or raw
 352  
      * properties objects.
 353  
      * 
 354  
      * @param key they key of the property for which to determine the value
 355  
      * @param keySet contains all keys used so far in this recursion.  used to check for circular references.
 356  
      * @return
 357  
      */
 358  
     protected String resolve(String key, Set keySet) {
 359  
             
 360  
         // check if we have already resolved this key and have circular reference
 361  0
         if (keySet != null && keySet.contains(key)) {
 362  0
             throw new ConfigurationException("Circular reference in config: " + key);
 363  
         }
 364  
         
 365  0
         String value = this.rawProperties.getProperty(key);
 366  
         
 367  0
         if ((value == null || systemOverride) && System.getProperties().containsKey(key)) {
 368  0
             value = System.getProperty(key);
 369  
         }
 370  
         
 371  0
         if (value != null && value.contains("${")) {
 372  0
                 if(keySet == null) {
 373  0
                         keySet = new HashSet<String>();
 374  
                 }
 375  0
             keySet.add(key);
 376  
 
 377  0
             value = parseValue(value, keySet);
 378  
             
 379  0
             keySet.remove(key);
 380  
         }
 381  
         
 382  0
         if(value == null) {
 383  0
                 value = "";
 384  0
                 LOG.warn("Property key: '" + key + "' is not available and hence set to empty");
 385  
         }
 386  
 
 387  0
         return value;
 388  
     }
 389  
  
 390  
     /**
 391  
      * This method parses the value string to find all nested properties (foo=${nested}) and
 392  
      * replaces them with the value returned from calling resolve().  It does this in a new
 393  
      * string and does not modify the raw or resolved properties objects.
 394  
      * 
 395  
      * @param value the string to search for nest properties
 396  
      * @param keySet contains all keys used so far in this recursion.  used to check for circular references.
 397  
      * @return
 398  
      */
 399  
     protected String parseValue(String value, Set<String> keySet) {
 400  0
         String result = value;
 401  
 
 402  0
         Matcher matcher = pattern.matcher(value);
 403  
 
 404  0
         while (matcher.find()) {
 405  
 
 406  
             // get the first, outermost ${} in the string.  removes the ${} as well.
 407  0
             String key = matcher.group(1);
 408  
 
 409  0
             String resolved = resolve(key, keySet);
 410  
 
 411  0
             result = matcher.replaceFirst(Matcher.quoteReplacement(resolved));
 412  0
             matcher = matcher.reset(result);
 413  0
         }
 414  
 
 415  0
         return result;
 416  
     }
 417  
     
 418  
     
 419  
     /**
 420  
      * This method is used when reading in new properties to check if there is a direct reference to the
 421  
      * key in the value.  This emulates operating system environment variable setting behavior 
 422  
      * and replaces the reference in the value with the current value of the property from the rawProperties.
 423  
      * <pre>
 424  
      * ex:
 425  
      * path=/usr/bin;${someVar}
 426  
      * path=${path};/some/other/path
 427  
      * 
 428  
      * resolves to:
 429  
      * path=/usr/bin;${someVar};/some/other/path
 430  
      * </pre>
 431  
      * 
 432  
      * It does not resolve the the value from rawProperties as it could contain nested properties that might change later.
 433  
      * If the property does not exist in the rawProperties it will check for a default system property now to
 434  
      * prevent a circular reference error.
 435  
      * 
 436  
      * @param name the property name
 437  
      * @param value the value to check for nested property of the same name
 438  
      * @return
 439  
      */
 440  
     protected String replaceVariable(String name, String value){
 441  0
             String regex = "(?:\\$\\{"+ name +"\\})";
 442  0
             String temporary = null;
 443  
             
 444  
             // Look for a property in the map first and use that.  If system override is true
 445  
             // then it will get overridden during the resolve phase.  If the value is null
 446  
             // we need to check the system now so we don't throw an error.
 447  0
             if(value.contains("${" + name + "}")) {
 448  0
                     if( (temporary = rawProperties.getProperty(name)) == null ) {
 449  0
                             temporary = System.getProperty(name);
 450  
                     }
 451  
                     
 452  0
                     if(temporary != null) {
 453  0
                             return value.replaceAll(regex,  Matcher.quoteReplacement(temporary));
 454  
                     }
 455  
             }   
 456  
             
 457  0
             return value;
 458  
     }
 459  
     
 460  
     /**
 461  
      * This method iterates through the raw properties and stores their resolved values in the
 462  
      * resolved properties map, which acts as a cache so we don't have to run the recursion every
 463  
      * time getProperty() is called.
 464  
      */
 465  
     protected void resolveRawToCache() {
 466  0
             if(rawProperties.size() > 0) {
 467  0
                     Properties oldProps = new Properties(new ImmutableProperties(resolvedProperties));  
 468  
                     //oldProps.putAll(new ImmutableProperties(resolvedProperties));
 469  0
                     resolvedProperties.clear();
 470  
                     
 471  0
                     for(Object o : rawProperties.keySet()) {                            
 472  0
                             String resolved = resolve((String)o);
 473  
                             
 474  0
                             if(LOG.isInfoEnabled()){
 475  0
                                     String oldResolved = oldProps.getProperty((String)o);
 476  0
                                     if(oldResolved != null && !oldResolved.equals(resolved)){
 477  0
                                             String key = (String)o;
 478  0
                                             String unResolved = rawProperties.getProperty(key);
 479  
                                             
 480  0
                                             if(unResolved.contains("$")){
 481  0
                                                     LOG.info("Resolved Config Override: " + key + "(" + unResolved +")=[" + ConfigLogger.getDisplaySafeValue(key,oldResolved) +"]->[" + ConfigLogger.getDisplaySafeValue(key,resolved) +"]");                                             
 482  
                                             }else{
 483  0
                                                     LOG.info("Resolved Config Override: " + key + "=[" + ConfigLogger.getDisplaySafeValue(key,oldResolved) +"]->[" + ConfigLogger.getDisplaySafeValue(key,resolved) +"]"); 
 484  
                                             }                                            
 485  
                                     }
 486  
                             }                            
 487  0
                             resolvedProperties.setProperty((String)o, resolved);
 488  0
                     }
 489  
             }
 490  0
     }
 491  
 
 492  
     /**
 493  
      * Configures built-in properties.
 494  
      */
 495  
     protected void configureBuiltIns() {
 496  0
             this.setProperty("host.ip", RiceUtilities.getIpNumber());
 497  0
             this.setProperty("host.name", RiceUtilities.getHostName());
 498  0
     }
 499  
 
 500  
     /**
 501  
      * Generates a random integer in the range specified by the specifier, in the format: min-max
 502  
      * 
 503  
      * @param rangeSpec
 504  
      *            a range specification, 'min-max'
 505  
      * @return a random integer in the range specified by the specifier, in the format: min-max
 506  
      */
 507  
     protected int generateRandomInteger(String rangeSpec) {
 508  0
         String[] range = rangeSpec.split("-");
 509  0
         if (range.length != 2) {
 510  0
             throw new RuntimeException("Invalid range specifier: " + rangeSpec);
 511  
         }
 512  0
         int from = Integer.parseInt(range[0].trim());
 513  0
         int to = Integer.parseInt(range[1].trim());
 514  0
         if (from > to) {
 515  0
             int tmp = from;
 516  0
             from = to;
 517  0
             to = tmp;
 518  
         }
 519  
         int num;
 520  
         // not very random huh...
 521  0
         if (from == to) {
 522  0
             num = from;
 523  0
             if(LOG.isInfoEnabled())
 524  
             {
 525  0
                     LOG.info("from==to, so not generating random value for property.");
 526  
             }
 527  
         } else {
 528  0
             num = from + RANDOM.nextInt((to - from) + 1);
 529  
         }
 530  0
         return num;
 531  
     }
 532  
     
 533  
     public boolean isSystemOverride() {
 534  0
         return systemOverride;
 535  
     }
 536  
     
 537  
     /**
 538  
      * If set to true then system properties will always be checked first, disregarding
 539  
      * any values in the config.
 540  
      * 
 541  
      * The default is false.
 542  
      * 
 543  
      * @param systemOverride
 544  
      */
 545  
     public void setSystemOverride(boolean systemOverride) {
 546  0
         this.systemOverride = systemOverride;
 547  0
     }
 548  
 
 549  
     protected org.kuali.rice.core.config.xsd.Config unmarshal(Unmarshaller unmarshaller, InputStream in) throws Exception {
 550  0
         SAXParserFactory spf = SAXParserFactory.newInstance();
 551  0
         spf.setNamespaceAware(true);
 552  
 
 553  0
         XMLFilter filter = new ConfigNamespaceURIFilter();
 554  0
         filter.setParent(spf.newSAXParser().getXMLReader());
 555  
 
 556  0
         UnmarshallerHandler handler = unmarshaller.getUnmarshallerHandler();
 557  0
         filter.setContentHandler(handler);
 558  
 
 559  0
         filter.parse(new InputSource(in));
 560  
 
 561  0
         return (org.kuali.rice.core.config.xsd.Config)handler.getResult();
 562  
     }
 563  
 
 564  
     /**
 565  
      *  This is a SAX filter that adds the config xml namespace to the document if the document
 566  
      *  does not have a namespace (for backwards compatibility).  This filter assumes unqualified
 567  
      *  attributes and does not modify their namespace (if any).
 568  
      *  
 569  
      *   This could be broken out into a more generic class if Rice makes more use of JAXB.
 570  
      * 
 571  
      * @author Kuali Rice Team (kuali-rice@googlegroups.com)
 572  
      *
 573  
      */
 574  0
     public class ConfigNamespaceURIFilter extends XMLFilterImpl {
 575  
 
 576  
         public static final String CONFIG_URI="http://rice.kuali.org/xsd/core/config";
 577  
         
 578  
         public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
 579  0
             if(StringUtils.isBlank(uri)) {
 580  0
                 uri = CONFIG_URI;
 581  
             }
 582  
             
 583  0
             super.startElement(uri, localName, qName, atts);
 584  0
         }
 585  
 
 586  
         public void endElement(String uri, String localName, String qName) throws SAXException {
 587  0
             if(StringUtils.isBlank(uri)) {
 588  0
                 uri = CONFIG_URI;
 589  
             }
 590  
             
 591  0
             super.endElement(uri, localName, qName);
 592  0
         }
 593  
     }
 594  
 
 595  
         
 596  
         public void putObject(String key, Object value) {
 597  0
                 this.objects.put(key, value);                
 598  0
         }
 599  
         
 600  
         public void putObjects(Map<String, Object> objects) {
 601  0
                 this.objects.putAll(objects);        
 602  0
         }
 603  
         
 604  
         public void removeObject(String key){
 605  0
                 this.objects.remove(key);
 606  0
         }
 607  
         
 608  
         public void removeProperty(String key){
 609  0
                 this.rawProperties.remove(key);
 610  
                         
 611  0
             resolveRawToCache();
 612  0
         }
 613  
 
 614  
         public void putConfig(Config config) {
 615  0
                 this.copyConfig(config);
 616  0
         }
 617  
         
 618  
     
 619  
 }