You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ode.apache.org by mi...@apache.org on 2009/06/04 20:12:03 UTC

svn commit: r781820 - in /ode/branches/APACHE_ODE_1.X: ./ utils/src/main/java/org/apache/ode/utils/ utils/src/test/java/org/apache/ode/utils/ utils/src/test/resources/

Author: midon
Date: Thu Jun  4 18:12:03 2009
New Revision: 781820

URL: http://svn.apache.org/viewvc?rev=781820&view=rev
Log:
ODE-619: support placeholder, system properties and environment variables

Modified:
    ode/branches/APACHE_ODE_1.X/Rakefile
    ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java
    ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/SystemUtils.java
    ode/branches/APACHE_ODE_1.X/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java
    ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-1.properties
    ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-2.properties

Modified: ode/branches/APACHE_ODE_1.X/Rakefile
URL: http://svn.apache.org/viewvc/ode/branches/APACHE_ODE_1.X/Rakefile?rev=781820&r1=781819&r2=781820&view=diff
==============================================================================
--- ode/branches/APACHE_ODE_1.X/Rakefile (original)
+++ ode/branches/APACHE_ODE_1.X/Rakefile Thu Jun  4 18:12:03 2009
@@ -521,6 +521,8 @@
   desc "ODE Utils"
   define "utils" do
     compile.with AXIOM, AXIS2_ALL, COMMONS.lang, COMMONS.collections, COMMONS.logging, COMMONS.pool, COMMONS.httpclient, COMMONS.codec, LOG4J, XERCES, JAVAX.stream, WSDL4J, SAXON
+    # env variable requires by HierarchicalPropertiesTest
+    ENV['TEST_DUMMY_ENV_VAR'] = "42"
     test.exclude "*TestResources"
     package :jar
   end

Modified: ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java
URL: http://svn.apache.org/viewvc/ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java?rev=781820&r1=781819&r2=781820&view=diff
==============================================================================
--- ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java (original)
+++ ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/HierarchicalProperties.java Thu Jun  4 18:12:03 2009
@@ -20,11 +20,9 @@
 package org.apache.ode.utils;
 
 import org.apache.commons.collections.map.MultiKeyMap;
-import org.apache.commons.collections.*;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.lang.StringUtils;
-import org.apache.ode.utils.fs.FileUtils;
 
 import javax.xml.namespace.QName;
 import java.io.File;
@@ -32,6 +30,7 @@
 import java.io.IOException;
 import java.util.*;
 import java.util.Properties;
+import java.util.regex.Pattern;
 
 /**
  * This class load a list of regular property files (order matters). The main feature is that property can
@@ -142,53 +141,28 @@
     public void loadFiles() throws IOException {
         // #1. clear all existing content
         clear();
-        // #3. put the root map
-        hierarchicalMap.put(null, null, new ChainedMap());
 
-        for (File file : files) loadFile(file);
-    }
+        // #3. put the root map
+        initRoot();
 
-    private void loadFile(File file) throws IOException {
-        if (!file.exists()) {
-            if (log.isDebugEnabled()) log.debug("File does not exist [" + file + "]");
-            return;
-        }
-        Properties props = new Properties();
-        // #2. read the file
-        FileInputStream fis = new FileInputStream(file);
-        try {
-            if (log.isDebugEnabled()) log.debug("Loading property file: " + file);
-            props.load(fis);
-        } finally {
-            fis.close();
+        for (File file : files) {
+            Properties props = loadFile(file);
+            if(!props.isEmpty()) processProperties(props, file);
         }
+        replacePlaceholders();
+    }
 
-        validatePropertyNames(props, file);
+    private ChainedMap initRoot() {
+        ChainedMap root = new ChainedMap();
+        hierarchicalMap.put(null, null, root);
+        return root;
+    }
 
-        // gather all aliases
-        Map<String, String> nsByAlias = new HashMap<String, String>();
+    private void processProperties(Properties props, File file) throws IOException {
 
-        // replace env variable by their values and collect namespace aliases
-        for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
-            Map.Entry e = (Map.Entry) it.next();
-            String key = (String) e.getKey();
-            String value = (String) e.getValue();
+        validatePropertyNames(props, file);
 
-            // replace any env variables by its value
-            value = SystemUtils.replaceSystemProperties(value);
-            props.put(key, value);
-
-            if (key.startsWith("alias.")) {
-                // we found an namespace alias
-                final String alias = key.substring("alias.".length(), key.length());
-                if (log.isDebugEnabled()) log.debug("Alias found: " + alias + " -> " + value);
-                if (nsByAlias.containsKey(alias) && value.equals(nsByAlias.get(alias)))
-                    throw new RuntimeException("Same alias used twice for 2 different namespaces! file=" + file + ", alias=" + alias);
-                nsByAlias.put(alias, value);
-                // remove the pair from the Properties
-                it.remove();
-            }
-        }
+        Map<String, String> nsByAlias = collectAliases(props, file);
 
         // #4. process each property
 
@@ -240,6 +214,47 @@
         }
     }
 
+    private Properties loadFile(File file) throws IOException {
+        Properties props = new Properties();
+        if (!file.exists()) {
+            if (log.isDebugEnabled()) log.debug("File does not exist [" + file + "]");
+            return props;
+        }
+        // #2. read the file
+        FileInputStream fis = new FileInputStream(file);
+        try {
+            if (log.isDebugEnabled()) log.debug("Loading property file: " + file);
+            props.load(fis);
+        } finally {
+            fis.close();
+        }
+        return props;
+    }
+
+    private Map<String, String> collectAliases(Properties props, File file) {
+        // gather all aliases
+        Map<String, String> nsByAlias = new HashMap<String, String>();
+
+        // replace env variable by their values and collect namespace aliases
+        for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
+            Map.Entry e = (Map.Entry) it.next();
+            String key = (String) e.getKey();
+            String value = (String) e.getValue();
+
+            if (key.startsWith("alias.")) {
+                // we found an namespace alias
+                final String alias = key.substring("alias.".length(), key.length());
+                if (log.isDebugEnabled()) log.debug("Alias found: " + alias + " -> " + value);
+                if (nsByAlias.containsKey(alias) && value.equals(nsByAlias.get(alias)))
+                    throw new RuntimeException("Same alias used twice for 2 different namespaces! file=" + file + ", alias=" + alias);
+                nsByAlias.put(alias, value);
+                // remove the pair from the Properties
+                it.remove();
+            }
+        }
+        return nsByAlias;
+    }
+
     private void validatePropertyNames(Properties props, File file) {
         List invalids = new ArrayList();
         for (Iterator<Object> it = props.keySet().iterator(); it.hasNext();) {
@@ -252,6 +267,22 @@
     }
 
 
+    private void replacePlaceholders() {
+        Pattern systemProperty = Pattern.compile("\\$\\{system\\.([^\\}]+)\\}");
+        Pattern environmentVariable = Pattern.compile("\\$\\{env\\.([^\\}]+)\\}");
+        Pattern localPlaceholder = Pattern.compile("\\$\\{([^\\}]+)\\}");
+        for (Iterator it = hierarchicalMap.values().iterator(); it.hasNext();) {
+            Map properties = ((ChainedMap) it.next()).child;
+            for (Iterator it1 = properties.entrySet().iterator(); it1.hasNext();) {
+                Map.Entry e = (Map.Entry) it1.next();
+                // /!\ replacement values themselves might contain placeholders. So always retrieve the value from the map entry
+                e.setValue(SystemUtils.replaceProperties((String) e.getValue(), localPlaceholder, getRootMap().child));
+                e.setValue(SystemUtils.replaceProperties((String) e.getValue(), systemProperty, System.getProperties()));
+                e.setValue(SystemUtils.replaceProperties((String) e.getValue(), environmentVariable, System.getenv()));
+            }
+        }
+    }
+
     /**
      * Clear all content. If {@link #loadFiles()} is not invoked later, all returned values will be null.
      */
@@ -263,8 +294,7 @@
     protected ChainedMap getRootMap() {
         Object o = hierarchicalMap.get(null, null);
         if (o == null) {
-            o = new ChainedMap();
-            hierarchicalMap.put(null, null, o);
+            o = initRoot();
         }
         return (ChainedMap) o;
     }

Modified: ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/SystemUtils.java
URL: http://svn.apache.org/viewvc/ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/SystemUtils.java?rev=781820&r1=781819&r2=781820&view=diff
==============================================================================
--- ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/SystemUtils.java (original)
+++ ode/branches/APACHE_ODE_1.X/utils/src/main/java/org/apache/ode/utils/SystemUtils.java Thu Jun  4 18:12:03 2009
@@ -21,6 +21,7 @@
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.Map;
 
 /**
  * Extensions for java.lang.System
@@ -147,17 +148,33 @@
 	 * e.g., "The java version is ${java.version}" ==> "The java version is 1.5.0_11"
 	 */
 	public static String replaceSystemProperties(String str) {
+        return replaceProperties(str, PROPERTY_PATTERN, System.getProperties());
+    }
+
+    /**
+     * Match the received string against the given pattern, and replace each match by the value associated to the first group of the match (group(1)).
+     * <br/>If there's no value in the map, no substitution is made.
+     * <p>
+     * There's one constraint on the regex pattern, it should capture at least one group (i.e. match.groupe(1) should not be null). The value of this group is used to retrieved the replacement value from the map.
+     * For instance: pattern = "\\$\\{([^\\}]+)\\}"
+     * @param str
+     * @param pattern
+     * @param values
+     * @return
+     */
+    public static String replaceProperties(String str, Pattern pattern, Map values){
         int start = 0;
 		while (true) {
-			Matcher match = PROPERTY_PATTERN.matcher(str);
+			Matcher match = pattern.matcher(str);
 			if (!match.find(start))
 				break;
 			String property = match.group(1);
-			String value = System.getProperty(property);
+            if(property==null) throw new IllegalArgumentException("Regex pattern must capture at least 1 group! "+pattern.toString());
+			String value = (String) values.get(property);
 			if (value != null) {
 				str = match.replaceFirst(Matcher.quoteReplacement(value));
 			} else {
-				// if the system property doesn't exist, no substitution and skip to next
+				// if the property doesn't exist, no substitution and skip to next
 				start = match.end();
 			}
 		}

Modified: ode/branches/APACHE_ODE_1.X/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java
URL: http://svn.apache.org/viewvc/ode/branches/APACHE_ODE_1.X/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java?rev=781820&r1=781819&r2=781820&view=diff
==============================================================================
--- ode/branches/APACHE_ODE_1.X/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java (original)
+++ ode/branches/APACHE_ODE_1.X/utils/src/test/java/org/apache/ode/utils/HierarchicalPropertiesTest.java Thu Jun  4 18:12:03 2009
@@ -35,11 +35,21 @@
     protected HierarchicalProperties hp;
 
     protected void setUp() throws Exception {
+        // one config step happens in setUp()
+        System.setProperty("TestSystemProperty", "42");
         File[] files = new File[]{new File(getClass().getResource("/hierarchical-2.properties").toURI()),
                 new File(getClass().getResource("/hierarchical-1.properties").toURI())};
         hp = new HierarchicalProperties(files);
     }
 
+    private void checkEnv() {
+        // Java does not let us set ENV variables. The test avriable must be set by the process running this test.
+        if (System.getenv("TEST_DUMMY_ENV_VAR") == null) {
+            fail("Test configuration issue: for testing purposes, the environment variable TEST_DUMMY_ENV_VAR must be set to 42");
+        }
+    }
+
+
     public void testGetProperty() {
         String msg = "Returned value does not match expected value for this property!";
         assertEquals(msg, "30", hp.getProperty("max-redirects"));
@@ -54,9 +64,7 @@
     }
 
     public void testGetProperties() {
-        final List keys = Arrays.asList("timeout", "max-redirects", "ode.a.property.beginning.with.the.prefix.but.no.service");
         Map map = hp.getProperties("http://foo.com", "film-service");
-        assertEquals("Number of properties is wrong", keys.size(), map.size());
         assertEquals("40000", map.get("timeout"));
         assertEquals("30", map.get("max-redirects"));
         assertEquals("so green or red?", map.get("ode.a.property.beginning.with.the.prefix.but.no.service"));
@@ -69,7 +77,7 @@
         assertSame("Snapshot maps should be cached!", hp.getProperties("bla", "unknown-service"), hp.getProperties("bla", "unknown-service"));
     }
 
-    public void testPathHandling(){
+    public void testPathHandling() {
         assertTrue("If the property name ends with '.file' or '.path' its value might be resolved against the file path", FileUtils.isAbsolute(hp.getProperty("http://foo.com", "film-service", "port-of-cannes", "p1.file")));
         assertTrue("If the property name ends with '.file' or '.path' its value might be resolved against the file path", FileUtils.isAbsolute(hp.getProperty("http://foo.com", "film-service", "port-of-cannes", "p1.path")));
         assertEquals("An absolute path should not be altered", "/home/ode/hello.txt", hp.getProperty("http://foo.com", "film-service", "port-of-cannes", "p2.path"));
@@ -88,6 +96,29 @@
         }
     }
 
+    public void testReplaceSystemProperty() {
+        // one config step happens in setUp()
+        assertEquals("${system.*} must be replaced by the corresponding system property.", System.getProperty("TestSystemProperty"), hp.getProperty("http://bar.com", "brel-service", "sys.property"));
+    }
+
+    public void testReplaceEnvVariable() {
+        checkEnv();
+        assertEquals("${env.*} must be replaced by the corresponding environment variable.", System.getenv("TEST_DUMMY_ENV_VAR"), hp.getProperty("http://bar.com", "brel-service", "environment.property"));
+    }
+
+    public void test_placeholder() {
+        assertEquals("A placeholder must be replaced by its value", "placeholder1-value", hp.getProperty("test.placeholder1"));
+    }
+
+    public void test_placeholder_that_uses_sys_prop_and_env_var() {
+        checkEnv();
+        assertEquals("A placeholder can use system properties and/or environment variables", System.getProperty("TestSystemProperty") + "#" + System.getenv("TEST_DUMMY_ENV_VAR"), hp.getProperty("test.placeholder2"));
+    }
+
+    public void test_placeholder_defined_in_2_files() {
+        assertEquals("Last loaded placeholder value must overridden any previous values", "placeholder3-value", hp.getProperty("http://foo.com", "film-service", "port-of-cannes","test.placeholder3"));
+    }
+
     public void testWithNoFile() throws IOException {
         File file = new File("/a-file-that-does-not-exist");
         Map m = new HierarchicalProperties(file).getProperties("an-uri", "a-service", "a-port");

Modified: ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-1.properties
URL: http://svn.apache.org/viewvc/ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-1.properties?rev=781820&r1=781819&r2=781820&view=diff
==============================================================================
--- ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-1.properties (original)
+++ ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-1.properties Thu Jun  4 18:12:03 2009
@@ -32,4 +32,14 @@
 
 foo.film-service.port-of-cannes.ode.p1.path=hello.txt
 foo.film-service.port-of-cannes.ode.p1.file=hello.txt
-foo.film-service.port-of-cannes.ode.p2.path=/home/ode/hello.txt
\ No newline at end of file
+foo.film-service.port-of-cannes.ode.p2.path=/home/ode/hello.txt
+
+
+# test that System properties are properly replaced. The expected value is set by the unit test
+bar.brel-service.ode.sys.property=${system.TestSystemProperty}
+
+# test that System properties are properly replaced. The expected value is set by the unit test
+bar.brel-service.ode.environment.property=${env.TEST_DUMMY_ENV_VAR}
+
+# must override the value set in hierarchical-2.properties
+placeholder3=placeholder3-value
\ No newline at end of file

Modified: ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-2.properties
URL: http://svn.apache.org/viewvc/ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-2.properties?rev=781820&r1=781819&r2=781820&view=diff
==============================================================================
--- ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-2.properties (original)
+++ ode/branches/APACHE_ODE_1.X/utils/src/test/resources/hierarchical-2.properties Thu Jun  4 18:12:03 2009
@@ -19,7 +19,17 @@
 alias.foo2=http://foo.com
 alias.hello=http://hello.com
 
-
 foo2.film-service.port-of-cannes.ode.timeout=60000
 
 hello.a_service.ode.worldproperty=hi!
+
+placeholder1=placeholder1-value
+test.placeholder1=${placeholder1}
+
+# placeholder can use sys property or env vars
+placeholder2=${system.TestSystemProperty}#${env.TEST_DUMMY_ENV_VAR}
+test.placeholder2=${placeholder2}
+
+# must be overridden by the value defined in hierarchical-1.properties
+placeholder3="must be overridden by the value defined in hierarchical-1.properties"
+foo2.film-service.port-of-cannes.ode.test.placeholder3=${placeholder3}