Coverage report

  %line %branch
ca.spaz.cron.CRONConfiguration
56% 
83% 

 1  
 /*
 2  
  *******************************************************************************
 3  
  * Copyright (c) 2005 Chris Rose and AIMedia
 4  
  * All rights reserved. CRONConfiguration and the accompanying materials
 5  
  * are made available under the terms of the Common Public License v1.0
 6  
  * which accompanies this distribution, and is available at
 7  
  * http://www.eclipse.org/legal/cpl-v10.html
 8  
  * 
 9  
  * Contributors:
 10  
  *     Chris Rose
 11  
  *******************************************************************************/
 12  
 package ca.spaz.cron;
 13  
 
 14  
 import java.io.*;
 15  
 import java.lang.reflect.*;
 16  
 import java.util.*;
 17  
 import java.util.regex.*;
 18  
 
 19  
 import org.apache.log4j.Logger;
 20  
 
 21  
 import ca.spaz.cron.config.*;
 22  
 import ca.spaz.util.ToolBox;
 23  
 
 24  21
 public final class CRONConfiguration {
 25  
    /**
 26  
     * Logger for this class
 27  
     */
 28  30
    private static final Logger logger = Logger
 29  30
          .getLogger(CRONConfiguration.class);
 30  
 
 31  30
    private static CRONConfiguration instance = null;
 32  
    
 33  30
    private static final Pattern VALIDATOR_PATTERN = Pattern.compile("cronconfig\\.validator\\.(\\d+)\\.classname");
 34  
    
 35  30
    private static final Pattern ENV_PATTERN = Pattern.compile("\\$\\(([^)]+)\\)");
 36  
    
 37  
    /**
 38  
     * Get the global singleton instance of the configuration manager
 39  
     * @return a unique instance of <code>CRONConfiguration</code>.
 40  
     */
 41  
    public static final CRONConfiguration getInstance() {
 42  1150
       return getInstance(DEFAULT_CONFIGURATION);
 43  
    }
 44  
    
 45  
    static final CRONConfiguration getInstance(String propertiesURL) {
 46  1150
       if (null == instance) {
 47  30
          instance = new CRONConfiguration(propertiesURL);
 48  
       }
 49  1150
       return instance;
 50  
    }
 51  
    
 52  
    private Properties systemProperties;
 53  
    private Properties userProperties;
 54  
    
 55  
    private static final String DEFAULT_CONFIGURATION = "/cron.properties";
 56  
    private static final String USER_PROPERTIES_FILE = "cron.properties";
 57  
 
 58  
    private boolean userCanSave;
 59  
 
 60  30
    private File userPropertyFile = null;
 61  
 
 62  30
    private Set cachedKeys = null;
 63  
 
 64  
    private boolean writeOnChange;
 65  
 
 66  30
    private List validatorList = null;
 67  
 
 68  
    private String systemPropertiesURL;
 69  
    
 70  30
    private CRONConfiguration(String propertiesURL) {
 71  30
       this.systemPropertiesURL = propertiesURL;
 72  30
       writeOnChange = true;
 73  30
       userCanSave = true;
 74  
 
 75  
       // Load user property validators
 76  30
       loadDefaultValidators();
 77  
       // Load user properties
 78  
       try {
 79  30
          loadUserProperties();
 80  0
       } catch (FileNotFoundException e) {
 81  0
          logger.error("CRONConfiguration()", e);
 82  0
       } catch (IOException e) {
 83  0
          logger.error("CRONConfiguration()", e);
 84  9
       }
 85  30
    }
 86  
 
 87  
    private static String getClassnameKey(int value) {
 88  30
       return "cronconfig.validator." + value + ".classname";
 89  
    }
 90  
    
 91  
    private static String getFactoryMethodNameKey(int value) {
 92  30
       return "cronconfig.validator." + value + ".constructor";
 93  
    }
 94  
    
 95  
    private void loadDefaultValidators() {
 96  30
       Properties p = getSystemProperties();
 97  1080
       for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
 98  1470
          String key = (String) iter.next();
 99  1470
          Matcher matcher = VALIDATOR_PATTERN.matcher(key);
 100  1470
          if (matcher.find()) {
 101  30
             int id = Integer.parseInt(matcher.group(1));
 102  30
             String classname = p.getProperty(getClassnameKey(id));
 103  30
             String methodName = "";
 104  
             try {
 105  30
                Class val = Class.forName(classname);
 106  30
                methodName = p.getProperty(getFactoryMethodNameKey(id));
 107  30
                PropertyValidator validator = null;
 108  30
                if (null == methodName) {
 109  0
                   validator = (PropertyValidator) val.newInstance();
 110  0
                } else {
 111  30
                   Method method = val.getMethod(methodName, null);
 112  30
                   validator = (PropertyValidator) method.invoke(val, null);
 113  
                }
 114  30
                addPropertyValidator(validator);
 115  30
                logger.info("Loaded default validator " + classname);
 116  0
             } catch (ClassNotFoundException e) {
 117  0
                logger.error("Error in " + systemPropertiesURL + ", " + key + "=" + classname + " does not exist.");
 118  0
             } catch (SecurityException e) {
 119  0
                logger.error("loadDefaultValidators()", e);
 120  0
             } catch (InstantiationException e) {
 121  0
                logger.error("loadDefaultValidators()", e);
 122  0
             } catch (IllegalAccessException e) {
 123  0
                logger.error("loadDefaultValidators()", e);
 124  0
             } catch (NoSuchMethodException e) {
 125  0
                logger.error("Error in " + systemPropertiesURL + ", " + getFactoryMethodNameKey(id) + "constructor=" + methodName + " does not exist.");
 126  0
             } catch (IllegalArgumentException e) {
 127  0
                logger.error("loadDefaultValidators()", e);
 128  0
             } catch (InvocationTargetException e) {
 129  0
                logger.error("loadDefaultValidators()", e);
 130  9
             }
 131  
          }
 132  441
       }
 133  30
    }
 134  
 
 135  
    private Properties getSystemProperties() {
 136  60
       if (null == systemProperties) {
 137  30
          systemProperties = new Properties();
 138  
       
 139  30
          InputStream sysProps = getClass().getResourceAsStream(systemPropertiesURL);
 140  30
          if (null == sysProps) {
 141  0
             logger.error("Unable to load resource for system properties");
 142  0
             throw new IllegalStateException("No system property resource");
 143  
          }
 144  
          try {
 145  30
             systemProperties.load(sysProps);
 146  0
          } catch (IOException e) {
 147  0
             logger.error("getSystemProperties()", e);
 148  0
             throw new IllegalStateException("Cannot operate without system properties");
 149  9
          }
 150  
       }
 151  60
       return systemProperties;
 152  
    }
 153  
 
 154  
    /**
 155  
     * @throws IOException
 156  
     * @throws FileNotFoundException
 157  
     */
 158  
    private void loadUserProperties() throws IOException, FileNotFoundException {
 159  30
       userProperties = new Properties(getSystemProperties());
 160  30
       File userFile = getUserPropertiesFile();
 161  30
       if (userFile == null || !userFile.exists()) {
 162  
          // Here, it's critical.  We failed to create the user props file, which is bad.
 163  
          // However, this might just mean we can't save the file.
 164  0
          userCanSave = false;
 165  
       }
 166  
       // At this point we're guaranteed that the user properties exist.
 167  30
       userProperties.load(new FileInputStream(userFile));
 168  30
    }
 169  
    
 170  
    private File getUserPropertiesFile() {
 171  30
       if (userPropertyFile == null && userCanSave) {
 172  30
          File appDir = ToolBox.getUserAppDirectory("cronometer");
 173  30
          if (!appDir.exists()) {
 174  0
             appDir.mkdirs();
 175  0
             if (!appDir.exists()) {
 176  0
                logger.error("Unable to create user app prefs directory " + appDir);
 177  
             }
 178  
          }
 179  30
          userPropertyFile = new File(appDir, USER_PROPERTIES_FILE);
 180  30
          logger.info("Initializing user property file " + userPropertyFile.getAbsolutePath());
 181  30
          if (!userPropertyFile.exists()) {
 182  0
             logger.info("Creating user property file " + userPropertyFile.getAbsolutePath());
 183  
             try {
 184  0
                if (userPropertyFile.createNewFile()) {
 185  
                   // Nothing.  All is well.
 186  0
                } else {
 187  0
                   logger.error("Unable to create user property file");
 188  0
                   userCanSave = false;
 189  
                }
 190  0
             } catch (IOException e) {
 191  0
                logger.error("getUserPropertiesFile()", e);
 192  0
                userCanSave = false;
 193  0
             }
 194  
          } else {
 195  
             // Nothing.  All is well.
 196  
          }
 197  
       }
 198  30
       return userPropertyFile;
 199  
    }
 200  
    
 201  
    /**
 202  
     * Sets the value of a property in the user configuration section.  If
 203  
     * <code>writeOnChange</code> is <code>true</code>, this will immediately commit
 204  
     * the change to the user config file.  Otherwise, it will write to the
 205  
     * properties in memory, and the caller must invoke <code>store()</code> to
 206  
     * write the configuration to permanent storage.
 207  
     * 
 208  
     * @param key The name of the property to set.
 209  
     * @param value The new value of the property
 210  
     * @return the value that the property held prior to being set.  <code>null</code>,
 211  
     * if the property had not previously been set.
 212  
     */
 213  
    public String setProperty(String key, String value) {
 214  10
       if (!validateKey(key, value)) {
 215  0
          return null;
 216  
       }
 217  
       // Set the user property
 218  10
       String ret = null;
 219  17
       synchronized(userProperties) {
 220  10
          if (!userProperties.containsKey(key)) {
 221  
             // Clear the key cache
 222  10
             cachedKeys = null;
 223  
          }
 224  10
          ret = (String) userProperties.setProperty(key, value);
 225  10
          if (writeOnChange) {
 226  0
             store();
 227  
          }
 228  3
       }
 229  10
       return ret;
 230  
    }
 231  
    
 232  
    /**
 233  
     * Add a <code>PropertyValidator</code> to the configuration.  This will be checked
 234  
     * against each time a property is set.
 235  
     * @param validator the validator.
 236  
     */
 237  
    public void addPropertyValidator(PropertyValidator validator) {
 238  30
       getValidators().add(validator);
 239  30
    }
 240  
    
 241  
    /**
 242  
     * Remove a validator from the configuration.
 243  
     * @param validator The validator to remove.
 244  
     */
 245  
    public void removePropertyValidator(PropertyValidator validator) {
 246  0
       getValidators().remove(validator);
 247  0
    }
 248  
    
 249  
    private List getValidators() {
 250  40
       if (null == validatorList) {
 251  30
          validatorList = new ArrayList();
 252  
       }
 253  40
       return validatorList;
 254  
    }
 255  
 
 256  
    private boolean validateKey(String key, String value) {
 257  24
       for (Iterator iter = getValidators().iterator(); iter.hasNext();) {
 258  10
          PropertyValidator val = (PropertyValidator) iter.next();
 259  10
          if (!val.isValid(key, value)) {
 260  0
             return false;
 261  
          }
 262  3
       }
 263  10
       return true;
 264  
    }
 265  
    
 266  
    public String getProperty(String property, String defaultValue, boolean expandEnv) {
 267  520
       String ret = userProperties.getProperty(property, defaultValue);
 268  520
       if (expandEnv) {
 269  0
          ret = replaceEnvVars(ret);
 270  
       }
 271  520
       return ret;
 272  
    }
 273  
    
 274  
    public String getProperty(String property, boolean expandEnv) {
 275  3120
       String ret = userProperties.getProperty(property);
 276  3120
       if (ret != null && expandEnv) {
 277  10
          ret = replaceEnvVars(ret);
 278  
       }
 279  3120
       return ret;
 280  
    }
 281  
 
 282  
    /**
 283  
     * @param value
 284  
     * @return
 285  
     */
 286  
    private String replaceEnvVars(String value) {
 287  10
       Matcher mat = ENV_PATTERN.matcher(value);
 288  10
       int idx = 0;
 289  10
       StringBuffer buf = new StringBuffer();
 290  27
       while (mat.find(idx)) {
 291  10
          buf.append(value.substring(idx, mat.start()));
 292  10
          buf.append(System.getenv().get(mat.group(1)));
 293  10
          idx = mat.end();
 294  3
       }
 295  10
       buf.append(value.substring(idx));
 296  
       
 297  10
       value = buf.toString();
 298  10
       return value;
 299  
    }
 300  
 
 301  
    /**
 302  
     * Get a property from the configuration set, with a provided default value.
 303  
     * @param property The name of the property to retrieve.
 304  
     * @param defaultValue The default value of the named property.
 305  
     * @return The value of the property in the configuration, or 
 306  
     * <code>defaultValue</code> if none was set.
 307  
     */
 308  
    public String getProperty(String property, String defaultValue) {
 309  520
       return getProperty(property, defaultValue, false);
 310  
    }
 311  
    
 312  
    /**
 313  
     * Get a property from the configuration set.
 314  
     * @param property The name of the property to retrieve.
 315  
     * @return The value of the property in the configuration, or 
 316  
     * <code>null</code> if none was set.
 317  
     */
 318  
    public String getProperty(String property) {
 319  3100
       return getProperty(property, false);
 320  
    }
 321  
    
 322  
    /**
 323  
     * Set this value to determine if the configuration manager should write to disk
 324  
     * on every change to the configuration, or if it should wait until
 325  
     * <code>store()</code> is called to proceed.
 326  
     * 
 327  
     * @param writeOnChange Set to <code>true</code> to enable writes, <code>false</code>
 328  
     * to disable.
 329  
     */
 330  
    public void setWriteOnChange(boolean writeOnChange) {
 331  10
       this.writeOnChange = writeOnChange;
 332  10
    }
 333  
    
 334  
    /**
 335  
     * Write the configuration to disk.  This will flush the user config into their
 336  
     * configuration file.
 337  
     * @return <code>true</code> if the write succeeded, <code>false</code> if some
 338  
     * exception was thrown.
 339  
     */
 340  
    public boolean store() {
 341  0
       boolean success = false;
 342  0
       if (userCanSave) {
 343  
          // Store them.  Log any errors, but catch exceptions.
 344  
          // We have a valid property file.
 345  
          try {
 346  0
             FileOutputStream fos = new FileOutputStream(getUserPropertiesFile(), false);
 347  0
             userProperties.store(fos, "CRONOMETER User Property File.");
 348  0
             success = true;
 349  0
          } catch (FileNotFoundException e) {
 350  0
             logger.error("setProperty(String, String)", e);
 351  0
             userCanSave = false;
 352  0
          } catch (IOException e) {
 353  0
             logger.error("setProperty(String, String)", e);
 354  0
             userCanSave = false;
 355  0
          }
 356  
       }
 357  0
       return success;
 358  
    }
 359  
    
 360  
    /**
 361  
     * Retrieve the set of all keys in both the user and system configurations.
 362  
     * 
 363  
     * @return a <code>Set</code> containing the union of the property keys in both
 364  
     * the user properties and system properties.
 365  
     */
 366  
    public Set keySet() {
 367  0
       if (cachedKeys == null) {
 368  0
          cachedKeys = new HashSet();
 369  0
          cachedKeys.addAll(userProperties.keySet());
 370  0
          cachedKeys.addAll(systemProperties.keySet());
 371  
       }
 372  0
       return Collections.unmodifiableSet(cachedKeys);
 373  
    }
 374  
 
 375  
    /**
 376  
     * The main method.  This is what is run when the app is executed.
 377  
     * @param args  The command-line arguments for the application.
 378  
     */
 379  
    public static void main(String[] args) {
 380  0
       CRONOMETER.configureLogger();
 381  
       // Test -- list all properties
 382  0
       CRONConfiguration cfg = getInstance();
 383  0
       cfg.setWriteOnChange(false);
 384  0
       Properties sysprops = cfg.systemProperties;
 385  0
       Properties userprops = cfg.userProperties;
 386  
 
 387  0
       cfg.setProperty("datasource.thisshouldnotwork", "BAD!");
 388  
 
 389  0
       System.out.println("System properties:");
 390  0
       for (Iterator siter = sysprops.keySet().iterator(); siter.hasNext();) {
 391  0
          String key = (String) siter.next();
 392  0
          System.out.println(key + "=" + sysprops.getProperty(key, "no value"));
 393  0
       }
 394  
       
 395  0
       System.out.println("User properties:");
 396  0
       for (Iterator iter = userprops.keySet().iterator(); iter.hasNext();) {
 397  0
          String key = (String) iter.next();
 398  0
          System.out.println(key + "=" + userprops.getProperty(key, "no value"));
 399  0
       }
 400  0
    }
 401  
 
 402  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.