brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [3/5] incubator-brooklyn git commit: support config key for osgi cache dir, which now defaults to under mgmt base dir
Date Mon, 17 Nov 2014 15:23:03 GMT
support config key for osgi cache dir, which now defaults to under mgmt base dir

running in /tmp is not recommended. we attempt as best we can to delete it if it looks transient,
but on `kill -9` there may be junk leftover under osgi/cache/


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/35739901
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/35739901
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/35739901

Branch: refs/heads/master
Commit: 3573990142b8e045b95ec7f219576c5145b5a97b
Parents: bf14bb5
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Mon Nov 17 13:44:23 2014 +0000
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Mon Nov 17 13:44:23 2014 +0000

----------------------------------------------------------------------
 .../brooklyn/config/BrooklynServerConfig.java   |  12 ++
 .../brooklyn/config/BrooklynServerPaths.java    |  43 +++++-
 .../brooklyn/management/ha/OsgiManager.java     |  29 ++--
 .../internal/LocalManagementContext.java        |   2 +-
 .../brooklyn/util/text/TemplateProcessor.java   | 138 +++++++++++++++++--
 .../brooklyn/management/osgi/OsgiPathTest.java  | 105 ++++++++++++++
 .../util/text/TemplateProcessorTest.java        |  40 ++++--
 7 files changed, 334 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35739901/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
index 4fdf4c4..621dc10 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
@@ -109,8 +109,19 @@ public class BrooklynServerConfig {
             "The mode the management context should use to load the catalog when first starting",
             CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL);
 
+    /** string used in places where the management node ID is needed to resolve a path */
+    public static final String MANAGEMENT_NODE_ID_PROPERTY = "brooklyn.mgmt.node.id";
+    
     public static final ConfigKey<Boolean> USE_OSGI = ConfigKeys.newBooleanConfigKey("brooklyn.osgi.enabled",
         "Whether OSGi is enabled, defaulting to true", true);
+    public static final ConfigKey<String> OSGI_CACHE_DIR = ConfigKeys.newStringConfigKey("brooklyn.osgi.cache.dir",
+        "Directory to use for OSGi cache, potentially including Freemarker template variables
"
+        + "${"+MGMT_BASE_DIR.getName()+"} (which is the default for relative paths), "
+        + "${"+Os.TmpDirFinder.BROOKLYN_OS_TMPDIR_PROPERTY+"} if it should be in the tmp
dir space,  "
+        + "and ${"+MANAGEMENT_NODE_ID_PROPERTY+"} to include the management node ID (recommended
if running multiple OSGi paths)",
+        "osgi/cache/${"+MANAGEMENT_NODE_ID_PROPERTY+"}/");
+    public static final ConfigKey<Boolean> OSGI_CACHE_CLEAN = ConfigKeys.newBooleanConfigKey("brooklyn.osgi.cache.clean",
+        "Whether to delete the OSGi directory before and after use; if unset, it will delete
if the node ID forms part of the cache dir path (which by default it does) to avoid file leaks");
 
     public static final ConfigKey<CampPlatform> CAMP_PLATFORM = ConfigKeys.newConfigKey(CampPlatform.class,
"brooklyn.camp.platform",
         "Config set at brooklyn management platform to find the CampPlatform instance (bi-directional)");
@@ -118,6 +129,7 @@ public class BrooklynServerConfig {
     public static final AttributeSensor<ManagementContext.PropertiesReloadListener>
PROPERTIES_RELOAD_LISTENER = Sensors.newSensor(
             ManagementContext.PropertiesReloadListener.class, "brooklyn.management.propertiesReloadListenet",
"Properties reload listener");
 
+
     /** @see BrooklynServerPaths#getMgmtBaseDir(ManagementContext) */
     public static String getMgmtBaseDir(ManagementContext mgmt) {
         return BrooklynServerPaths.getMgmtBaseDir(mgmt);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35739901/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynServerPaths.java b/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
index 06c8033..ae56d96 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServerPaths.java
@@ -29,12 +29,14 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.management.ManagementContext;
 import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
+import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.time.Time;
 
 import com.google.common.base.Objects;
@@ -234,6 +236,45 @@ public class BrooklynServerPaths {
         }
     }
 
-    // TODO OSGi
+    public static File getOsgiCacheDir(ManagementContext mgmt) {
+        StringConfigMap brooklynProperties = mgmt.getConfig();
+        String cacheDir = brooklynProperties.getConfig(BrooklynServerConfig.OSGI_CACHE_DIR);
+        
+        // note dir should be different for each instance if starting multiple instances
+        // hence default including management node ID
+        
+        cacheDir = TemplateProcessor.processTemplateContents(cacheDir, (ManagementContextInternal)mgmt,

+            MutableMap.of(BrooklynServerConfig.MGMT_BASE_DIR.getName(), getMgmtBaseDir(mgmt),
+                BrooklynServerConfig.MANAGEMENT_NODE_ID_PROPERTY, mgmt.getManagementNodeId(),
+                Os.TmpDirFinder.BROOKLYN_OS_TMPDIR_PROPERTY, Os.tmp()));
+        cacheDir = resolveAgainstBaseDir(mgmt.getConfig(), cacheDir);
+        
+        return new File(cacheDir);
+    }
+
+    public static boolean isOsgiCacheForCleaning(ManagementContext mgmt, File cacheDir) {
+        StringConfigMap brooklynProperties = mgmt.getConfig();
+        Boolean clean = brooklynProperties.getConfig(BrooklynServerConfig.OSGI_CACHE_CLEAN);
+        if (clean==null) {
+            // as per javadoc on key, clean defaults to iff it is a node-id-specific directory
+            clean = cacheDir.getName().contains(mgmt.getManagementNodeId());
+        }
+        return clean;
+    }
     
+    public static File getOsgiCacheDirCleanedIfNeeded(ManagementContext mgmt) {
+        File cacheDirF = getOsgiCacheDir(mgmt);
+        boolean clean = isOsgiCacheForCleaning(mgmt, cacheDirF);
+        log.debug("OSGi cache dir computed as "+cacheDirF.getName()+" ("+
+            (cacheDirF.exists() ? "already exists" : "does not exist")+", "+
+            (clean ? "cleaning now (and on exit)" : "cleaning not requested"));
+
+        if (clean) {
+            Os.deleteRecursively(cacheDirF);
+            Os.deleteOnExitRecursively(cacheDirF);
+        }
+        
+        return cacheDirF;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35739901/core/src/main/java/brooklyn/management/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
index 1042d1f..386af39 100644
--- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java
+++ b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
@@ -32,7 +32,9 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.config.BrooklynServerConfig;
+import brooklyn.config.BrooklynServerPaths;
 import brooklyn.config.ConfigKey;
+import brooklyn.management.ManagementContext;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
@@ -53,19 +55,24 @@ public class OsgiManager {
     
     /* see Osgis for info on starting framework etc */
     
+    protected ManagementContext mgmt;
     protected Framework framework;
-    protected File osgiTempDir;
+    protected File osgiCacheDir;
 
     // we could manage without this map but it is useful to validate what is a user-supplied
url
     protected Map<String,VersionedName> urlToBundleIdentifier = MutableMap.of();
 
+
+    public OsgiManager(ManagementContext mgmt) {
+        this.mgmt = mgmt;
+    }
+
     public void start() {
         try {
-            // TODO any extra startup args?
-            // TODO dir to come from brooklyn properties;
-            // note dir must be different for each if starting multiple instances
-            osgiTempDir = Os.newTempDir("brooklyn-osgi-cache");
-            framework = Osgis.newFrameworkStarted(osgiTempDir.getAbsolutePath(), false, MutableMap.of());
+            osgiCacheDir = BrooklynServerPaths.getOsgiCacheDirCleanedIfNeeded(mgmt);
+            
+            // any extra OSGi startup args could go here
+            framework = Osgis.newFrameworkStarted(osgiCacheDir.getAbsolutePath(), false,
MutableMap.of());
             
         } catch (Exception e) {
             throw Exceptions.propagate(e);
@@ -83,11 +90,13 @@ public class OsgiManager {
         } catch (InterruptedException e) {
             throw Exceptions.propagate(e);
         }
-        DeletionResult deleteRecursively = Os.deleteRecursively(osgiTempDir);
-        if (deleteRecursively.getThrowable()!=null) {
-            log.debug("Unable to delete "+osgiTempDir+" (possibly already deleted?): "+deleteRecursively.getThrowable());
+        if (BrooklynServerPaths.isOsgiCacheForCleaning(mgmt, osgiCacheDir)) {
+            DeletionResult deleteRecursively = Os.deleteRecursively(osgiCacheDir);
+            if (deleteRecursively.getThrowable()!=null) {
+                log.debug("Unable to delete "+osgiCacheDir+" (possibly already deleted?):
"+deleteRecursively.getThrowable());
+            }
         }
-        osgiTempDir = null;
+        osgiCacheDir = null;
         framework = null;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35739901/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java b/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
index 098e3af..257e1f6 100644
--- a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
@@ -186,7 +186,7 @@ public class LocalManagementContext extends AbstractManagementContext
{
         this.usageManager = new LocalUsageManager(this);
         
         if (configMap.getConfig(OsgiManager.USE_OSGI)) {
-            this.osgiManager = new OsgiManager();
+            this.osgiManager = new OsgiManager(this);
             osgiManager.start();
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35739901/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/text/TemplateProcessor.java b/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
index 25554a7..f6c35c4 100644
--- a/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
+++ b/core/src/main/java/brooklyn/util/text/TemplateProcessor.java
@@ -28,11 +28,14 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.entity.Entity;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.drivers.EntityDriver;
+import brooklyn.location.Location;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 
 import com.google.common.base.Charsets;
@@ -47,11 +50,30 @@ import freemarker.template.TemplateHashModel;
 import freemarker.template.TemplateModel;
 import freemarker.template.TemplateModelException;
 
+/** A variety of methods to assist in Freemarker template processing,
+ * including passing in maps with keys flattened (dot-separated namespace),
+ * and accessing {@link ManagementContext} brooklyn.properties 
+ * and {@link Entity}, {@link EntityDriver}, and {@link Location} methods and config.
+ * <p>
+ * See {@link #processTemplateContents(String, ManagementContextInternal, Map)} for
+ * a description of how management access is done.
+ */
 public class TemplateProcessor {
 
     private static final Logger log = LoggerFactory.getLogger(TemplateProcessor.class);
 
+    protected static TemplateModel wrapAsTemplateModel(Object o) throws TemplateModelException
{
+        if (o instanceof Map) return new DotSplittingTemplateModel((Map<?,?>)o);
+        return ObjectWrapper.DEFAULT_WRAPPER.wrap(o);
+    }
+    
+    /** @deprecated since 0.7.0 use {@link #processTemplateFile(String, Map)} */ @Deprecated
     public static String processTemplate(String templateFileName, Map<String, ? extends
Object> substitutions) {
+        return processTemplateFile(templateFileName, substitutions);
+    }
+    
+    /** As per {@link #processTemplateContents(String, Map)}, but taking a file. */
+    public static String processTemplateFile(String templateFileName, Map<String, ? extends
Object> substitutions) {
         String templateContents;
         try {
             templateContents = Files.toString(new File(templateFileName), Charsets.UTF_8);
@@ -62,7 +84,13 @@ public class TemplateProcessor {
         return processTemplateContents(templateContents, substitutions);
     }
 
+    /** @deprecated since 0.7.0 use {@link #processTemplateFile(String, EntityDriver, Map)}
*/ @Deprecated
     public static String processTemplate(String templateFileName, EntityDriver driver, Map<String,
? extends Object> extraSubstitutions) {
+        return processTemplateFile(templateFileName, driver, extraSubstitutions);
+    }
+    
+    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
+    public static String processTemplateFile(String templateFileName, EntityDriver driver,
Map<String, ? extends Object> extraSubstitutions) {
         String templateContents;
         try {
             templateContents = Files.toString(new File(templateFileName), Charsets.UTF_8);
@@ -73,14 +101,85 @@ public class TemplateProcessor {
         return processTemplateContents(templateContents, driver, extraSubstitutions);
     }
 
+    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
     public static String processTemplateContents(String templateContents, EntityDriver driver,
Map<String,? extends Object> extraSubstitutions) {
         return processTemplateContents(templateContents, new EntityAndMapTemplateModel(driver,
extraSubstitutions));
     }
 
+    /** Processes template contents according to {@link EntityAndMapTemplateModel}. */
     public static String processTemplateContents(String templateContents, ManagementContextInternal
managementContext, Map<String,? extends Object> extraSubstitutions) {
         return processTemplateContents(templateContents, new EntityAndMapTemplateModel(managementContext,
extraSubstitutions));
     }
 
+    /**
+     * A Freemarker {@link TemplateHashModel} which will correctly handle entries of the
form "a.b" in this map,
+     * matching against template requests for "${a.b}".
+     * <p>
+     * Freemarker requests "a" in a map when given such a request, and expects that to point
to a map
+     * with a key "b". This model provides such maps even for "a.b" in a map.
+     * <p>
+     * However if "a" <b>and</b> "a.b" are in the map, this will <b>not</b>
currently do the deep mapping.
+     * (It does not have enough contextual information from Freemarker to handle this case.)
*/
+    public static final class DotSplittingTemplateModel implements TemplateHashModel {
+        protected final Map<?,?> map;
+
+        protected DotSplittingTemplateModel(Map<?,?> map) {
+            this.map = map;
+        }
+
+        @Override
+        public boolean isEmpty() { return map!=null && map.isEmpty(); }
+
+        public boolean contains(String key) {
+            if (map==null) return false;
+            if (map.containsKey(key)) return true;
+            for (Map.Entry<?,?> entry: map.entrySet()) {
+                String k = Strings.toString(entry.getKey());
+                if (k.startsWith(key+".")) {
+                    // contains this prefix
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        @Override
+        public TemplateModel get(String key) throws TemplateModelException {
+            if (map==null) return null;
+            try {
+                if (map.containsKey(key)) 
+                    return wrapAsTemplateModel( map.get(key) );
+                
+                Map<String,Object> result = MutableMap.of();
+                for (Map.Entry<?,?> entry: map.entrySet()) {
+                    String k = Strings.toString(entry.getKey());
+                    if (k.startsWith(key+".")) {
+                        String k2 = Strings.removeFromStart(k, key+".");
+                        result.put(k2, entry.getValue());
+                    }
+                }
+                if (!result.isEmpty()) 
+                        return wrapAsTemplateModel( result );
+                
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                throw new IllegalStateException("Error accessing config '"+key+"'"+": "+e,
e);
+            }
+            
+            return null;
+        }
+        
+        @Override
+        public String toString() {
+            return getClass().getName()+"["+map+"]";
+        }
+    }
+    
+    /** FreeMarker {@link TemplateHashModel} which resolves keys inside the given entity
or management context.
+     * Callers are required to include dots for dot-separated keys.
+     * Freemarker will only due this when in inside bracket notation in an outer map, as
in <code>${outer['a.b.']}</code>; 
+     * as a result this is intended only for use by {@link EntityAndMapTemplateModel} where

+     * a caller has used bracked notation, as in <code>${mgmt['key.subkey']}</code>.
*/
     protected static final class EntityConfigTemplateModel implements TemplateHashModel {
         protected final EntityInternal entity;
         protected final ManagementContext mgmt;
@@ -109,7 +208,7 @@ public class TemplateProcessor {
                     result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
                 
                 if (result!=null)
-                    return ObjectWrapper.DEFAULT_WRAPPER.wrap( result );
+                    return wrapAsTemplateModel( result );
                 
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);
@@ -126,31 +225,38 @@ public class TemplateProcessor {
         }
     }
 
+    /**
+     * Provides access to config on an entity or management context, using
+     * <code>${config['entity.config.key']}</code> or <code>${mgmt['brooklyn.properties.key']}</code>
notation,
+     * and also allowing access to <code>getX()</code> methods on entity (interface)
or driver
+     * using <code>${entity.x}</code> or <code><${driver.x}</code>.
+     * Optional extra properties can be supplied, treated as per {@link DotSplittingTemplateModel}.
+     */
     protected static final class EntityAndMapTemplateModel implements TemplateHashModel {
         protected final EntityInternal entity;
         protected final EntityDriver driver;
         protected final ManagementContext mgmt;
-        protected final Map<String,? extends Object> extraSubstitutions;
+        protected final DotSplittingTemplateModel extraSubstitutionsModel;
 
         protected EntityAndMapTemplateModel(ManagementContext mgmt, Map<String,? extends
Object> extraSubstitutions) {
             this.entity = null;
             this.driver = null;
             this.mgmt = mgmt;
-            this.extraSubstitutions = extraSubstitutions;
+            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
         }
 
         protected EntityAndMapTemplateModel(EntityDriver driver, Map<String,? extends
Object> extraSubstitutions) {
             this.driver = driver;
             this.entity = (EntityInternal) driver.getEntity();
             this.mgmt = entity.getManagementContext();
-            this.extraSubstitutions = extraSubstitutions;
+            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
         }
 
         protected EntityAndMapTemplateModel(EntityInternal entity, Map<String,? extends
Object> extraSubstitutions) {
             this.entity = entity;
             this.driver = null;
             this.mgmt = entity.getManagementContext();
-            this.extraSubstitutions = extraSubstitutions;
+            this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
         }
 
         @Override
@@ -158,11 +264,11 @@ public class TemplateProcessor {
 
         @Override
         public TemplateModel get(String key) throws TemplateModelException {
-            if (extraSubstitutions!=null && extraSubstitutions.containsKey(key))
-                return ObjectWrapper.DEFAULT_WRAPPER.wrap( extraSubstitutions.get(key) );
+            if (extraSubstitutionsModel.contains(key))
+                return wrapAsTemplateModel( extraSubstitutionsModel.get(key) );
 
             if ("entity".equals(key) && entity!=null)
-                return ObjectWrapper.DEFAULT_WRAPPER.wrap( entity );
+                return wrapAsTemplateModel( entity );
             if ("config".equals(key)) {
                 if (entity!=null)
                     return new EntityConfigTemplateModel(entity);
@@ -174,12 +280,12 @@ public class TemplateProcessor {
             }
 
             if ("driver".equals(key) && driver!=null)
-                return ObjectWrapper.DEFAULT_WRAPPER.wrap( driver );
+                return wrapAsTemplateModel( driver );
             if ("location".equals(key)) {
                 if (driver!=null && driver.getLocation()!=null)
-                    return ObjectWrapper.DEFAULT_WRAPPER.wrap( driver.getLocation() );
+                    return wrapAsTemplateModel( driver.getLocation() );
                 if (entity!=null)
-                    return ObjectWrapper.DEFAULT_WRAPPER.wrap( Iterables.getOnlyElement(
entity.getLocations() ) );
+                    return wrapAsTemplateModel( Iterables.getOnlyElement( entity.getLocations()
) );
             }
             
             if (mgmt!=null) {
@@ -188,12 +294,12 @@ public class TemplateProcessor {
                 Object result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());
                 if (result!=null) { 
                     log.warn("Deprecated access of global brooklyn.properties value for "+key+";
should be qualified with 'mgmt.'");
-                    return ObjectWrapper.DEFAULT_WRAPPER.wrap( result );
+                    return wrapAsTemplateModel( result );
                 }
             }
             
             if ("javaSysProps".equals(key))
-                return ObjectWrapper.DEFAULT_WRAPPER.wrap( System.getProperties() );
+                return wrapAsTemplateModel( System.getProperties() );
 
             return null;
         }
@@ -204,15 +310,18 @@ public class TemplateProcessor {
         }
     }
 
+    /** Processes template contents with the given items in scope as per {@link EntityAndMapTemplateModel}.
*/
     public static String processTemplateContents(String templateContents, final EntityInternal
entity, Map<String,? extends Object> extraSubstitutions) {
         return processTemplateContents(templateContents, new EntityAndMapTemplateModel(entity,
extraSubstitutions));
     }
     
+    /** Processes template contents using the given map, passed to freemarker,
+     * with dot handling as per {@link DotSplittingTemplateModel}. */
     public static String processTemplateContents(String templateContents, final Map<String,
? extends Object> substitutions) {
         TemplateHashModel root;
         try {
             root = substitutions != null
-                ? (TemplateHashModel)ObjectWrapper.DEFAULT_WRAPPER.wrap(substitutions)
+                ? (TemplateHashModel)wrapAsTemplateModel(substitutions)
                 : null;
         } catch (TemplateModelException e) {
             throw new IllegalStateException("Unable to set up TemplateHashModel to parse
template, given "+substitutions+": "+e, e);
@@ -221,6 +330,7 @@ public class TemplateProcessor {
         return processTemplateContents(templateContents, root);
     }
     
+    /** Processes template contents against the given {@link TemplateHashModel}. */
     public static String processTemplateContents(String templateContents, final TemplateHashModel
substitutions) {
         try {
             Configuration cfg = new Configuration();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35739901/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java
new file mode 100644
index 0000000..f4ac674
--- /dev/null
+++ b/core/src/test/java/brooklyn/management/osgi/OsgiPathTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.management.osgi;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.osgi.framework.BundleException;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.config.BrooklynServerPaths;
+import brooklyn.entity.basic.Entities;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.util.os.Os;
+import brooklyn.util.text.Identifiers;
+
+
+/** 
+ * Tests that OSGi entities load correctly and have the right catalog information set.
+ * Note further tests done elsewhere using CAMP YAML (referring to methods in this class).
+ */
+public class OsgiPathTest {
+   
+    protected LocalManagementContext mgmt = null;
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws BundleException, IOException, InterruptedException {
+        if (mgmt!=null) Entities.destroyAll(mgmt);
+    }
+    
+    @Test(groups="Integration") // integration only because OSGi takes ~200ms
+    public void testOsgiPathDefault() {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
+        Assert.assertTrue(path.startsWith(BrooklynServerPaths.getMgmtBaseDir(mgmt)), path);
+        Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path);
+        
+        assertExistsThenIsCleaned(path);
+    }
+
+    @Test(groups="Integration") // integration only because OSGi takes ~200ms
+    public void testOsgiPathCustom() {
+        BrooklynProperties bp = BrooklynProperties.Factory.newEmpty();
+        String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4);
+        bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"${brooklyn.mgmt.node.id}");
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build();
+        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
+        Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg));
+        
+        Assert.assertTrue(path.startsWith(Os.tmp()), path);
+        Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path);
+        
+        assertExistsThenIsCleaned(path);
+    }
+
+    @Test(groups="Integration") // integration only because OSGi takes ~200ms
+    public void testOsgiPathCustomWithoutNodeIdNotCleaned() {
+        BrooklynProperties bp = BrooklynProperties.Factory.newEmpty();
+        String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4);
+        bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"sample");
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build();
+        String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath();
+        Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg));
+        
+        Assert.assertTrue(path.startsWith(Os.tmp()), path);
+        Assert.assertFalse(path.contains(mgmt.getManagementNodeId()), path);
+        
+        assertExistsThenCorrectCleanedBehaviour(path, false);
+    }
+
+    private void assertExistsThenIsCleaned(String path) {
+        assertExistsThenCorrectCleanedBehaviour(path, true);
+    }
+    private void assertExistsThenCorrectCleanedBehaviour(String path, boolean shouldBeCleanAfterwards)
{
+        Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should exist when
in use");
+        Entities.destroyAll(mgmt);
+        mgmt = null;
+        if (shouldBeCleanAfterwards)
+            Assert.assertFalse(new File(path).exists(), "OSGi cache "+path+" should be cleaned
after");
+        else
+            Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should NOT be
cleaned after");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/35739901/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java b/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
index ef9c41a..030e8c6 100644
--- a/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
+++ b/core/src/test/java/brooklyn/util/text/TemplateProcessorTest.java
@@ -28,6 +28,7 @@ import org.testng.annotations.Test;
 import brooklyn.entity.BrooklynAppUnitTestSupport;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.event.basic.DependentConfiguration;
+import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.test.FixedLocaleTest;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.test.entity.TestEntity;
@@ -109,14 +110,6 @@ public class TemplateProcessorTest extends BrooklynAppUnitTestSupport
{
     }
     
     @Test
-    public void testManagementContextConfigWithDot() {
-        mgmt.getBrooklynProperties().put("global.mykey", "myval");
-        String templateContents = "${mgmt['global.mykey']}";
-        String result = TemplateProcessor.processTemplateContents(templateContents, app,
ImmutableMap.<String,Object>of());
-        assertEquals(result, "myval");
-    }
-    
-    @Test
     public void testManagementContextDefaultValue() {
         String templateContents = "${(missing)!\"defval\"}";
         Object result = TemplateProcessor.processTemplateContents(templateContents, app,
ImmutableMap.<String,Object>of());
@@ -131,9 +124,17 @@ public class TemplateProcessorTest extends BrooklynAppUnitTestSupport
{
     }
     
     @Test
+    public void testManagementContextConfigWithDot() {
+        mgmt.getBrooklynProperties().put("global.mykey", "myval");
+        String templateContents = "${mgmt['global.mykey']}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, app,
ImmutableMap.<String,Object>of());
+        assertEquals(result, "myval");
+    }
+    
+    @Test
     public void testManagementContextErrors() {
         try {
-            // NB: dot has special meaning so this should fail
+            // NB: dot has special meaning so this should fail; must be accessed using bracket
notation as above
             mgmt.getBrooklynProperties().put("global.mykey", "myval");
             String templateContents = "${mgmt.global.mykey}";
             TemplateProcessor.processTemplateContents(templateContents, app, ImmutableMap.<String,Object>of());
@@ -154,4 +155,25 @@ public class TemplateProcessorTest extends BrooklynAppUnitTestSupport
{
         String result = TemplateProcessor.processTemplateContents(templateContents, entity,
ImmutableMap.<String,Object>of());
         assertEquals(result, "myval");
     }
+    
+    @Test
+    public void testDotSeparatedKey() {
+        String templateContents = "${a.b}";
+        String result = TemplateProcessor.processTemplateContents(templateContents, (ManagementContextInternal)null,

+            ImmutableMap.<String,Object>of("a.b", "myval"));
+        assertEquals(result, "myval");
+    }
+    
+    @Test
+    public void testDotSeparatedKeyCollisionFailure() {
+        String templateContents = "${aaa.bbb}";
+        try {
+            TemplateProcessor.processTemplateContents(templateContents, (ManagementContextInternal)null,

+                ImmutableMap.<String,Object>of("aaa.bbb", "myval", "aaa", "blocker"));
+            Assert.fail("Should not have found value with intermediate dot where prefix is
overridden");
+        } catch (Exception e) {
+            Assert.assertTrue(e.toString().contains("aaa"), "Should have mentioned missing
key 'aaa' in error");
+        }
+    }
+
 }


Mime
View raw message