You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2014/11/17 16:23:01 UTC
[1/5] incubator-brooklyn git commit: provide a clean way for
management and driver-mapping customizations
Repository: incubator-brooklyn
Updated Branches:
refs/heads/master 60ccc2cc8 -> cb043e22a
provide a clean way for management and driver-mapping customizations
* custom launchers can customize launch and management
* new driver-naming strategies can be defined and added
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/978d8fd7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/978d8fd7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/978d8fd7
Branch: refs/heads/master
Commit: 978d8fd7d20965b4647ff32dab500a3615dc2b80
Parents: de1d8bc
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Nov 17 11:03:00 2014 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Nov 17 11:03:00 2014 +0000
----------------------------------------------------------------------
.../drivers/BasicEntityDriverManager.java | 8 +
.../drivers/ReflectiveEntityDriverFactory.java | 218 ++++++++++++++++---
.../ReflectiveEntityDriverFactoryTest.java | 58 ++++-
usage/cli/src/main/java/brooklyn/cli/Main.java | 6 +
.../brooklyn/launcher/BrooklynLauncher.java | 12 +
5 files changed, 262 insertions(+), 40 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/978d8fd7/core/src/main/java/brooklyn/entity/drivers/BasicEntityDriverManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/drivers/BasicEntityDriverManager.java b/core/src/main/java/brooklyn/entity/drivers/BasicEntityDriverManager.java
index 6dd92fc..268bee5 100644
--- a/core/src/main/java/brooklyn/entity/drivers/BasicEntityDriverManager.java
+++ b/core/src/main/java/brooklyn/entity/drivers/BasicEntityDriverManager.java
@@ -18,6 +18,8 @@
*/
package brooklyn.entity.drivers;
+import com.google.common.annotations.Beta;
+
import brooklyn.location.Location;
public class BasicEntityDriverManager implements EntityDriverManager {
@@ -30,6 +32,12 @@ public class BasicEntityDriverManager implements EntityDriverManager {
reflective = new ReflectiveEntityDriverFactory();
}
+ /** driver override mechanism; experimental @since 0.7.0 */
+ @Beta
+ public ReflectiveEntityDriverFactory getReflectiveDriverFactory() {
+ return reflective;
+ }
+
public <D extends EntityDriver> void registerDriver(Class<D> driverInterface, Class<? extends Location> locationClazz, Class<? extends D> driverClazz) {
registry.registerDriver(driverInterface, locationClazz, driverClazz);
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/978d8fd7/core/src/main/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactory.java b/core/src/main/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactory.java
index c2f19aa..4a52647 100644
--- a/core/src/main/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactory.java
+++ b/core/src/main/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactory.java
@@ -19,70 +19,216 @@
package brooklyn.entity.drivers;
import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import brooklyn.location.Location;
import brooklyn.location.basic.SshMachineLocation;
-
-import com.google.common.base.Throwables;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.ReferenceWithError;
+import brooklyn.util.text.Strings;
/**
- * Follows a class naming convention: the driver interface must end in "Driver", and the implementation
+ * Follows a class naming convention: the driver interface typically ends in "Driver", and the implementation
* must match the driver interface name but with a suffix like "SshDriver" instead of "Driver".
+ * Other rules can be added using {@link #addRule(String, DriverInferenceRule)} or
+ * {@link #addClassFullNameMapping(String, String)}.
+ * <p>
+ * Reflectively instantiates and returns the driver, based on the location passed in,
+ * in {@link #build(DriverDependentEntity, Location)}.
*
- * Reflectively instantiates and returns the driver, based on the location passed in.
- *
- * @author Peter Veentjer
+ * @author Peter Veentjer, Alex Heneveld
*/
public class ReflectiveEntityDriverFactory {
private static final Logger LOG = LoggerFactory.getLogger(ReflectiveEntityDriverFactory.class);
- public <D extends EntityDriver> D build(DriverDependentEntity<D> entity, Location location){
- Class<D> driverInterface = entity.getDriverInterface();
- Class<? extends D> driverClass;
- if (driverInterface.isInterface()) {
- String driverClassName = inferClassName(driverInterface, location);
+ /** Rules, keyed by a unique identifier. Executed in order of most-recently added first. */
+ protected final Map<String,DriverInferenceRule> rules = MutableMap.of();
+
+ public ReflectiveEntityDriverFactory() {
+ addRule(DriverInferenceForSshLocation.DEFAULT_IDENTIFIER, new DriverInferenceForSshLocation());
+ }
+
+ public interface DriverInferenceRule {
+ public <D extends EntityDriver> ReferenceWithError<Class<? extends D>> resolve(DriverDependentEntity<D> entity, Class<D> driverInterface, Location location);
+ }
+
+ public static abstract class AbstractDriverInferenceRule implements DriverInferenceRule {
+
+ @Override
+ public <D extends EntityDriver> ReferenceWithError<Class<? extends D>> resolve(DriverDependentEntity<D> entity, Class<D> driverInterface, Location location) {
try {
- driverClass = (Class<? extends D>) entity.getClass().getClassLoader().loadClass(driverClassName);
- } catch (ClassNotFoundException e) {
- throw Throwables.propagate(e);
+ String newName = inferDriverClassName(entity, driverInterface, location);
+ if (newName==null) return null;
+
+ return loadDriverClass(newName, entity, driverInterface);
+
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ return ReferenceWithError.newInstanceThrowingError(null, e);
}
- } else {
- driverClass = driverInterface;
}
- Constructor<? extends D> constructor = getConstructor(driverClass);
- try {
- constructor.setAccessible(true);
- return constructor.newInstance(entity, location);
- } catch (InstantiationException e) {
- throw Throwables.propagate(e);
- } catch (IllegalAccessException e) {
- throw Throwables.propagate(e);
- } catch (InvocationTargetException e) {
- throw Throwables.propagate(e);
+ public abstract <D extends EntityDriver> String inferDriverClassName(DriverDependentEntity<D> entity, Class<D> driverInterface, Location location);
+
+ protected <D extends EntityDriver> ReferenceWithError<Class<? extends D>> loadDriverClass(String className, DriverDependentEntity<D> entity, Class<D> driverInterface) {
+ ReferenceWithError<Class<? extends D>> r1 = loadClass(className, entity.getClass().getClassLoader());
+ if (!r1.hasError()) return r1;
+ ReferenceWithError<Class<? extends D>> r2 = loadClass(className, driverInterface.getClass().getClassLoader());
+ if (!r2.hasError()) return r2;
+ return r1;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected <D extends EntityDriver> ReferenceWithError<Class<? extends D>> loadClass(String className, ClassLoader classLoader) {
+ try {
+ return (ReferenceWithError<Class<? extends D>>)(ReferenceWithError) ReferenceWithError.newInstanceWithoutError((Class<? extends EntityDriver>)classLoader.loadClass(className));
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ return ReferenceWithError.newInstanceThrowingError(null, e);
+ }
}
}
+
+ public static abstract class AbstractDriverInferenceRenamingInferenceRule extends AbstractDriverInferenceRule {
+
+ protected final String expectedPattern;
+ protected final String replacement;
- private String inferClassName(Class<? extends EntityDriver> driverInterface, Location location) {
- String driverInterfaceName = driverInterface.getName();
+ public AbstractDriverInferenceRenamingInferenceRule(String expectedPattern, String replacement) {
+ this.expectedPattern = expectedPattern;
+ this.replacement = replacement;
+ }
- if (location instanceof SshMachineLocation) {
+ public String getIdentifier() {
+ return getClass().getName()+"["+expectedPattern+"]";
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName()+"["+expectedPattern+"->"+replacement+"]";
+ }
+ }
+
+ public static class DriverInferenceByRenamingClassFullName extends AbstractDriverInferenceRenamingInferenceRule {
+
+ public DriverInferenceByRenamingClassFullName(String expectedClassFullName, String newClassFullName) {
+ super(expectedClassFullName, newClassFullName);
+ }
+
+ @Override
+ public <D extends EntityDriver> String inferDriverClassName(DriverDependentEntity<D> entity, Class<D> driverInterface, Location location) {
+ if (driverInterface.getName().equals(expectedPattern)) {
+ return replacement;
+ }
+ return null;
+ }
+ }
+
+ public static class DriverInferenceByRenamingClassSimpleName extends AbstractDriverInferenceRenamingInferenceRule {
+
+ public DriverInferenceByRenamingClassSimpleName(String expectedClassSimpleName, String newClassSimpleName) {
+ super(expectedClassSimpleName, newClassSimpleName);
+ }
+
+ @Override
+ public <D extends EntityDriver> String inferDriverClassName(DriverDependentEntity<D> entity, Class<D> driverInterface, Location location) {
+ if (driverInterface.getSimpleName().equals(expectedPattern)) {
+ // i'd like to do away with drivers altogether, but if people *really* need to use this and suppress the warning,
+ // they can use the full class rename
+ LOG.warn("Using discouraged driver simple class rename to find "+replacement+" for "+expectedPattern+"; it is recommended to set getDriverInterface() or newDriver() appropriately");
+ return Strings.removeFromEnd(driverInterface.getName(), expectedPattern)+replacement;
+ }
+ return null;
+ }
+ }
+
+ public static class DriverInferenceForSshLocation extends AbstractDriverInferenceRule {
+
+ public static final String DEFAULT_IDENTIFIER = "ssh-location-driver-inference-rule";
+
+ @Override
+ public <D extends EntityDriver> String inferDriverClassName(DriverDependentEntity<D> entity, Class<D> driverInterface, Location location) {
+ String driverInterfaceName = driverInterface.getName();
+ if (!(location instanceof SshMachineLocation)) return null;
if (!driverInterfaceName.endsWith("Driver")) {
- throw new RuntimeException(String.format("Driver name [%s] doesn't end with 'Driver'",driverInterfaceName));
+ throw new IllegalArgumentException(String.format("Driver name [%s] doesn't end with 'Driver'; cannot auto-detect SshDriver class name", driverInterfaceName));
}
+ return Strings.removeFromEnd(driverInterfaceName, "Driver")+"SshDriver";
+ }
+ }
- return driverInterfaceName.substring(0, driverInterfaceName.length()-"Driver".length())+"SshDriver";
+ /** adds a rule; possibly replacing an old one if one exists with the given identifier. the new rule is added after all previous ones.
+ * @return the replaced rule, or null if there was no old rule */
+ public DriverInferenceRule addRule(String identifier, DriverInferenceRule rule) {
+ DriverInferenceRule oldRule = rules.remove(identifier);
+ rules.put(identifier, rule);
+ LOG.debug("Added driver mapping rule "+rule);
+ return oldRule;
+ }
+
+ public DriverInferenceRule addClassFullNameMapping(String expectedClassFullName, String newClassFullName) {
+ DriverInferenceByRenamingClassFullName rule = new DriverInferenceByRenamingClassFullName(expectedClassFullName, newClassFullName);
+ return addRule(rule.getIdentifier(), rule);
+ }
+
+ public DriverInferenceRule addClassSimpleNameMapping(String expectedClassSimpleName, String newClassSimpleName) {
+ DriverInferenceByRenamingClassSimpleName rule = new DriverInferenceByRenamingClassSimpleName(expectedClassSimpleName, newClassSimpleName);
+ return addRule(rule.getIdentifier(), rule);
+ }
+
+ public <D extends EntityDriver> D build(DriverDependentEntity<D> entity, Location location){
+ Class<D> driverInterface = entity.getDriverInterface();
+ Class<? extends D> driverClass = null;
+ List<Throwable> exceptions = MutableList.of();
+ if (driverInterface.isInterface()) {
+ List<DriverInferenceRule> ruleListInExecutionOrder = MutableList.copyOf(rules.values());
+ Collections.reverse(ruleListInExecutionOrder);
+ // above puts rules in order with most recently added first
+ for (DriverInferenceRule rule: ruleListInExecutionOrder) {
+ ReferenceWithError<Class<? extends D>> clazzR = rule.resolve(entity, driverInterface, location);
+ if (clazzR!=null) {
+ if (!clazzR.hasError()) {
+ Class<? extends D> clazz = clazzR.get();
+ if (clazz!=null) {
+ driverClass = clazz;
+ break;
+ }
+ } else {
+ exceptions.add(clazzR.getError());
+ }
+ }
+ }
} else {
- //TODO: Improve
- throw new RuntimeException("Currently only SshMachineLocation is supported, but location="+location+" for driver +"+driverInterface);
+ driverClass = driverInterface;
+ }
+ LOG.debug("Driver for "+driverInterface.getName()+" in "+location+" is: "+driverClass);
+
+ if (driverClass==null) {
+ if (exceptions.isEmpty())
+ throw new RuntimeException("No drivers could be found for "+driverInterface.getName()+"; "
+ + "currently only SshMachineLocation is supported for autodetection (location "+location+")");
+ else throw Exceptions.create("No drivers could be loaded for "+driverInterface.getName()+" in "+location, exceptions);
+ }
+
+ try {
+ Constructor<? extends D> constructor = getConstructor(driverClass);
+ constructor.setAccessible(true);
+ return constructor.newInstance(entity, location);
+ } catch (Exception e) {
+ LOG.warn("Unable to instantiate "+driverClass+" (rethrowing): "+e);
+ throw Exceptions.propagate(e);
}
}
+ @SuppressWarnings("unchecked")
private <D extends EntityDriver> Constructor<D> getConstructor(Class<D> driverClass) {
for (Constructor<?> constructor : driverClass.getConstructors()) {
if (constructor.getParameterTypes().length == 2) {
@@ -90,7 +236,7 @@ public class ReflectiveEntityDriverFactory {
}
}
- //TODO:
- throw new RuntimeException(String.format("Class [%s] has no constructor with 2 arguments",driverClass.getName()));
+ throw new RuntimeException(String.format("Class [%s] has no constructor with 2 arguments", driverClass.getName()));
}
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/978d8fd7/core/src/test/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactoryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactoryTest.java b/core/src/test/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactoryTest.java
index 1eb97ab..a4a80f1 100644
--- a/core/src/test/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactoryTest.java
+++ b/core/src/test/java/brooklyn/entity/drivers/ReflectiveEntityDriverFactoryTest.java
@@ -35,11 +35,13 @@ public class ReflectiveEntityDriverFactoryTest {
private ReflectiveEntityDriverFactory factory;
private SshMachineLocation sshLocation;
-
+ private DriverDependentEntity<MyDriver> entity;
+
@BeforeMethod
public void setUp() throws Exception {
factory = new ReflectiveEntityDriverFactory();
sshLocation = new SshMachineLocation(MutableMap.of("address", "localhost"));
+ entity = new MyDriverDependentEntity<MyDriver>(MyDriver.class);
}
@AfterMethod
@@ -47,13 +49,55 @@ public class ReflectiveEntityDriverFactoryTest {
// nothing to tear down; no management context created
}
+ protected void assertDriverIs(Class<?> clazz) {
+ MyDriver driver = factory.build(entity, sshLocation);
+ assertTrue(driver.getClass().equals(clazz), "driver="+driver+"; should be "+clazz);
+ }
+
@Test
public void testInstantiatesSshDriver() throws Exception {
- DriverDependentEntity<MyDriver> entity = new MyDriverDependentEntity<MyDriver>(MyDriver.class);
- MyDriver driver = factory.build(entity, sshLocation);
- assertTrue(driver instanceof MySshDriver, "driver="+driver);
+ assertDriverIs(MySshDriver.class);
+ }
+
+ @Test
+ public void testFullNameMapping() throws Exception {
+ factory.addClassFullNameMapping(MyDriver.class.getName(), MyCustomDriver.class.getName());
+ assertDriverIs(MyCustomDriver.class);
+ }
+
+ @Test
+ public void testFullNameMappingMulti() throws Exception {
+ factory.addClassFullNameMapping(MyDriver.class.getName(), "X");
+ factory.addClassFullNameMapping(MyDriver.class.getName(), MyCustomDriver.class.getName());
+ assertDriverIs(MyCustomDriver.class);
+ }
+
+
+ @Test
+ public void testFullNameMappingFailure1() throws Exception {
+ factory.addClassFullNameMapping(MyDriver.class.getName()+"X", MyCustomDriver.class.getName());
+ assertDriverIs(MySshDriver.class);
+ }
+
+ @Test
+ public void testFullNameMappingFailure2() throws Exception {
+ factory.addClassFullNameMapping(MyDriver.class.getName(), MyCustomDriver.class.getName());
+ factory.addClassFullNameMapping(MyDriver.class.getName(), "X");
+ assertDriverIs(MySshDriver.class);
+ }
+
+ @Test
+ public void testSimpleNameMapping() throws Exception {
+ factory.addClassSimpleNameMapping(MyDriver.class.getSimpleName(), MyCustomDriver.class.getSimpleName());
+ assertDriverIs(MyCustomDriver.class);
}
+ @Test
+ public void testSimpleNameMappingFailure() throws Exception {
+ factory.addClassSimpleNameMapping(MyDriver.class.getSimpleName()+"X", MyCustomDriver.class.getSimpleName());
+ assertDriverIs(MySshDriver.class);
+ }
+
public static class MyDriverDependentEntity<D extends EntityDriver> extends AbstractEntity implements DriverDependentEntity<D> {
private final Class<D> clazz;
@@ -89,4 +133,10 @@ public class ReflectiveEntityDriverFactoryTest {
throw new UnsupportedOperationException();
}
}
+
+ public static class MyCustomDriver extends MySshDriver {
+ public MyCustomDriver(Entity entity, SshMachineLocation machine) {
+ super(entity, machine);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/978d8fd7/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index 3eb7fc0..8b9480a 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -379,6 +379,8 @@ public class Main extends AbstractMain {
computeAndSetApp(launcher, utils, loader);
+ customize(launcher);
+
} catch (FatalConfigurationRuntimeException e) {
throw e;
} catch (Exception e) {
@@ -424,6 +426,10 @@ public class Main extends AbstractMain {
return null;
}
+ /** can be overridden by subclasses which need to customize the launcher and/or management */
+ protected void customize(BrooklynLauncher launcher) {
+ }
+
protected void computeLocations() {
boolean hasLocations = !Strings.isBlank(locations);
if (app != null) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/978d8fd7/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
index 976db4c..64326c9 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
@@ -101,6 +101,7 @@ import brooklyn.util.text.Strings;
import brooklyn.util.time.Duration;
import brooklyn.util.time.Time;
+import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
@@ -155,6 +156,8 @@ public class BrooklynLauncher {
private StopWhichAppsOnShutdown stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED;
+ private Function<ManagementContext,Void> customizeManagement = null;
+
private PersistMode persistMode = PersistMode.DISABLED;
private HighAvailabilityMode highAvailabilityMode = HighAvailabilityMode.DISABLED;
private String persistenceDir;
@@ -402,6 +405,11 @@ public class BrooklynLauncher {
return this;
}
+ public BrooklynLauncher customizeManagement(Function<ManagementContext,Void> customizeManagement) {
+ this.customizeManagement = customizeManagement;
+ return this;
+ }
+
public BrooklynLauncher shutdownOnExit(boolean val) {
LOG.warn("Call to deprecated `shutdownOnExit`", new Throwable("source of deprecated call"));
stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED;
@@ -638,6 +646,10 @@ public class BrooklynLauncher {
brooklynProperties = ((ManagementContextInternal)managementContext).getBrooklynProperties();
brooklynProperties.addFromMap(brooklynAdditionalProperties);
}
+
+ if (customizeManagement!=null) {
+ customizeManagement.apply(managementContext);
+ }
}
private boolean fileExists(String file) {
[2/5] incubator-brooklyn git commit: fix warning when deleting files
Posted by he...@apache.org.
fix warning when deleting files
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/bf14bb51
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/bf14bb51
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/bf14bb51
Branch: refs/heads/master
Commit: bf14bb51c035765bc1cbd8f2b075780b714a3f7c
Parents: 978d8fd
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Nov 17 11:06:00 2014 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Nov 17 11:07:55 2014 +0000
----------------------------------------------------------------------
.../rebind/persister/FileBasedStoreObjectAccessor.java | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bf14bb51/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessor.java b/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessor.java
index 9a74cca..acaf660 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessor.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessor.java
@@ -97,10 +97,15 @@ public class FileBasedStoreObjectAccessor implements PersistenceObjectStore.Stor
@Override
public void delete() {
if (!file.delete()) {
- LOG.error("Unable to delete " + file.getAbsolutePath() + ". Probably still locked.");
+ if (!file.exists()) {
+ LOG.debug("Unable to delete " + file.getAbsolutePath() + ". Probably did not exist.");
+ } else {
+ LOG.warn("Unable to delete " + file.getAbsolutePath() + ". Probably still locked.");
+ }
}
- if (!tmpFile.delete()) {
- LOG.error("Unable to delete " + tmpFile.getAbsolutePath() + ". Probably still locked.");
+ if (tmpFile.exists() && !tmpFile.delete()) {
+ // tmpFile is probably already deleted, so don't even log debug if it does not exist
+ LOG.warn("Unable to delete " + tmpFile.getAbsolutePath() + ". Probably still locked.");
}
}
[4/5] incubator-brooklyn git commit: clean up ServerResource export
persistence folder
Posted by he...@apache.org.
clean up ServerResource export persistence folder
previously made lots of messy web-persistence-XXX folders under ~/.brooklyn;
now there is a single `tmp` in there
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/5a5773e5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/5a5773e5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/5a5773e5
Branch: refs/heads/master
Commit: 5a5773e5bb2d94a2e78fd59e33eb12025fc70849
Parents: 3573990
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Nov 17 13:54:35 2014 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Nov 17 13:57:27 2014 +0000
----------------------------------------------------------------------
.../brooklyn/rest/resources/ServerResource.java | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5a5773e5/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
index 2a4a8c5..04424d4 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
@@ -19,6 +19,7 @@
package brooklyn.rest.resources;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -61,6 +62,7 @@ import brooklyn.util.ResourceUtils;
import brooklyn.util.exceptions.Exceptions;
import brooklyn.util.file.ArchiveBuilder;
import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.os.Os;
import brooklyn.util.text.Identifiers;
import brooklyn.util.text.Strings;
import brooklyn.util.time.CountdownTimer;
@@ -303,20 +305,32 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ALL_SERVER_INFO, null))
throw WebResourceUtils.unauthorized("User '%s' is not authorized for this operation", Entitlements.getEntitlementContext().user());
+ File dir = null;
try {
String label = mgmt().getManagementNodeId()+"-"+Time.makeDateSimpleStampString();
PersistenceObjectStore targetStore = BrooklynPersistenceUtils.newPersistenceObjectStore(mgmt(), null,
- "web-persistence-"+label+"-"+Identifiers.makeRandomId(4));
+ "tmp/web-persistence-"+label+"-"+Identifiers.makeRandomId(4));
+ dir = ((FileBasedObjectStore)targetStore).getBaseDir();
+ // only register the parent dir because that will prevent leaks for the random ID
+ Os.deleteOnExitEmptyParentsUpTo(dir.getParentFile(), dir.getParentFile());
BrooklynPersistenceUtils.writeMemento(mgmt(), targetStore, preferredOrigin);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ArchiveBuilder.zip().addDirContentsAt( ((FileBasedObjectStore)targetStore).getBaseDir(), ((FileBasedObjectStore)targetStore).getBaseDir().getName() ).stream(baos);
+ Os.deleteRecursively(dir);
String filename = "brooklyn-state-"+label+".zip";
return Response.ok(baos.toByteArray(), MediaType.APPLICATION_OCTET_STREAM_TYPE)
.header("Content-Disposition","attachment; filename = "+filename)
.build();
} catch (Exception e) {
log.warn("Unable to serve persistence data (rethrowing): "+e, e);
+ if (dir!=null) {
+ try {
+ Os.deleteRecursively(dir);
+ } catch (Exception e2) {
+ log.warn("Ignoring error deleting '"+dir+"' after another error, throwing original error ("+e+"); ignored error deleting is: "+e2);
+ }
+ }
throw Exceptions.propagate(e);
}
}
[3/5] incubator-brooklyn git commit: support config key for osgi
cache dir, which now defaults to under mgmt base dir
Posted by he...@apache.org.
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 <al...@cloudsoftcorp.com>
Authored: Mon Nov 17 13:44:23 2014 +0000
Committer: Alex Heneveld <al...@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");
+ }
+ }
+
}
[5/5] incubator-brooklyn git commit: This closes #340
Posted by he...@apache.org.
This closes #340
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/cb043e22
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/cb043e22
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/cb043e22
Branch: refs/heads/master
Commit: cb043e22a349b95e4963adcfe4175b5d5a649152
Parents: 60ccc2c 5a5773e
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Nov 17 15:22:49 2014 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Nov 17 15:22:49 2014 +0000
----------------------------------------------------------------------
.../brooklyn/config/BrooklynServerConfig.java | 12 +
.../brooklyn/config/BrooklynServerPaths.java | 43 +++-
.../drivers/BasicEntityDriverManager.java | 8 +
.../drivers/ReflectiveEntityDriverFactory.java | 218 ++++++++++++++++---
.../persister/FileBasedStoreObjectAccessor.java | 11 +-
.../brooklyn/management/ha/OsgiManager.java | 29 ++-
.../internal/LocalManagementContext.java | 2 +-
.../brooklyn/util/text/TemplateProcessor.java | 138 ++++++++++--
.../ReflectiveEntityDriverFactoryTest.java | 58 ++++-
.../brooklyn/management/osgi/OsgiPathTest.java | 105 +++++++++
.../util/text/TemplateProcessorTest.java | 40 +++-
usage/cli/src/main/java/brooklyn/cli/Main.java | 6 +
.../brooklyn/launcher/BrooklynLauncher.java | 12 +
.../brooklyn/rest/resources/ServerResource.java | 16 +-
14 files changed, 619 insertions(+), 79 deletions(-)
----------------------------------------------------------------------