Skip to content

Commit

Permalink
This closes #975
Browse files Browse the repository at this point in the history
  • Loading branch information
aledsage committed Oct 21, 2015
2 parents 238816f + f792fdf commit 9790e8b
Show file tree
Hide file tree
Showing 6 changed files with 586 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ public SshFetchTaskWrapper newTask() {
}
}

/**
* @since 0.9.0
*/
public static SshEffectorTaskFactory<Integer> ssh(SshMachineLocation machine, String ...commands) {
return new SshEffectorTaskFactory<Integer>(machine, commands);
}

public static SshEffectorTaskFactory<Integer> ssh(String ...commands) {
return new SshEffectorTaskFactory<Integer>(commands);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.apache.brooklyn.util.core.text;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
Expand All @@ -33,6 +35,7 @@
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.location.internal.LocationInternal;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
import org.apache.brooklyn.core.sensor.DependentConfiguration;
import org.apache.brooklyn.core.sensor.Sensors;
Expand Down Expand Up @@ -115,6 +118,11 @@ public static String processTemplateContents(String templateContents, Management
return processTemplateContents(templateContents, new EntityAndMapTemplateModel(managementContext, extraSubstitutions));
}

/** Processes template contents according to {@link EntityAndMapTemplateModel}. */
public static String processTemplateContents(String templateContents, Location location, Map<String,? extends Object> extraSubstitutions) {
return processTemplateContents(templateContents, new LocationAndMapTemplateModel((LocationInternal)location, 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}".
Expand Down Expand Up @@ -181,21 +189,95 @@ public String toString() {

/** 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>;
* Freemarker will only do 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;

protected EntityConfigTemplateModel(EntityInternal entity) {
this.entity = entity;
this.entity = checkNotNull(entity, "entity");
this.mgmt = entity.getManagementContext();
}

protected EntityConfigTemplateModel(ManagementContext mgmt) {
this.entity = null;
this.mgmt = mgmt;
@Override
public boolean isEmpty() { return false; }

@Override
public TemplateModel get(String key) throws TemplateModelException {
try {
Object result = entity.getConfig(ConfigKeys.builder(Object.class).name(key).build());

if (result==null)
result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());

if (result!=null)
return wrapAsTemplateModel( result );

} catch (Exception e) {
Exceptions.propagateIfFatal(e);
throw new IllegalStateException("Error accessing config '"+key+"'"+" on "+entity+": "+e, e);
}

return null;
}

@Override
public String toString() {
return getClass().getName()+"["+entity+"]";
}
}

/** FreeMarker {@link TemplateHashModel} which resolves keys inside the given management context.
* Callers are required to include dots for dot-separated keys.
* Freemarker will only do 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 MgmtConfigTemplateModel implements TemplateHashModel {
protected final ManagementContext mgmt;

protected MgmtConfigTemplateModel(ManagementContext mgmt) {
this.mgmt = checkNotNull(mgmt, "mgmt");
}

@Override
public boolean isEmpty() { return false; }

@Override
public TemplateModel get(String key) throws TemplateModelException {
try {
Object result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());

if (result!=null)
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()+"["+mgmt+"]";
}
}

/** FreeMarker {@link TemplateHashModel} which resolves keys inside the given location.
* Callers are required to include dots for dot-separated keys.
* Freemarker will only do 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 LocationAndMapTemplateModel} where
* a caller has used bracked notation, as in <code>${mgmt['key.subkey']}</code>. */
protected static final class LocationConfigTemplateModel implements TemplateHashModel {
protected final LocationInternal location;
protected final ManagementContext mgmt;

protected LocationConfigTemplateModel(LocationInternal location) {
this.location = checkNotNull(location, "location");
this.mgmt = location.getManagementContext();
}

@Override
Expand All @@ -206,8 +288,8 @@ public TemplateModel get(String key) throws TemplateModelException {
try {
Object result = null;

if (entity!=null)
result = entity.getConfig(ConfigKeys.builder(Object.class).name(key).build());
result = location.getConfig(ConfigKeys.builder(Object.class).name(key).build());

if (result==null && mgmt!=null)
result = mgmt.getConfig().getConfig(ConfigKeys.builder(Object.class).name(key).build());

Expand All @@ -217,15 +299,15 @@ public TemplateModel get(String key) throws TemplateModelException {
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
throw new IllegalStateException("Error accessing config '"+key+"'"
+ (entity!=null ? " on "+entity : "")+": "+e, e);
+ (location!=null ? " on "+location : "")+": "+e, e);
}

return null;
}

@Override
public String toString() {
return getClass().getName()+"["+entity+"]";
return getClass().getName()+"["+location+"]";
}
}

Expand Down Expand Up @@ -311,10 +393,10 @@ public TemplateModel get(String key) throws TemplateModelException {
if (entity!=null)
return new EntityConfigTemplateModel(entity);
else
return new EntityConfigTemplateModel(mgmt);
return new MgmtConfigTemplateModel(mgmt);
}
if ("mgmt".equals(key)) {
return new EntityConfigTemplateModel(mgmt);
return new MgmtConfigTemplateModel(mgmt);
}

if ("driver".equals(key) && driver!=null)
Expand Down Expand Up @@ -351,6 +433,63 @@ public String toString() {
}
}

/**
* 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 LocationAndMapTemplateModel implements TemplateHashModel {
protected final LocationInternal location;
protected final ManagementContext mgmt;
protected final DotSplittingTemplateModel extraSubstitutionsModel;

protected LocationAndMapTemplateModel(LocationInternal location, Map<String,? extends Object> extraSubstitutions) {
this.location = checkNotNull(location, "location");
this.mgmt = location.getManagementContext();
this.extraSubstitutionsModel = new DotSplittingTemplateModel(extraSubstitutions);
}

@Override
public boolean isEmpty() { return false; }

@Override
public TemplateModel get(String key) throws TemplateModelException {
if (extraSubstitutionsModel.contains(key))
return wrapAsTemplateModel( extraSubstitutionsModel.get(key) );

if ("location".equals(key))
return wrapAsTemplateModel( location );
if ("config".equals(key)) {
return new LocationConfigTemplateModel(location);
}
if ("mgmt".equals(key)) {
return new MgmtConfigTemplateModel(mgmt);
}

if (mgmt!=null) {
// TODO deprecated in 0.7.0, remove after next version
// ie not supported to access global props without qualification
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 wrapAsTemplateModel( result );
}
}

if ("javaSysProps".equals(key))
return wrapAsTemplateModel( System.getProperties() );

return null;
}

@Override
public String toString() {
return getClass().getName()+"["+location+"]";
}
}

/** 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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
import org.apache.brooklyn.core.test.entity.TestApplication;
import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.test.FixedLocaleTest;
import org.apache.brooklyn.util.core.text.TemplateProcessor;
import org.testng.Assert;
Expand Down Expand Up @@ -101,6 +103,22 @@ public void testEntityGetterMethod() {
assertEquals(result, app.getId());
}

@Test
public void testLocationGetterMethod() {
LocalhostMachineProvisioningLocation location = app.newLocalhostProvisioningLocation();
String templateContents = "${location.id}";
String result = TemplateProcessor.processTemplateContents(templateContents, location, ImmutableMap.<String,Object>of());
assertEquals(result, location.getId());
}

@Test
public void testLocationConfig() {
LocalhostMachineProvisioningLocation location = app.newLocalhostProvisioningLocation(ImmutableMap.of("mykey", "myval"));
String templateContents = "${config['mykey']}";//"+TestEntity.CONF_NAME.getName()+"']}";
String result = TemplateProcessor.processTemplateContents(templateContents, location, ImmutableMap.<String,Object>of());
assertEquals(result, "myval");
}

@Test
public void testManagementContextConfig() {
mgmt.getBrooklynProperties().put("globalmykey", "myval");
Expand Down
Loading

0 comments on commit 9790e8b

Please sign in to comment.