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