From f1d49a1274597482c19b02e1555138d1a6345ef5 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 10:56:03 +0100 Subject: [PATCH 01/48] address @sjcorbett review comments on #46 --- .../java/brooklyn/entity/proxying/InternalEntityFactory.java | 2 +- core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java index 74b7ad2d86..6813efad35 100644 --- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java +++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java @@ -174,7 +174,7 @@ public T initEntity(final T entity, final EntitySpec spec) for (EntitySpec childSpec : spec.getChildren()) { Entity child = createEntity(childSpec); - ((EntityLocal)entity).addChild(child); + entity.addChild(child); } /* Marked transient so that the task is not needlessly kept around at the highest level. diff --git a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java index 623335096f..2af0d243bf 100644 --- a/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java +++ b/core/src/test/java/brooklyn/entity/basic/EntitySpecTest.java @@ -45,7 +45,7 @@ public void testSetsConfig() throws Exception { } @Test - public void testAddsChidlren() throws Exception { + public void testAddsChildren() throws Exception { entity = app.createAndManageChild( EntitySpec.create(TestEntity.class) .displayName("child") .child(EntitySpec.create(TestEntity.class) From 334d2a873aac21885d717735a9532f85d2df72c7 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 12:46:24 +0100 Subject: [PATCH 02/48] stricter coercion of booleans (previously treated everything but 'true' case-insensitive as false) --- .../brooklyn/util/flags/TypeCoercions.java | 28 ++++++++++++-- .../util/internal/TypeCoercionsTest.java | 37 +++++++++++++++++-- .../brooklyn/util/text/StringPredicates.java | 26 ++++++++++++- 3 files changed, 82 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java index de11ea419f..4276a9bade 100644 --- a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java +++ b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java @@ -236,11 +236,18 @@ public static T castPrimitive(Object value, Class targetType) { return (T) value; } - // boolean can only be cast to itself if (targetWrapType == Boolean.class) { - return (T) value; + // only char can be mapped to boolean + // (we could say 0=false, nonzero=true, but there is no compelling use case so better + // to encourage users to write as boolean) + if (sourceWrapType == Character.class) + return (T) stringToPrimitive(value.toString(), targetType); + + throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType); } else if (sourceWrapType == Boolean.class) { - return (T) value; + // boolean can't cast to anything else + + throw new ClassCoercionException("Cannot cast "+sourceWrapType+" ("+value+") to "+targetType); } // for whole-numbers (where casting to long won't lose anything)... @@ -294,7 +301,6 @@ public static T castPrimitive(Object value, Class targetType) { } } - @SuppressWarnings("unchecked") public static boolean isPrimitiveOrBoxer(Class type) { return Primitives.allPrimitiveTypes().contains(type) || Primitives.allWrapperTypes().contains(type); } @@ -312,6 +318,20 @@ public static T stringToPrimitive(String value, Class targetType) { } } + // For boolean we could use valueOf, but that returns false whereas we'd rather throw errors on bad values + if (targetType == Boolean.class || targetType == boolean.class) { + if ("true".equalsIgnoreCase(value)) return (T) Boolean.TRUE; + if ("false".equalsIgnoreCase(value)) return (T) Boolean.FALSE; + if ("yes".equalsIgnoreCase(value)) return (T) Boolean.TRUE; + if ("no".equalsIgnoreCase(value)) return (T) Boolean.FALSE; + if ("t".equalsIgnoreCase(value)) return (T) Boolean.TRUE; + if ("f".equalsIgnoreCase(value)) return (T) Boolean.FALSE; + if ("y".equalsIgnoreCase(value)) return (T) Boolean.TRUE; + if ("n".equalsIgnoreCase(value)) return (T) Boolean.FALSE; + + throw new ClassCoercionException("Cannot coerce type String to "+targetType.getCanonicalName()+" ("+value+"): adapting failed"); + } + // Otherwise can use valueOf reflectively Class wrappedType; if (Primitives.allPrimitiveTypes().contains(targetType)) { diff --git a/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java b/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java index 7318cda561..ae75d7b559 100644 --- a/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java +++ b/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java @@ -9,13 +9,17 @@ import java.util.Set; import org.codehaus.groovy.runtime.GStringImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.Test; import brooklyn.entity.basic.Lifecycle; import brooklyn.util.flags.ClassCoercionException; import brooklyn.util.flags.TypeCoercions; +import brooklyn.util.text.StringPredicates; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -23,6 +27,8 @@ public class TypeCoercionsTest { + private static final Logger log = LoggerFactory.getLogger(TypeCoercionsTest.class); + @Test public void testCoerceCharSequenceToString() { assertEquals(TypeCoercions.coerce(new StringBuilder("abc"), String.class), "abc"); @@ -38,6 +44,7 @@ public void testCoerceStringToPrimitive() { assertEquals(TypeCoercions.coerce("1", Float.class), (Float)1f); assertEquals(TypeCoercions.coerce("1", Double.class), (Double)1d); assertEquals(TypeCoercions.coerce("true", Boolean.class), (Boolean)true); + assertEquals(TypeCoercions.coerce("False", Boolean.class), (Boolean)false); assertEquals(TypeCoercions.coerce("1", char.class), (Character)'1'); assertEquals(TypeCoercions.coerce("1", short.class), (Short)((short)1)); @@ -45,7 +52,8 @@ public void testCoerceStringToPrimitive() { assertEquals(TypeCoercions.coerce("1", long.class), (Long)1l); assertEquals(TypeCoercions.coerce("1", float.class), (Float)1f); assertEquals(TypeCoercions.coerce("1", double.class), (Double)1d); - assertEquals(TypeCoercions.coerce("true", boolean.class), (Boolean)true); + assertEquals(TypeCoercions.coerce("TRUE", boolean.class), (Boolean)true); + assertEquals(TypeCoercions.coerce("false", boolean.class), (Boolean)false); } @Test @@ -84,9 +92,30 @@ public void testCastPrimitives() { assertEquals(TypeCoercions.coerce((long)1, Integer.class), (Integer)1); assertEquals(TypeCoercions.coerce((float)1, Integer.class), (Integer)1); assertEquals(TypeCoercions.coerce((double)1, Integer.class), (Integer)1); - } + @Test + public void testCoercePrimitiveFailures() { + // error messages don't have to be this exactly, but they should include sufficient information... + assertCoercionFailsWithErrorMatching("maybe", boolean.class, StringPredicates.containsAllLiterals("String", "boolean", "maybe")); + assertCoercionFailsWithErrorMatching("NaN", int.class, StringPredicates.containsAllLiterals("String", "int", "NaN")); + assertCoercionFailsWithErrorMatching('c', boolean.class, StringPredicates.containsAllLiterals("boolean", "(c)")); // will say 'string' rather than 'char' + assertCoercionFailsWithErrorMatching(0, boolean.class, StringPredicates.containsAllLiterals("Integer", "boolean", "0")); + } + + protected void assertCoercionFailsWithErrorMatching(Object input, Class type, Predicate errorMessageRequirement) { + try { + Object result = TypeCoercions.coerce(input, type); + Assert.fail("Should have failed type coercion of "+input+" to "+type+", instead got: "+result); + } catch (Exception e) { + if (errorMessageRequirement==null || errorMessageRequirement.apply(e.toString())) + log.info("Primitive coercion failed as expected, with: "+e); + else + Assert.fail("Error from type coercion of "+input+" to "+type+" failed with wrong exception; expected match of "+errorMessageRequirement+" but got: "+e); + } + + } + @Test public void testCastToNumericPrimitives() { assertEquals(TypeCoercions.coerce(BigInteger.ONE, Integer.class), (Integer)1); @@ -143,13 +172,13 @@ public void testStringToListCoercion() { @Test public void testStringToMapCoercion() { - Map s = TypeCoercions.coerce("a=1,b=2,c=3", Map.class); + Map s = TypeCoercions.coerce("a=1,b=2,c=3", Map.class); Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2", "c", "3")); } @Test public void testJsonStringToMapCoercion() { - Map s = TypeCoercions.coerce("{ \"a\" : \"1\", \"b\" : \"2\", \"c\" : \"3\" }", Map.class); + Map s = TypeCoercions.coerce("{ \"a\" : \"1\", \"b\" : \"2\", \"c\" : \"3\" }", Map.class); Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2", "c", "3")); } diff --git a/utils/common/src/main/java/brooklyn/util/text/StringPredicates.java b/utils/common/src/main/java/brooklyn/util/text/StringPredicates.java index 5399db203a..60655123cb 100644 --- a/utils/common/src/main/java/brooklyn/util/text/StringPredicates.java +++ b/utils/common/src/main/java/brooklyn/util/text/StringPredicates.java @@ -1,14 +1,17 @@ package brooklyn.util.text; import java.io.Serializable; +import java.util.Arrays; import java.util.Set; import javax.annotation.Nullable; import brooklyn.util.collections.MutableSet; +import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; public class StringPredicates { @@ -21,6 +24,10 @@ public static Predicate isBlank() { public boolean apply(@Nullable CharSequence input) { return Strings.isBlank(input); } + @Override + public String toString() { + return "isBlank"; + } }; } @@ -30,6 +37,10 @@ public static Predicate containsLiteralCaseInsensitive(final Strin public boolean apply(@Nullable CharSequence input) { return Strings.containsLiteralIgnoreCase(input, fragment); } + @Override + public String toString() { + return "containsLiteralCaseInsensitive("+fragment+")"; + } }; } @@ -39,9 +50,22 @@ public static Predicate containsLiteral(final String fragment) { public boolean apply(@Nullable CharSequence input) { return Strings.containsLiteral(input, fragment); } + @Override + public String toString() { + return "containsLiteral("+fragment+")"; + } }; } + public static Predicate containsAllLiterals(final String... fragments) { + return Predicates.and(Iterables.transform(Arrays.asList(fragments), new Function>() { + @Override + public Predicate apply(String input) { + return containsLiteral(input); + } + })); + } + public static Predicate containsRegex(final String regex) { // "Pattern" ... what a bad name :) return Predicates.containsPattern(regex); @@ -86,7 +110,7 @@ public String toString() { return "equalToAny("+vals+")"; } } - + // TODO globs, matches regex, etc ... add as you need them! } From fad85de355798a9420a73f14b57196fdb74e7676 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 12:47:01 +0100 Subject: [PATCH 03/48] the BrooklynAssemblyTemplateInstantiator now creates a spec, significantly tidying the code --- .../BrooklynAssemblyTemplateInstantiator.java | 282 ++++++++---------- .../rest/resources/CatalogResourceTest.java | 20 +- 2 files changed, 145 insertions(+), 157 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index 7633fb737a..f8aa62f1dd 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -10,7 +10,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,23 +30,25 @@ import brooklyn.entity.basic.EntityTypes; import brooklyn.entity.basic.StartableApplication; import brooklyn.entity.proxying.EntitySpec; -import brooklyn.entity.proxying.InternalEntityFactory; import brooklyn.entity.trait.Startable; import brooklyn.location.Location; import brooklyn.management.ManagementContext; import brooklyn.management.Task; -import brooklyn.management.internal.ManagementContextInternal; import brooklyn.util.collections.MutableList; import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.flags.TypeCoercions; import brooklyn.util.text.Strings; +import com.google.common.collect.Iterables; import com.google.common.collect.Maps; public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpecInstantiator { private static final Logger log = LoggerFactory.getLogger(BrooklynAssemblyTemplateInstantiator.class); + public static final String NEVER_UNWRAP_APPS_PROPERTY = "wrappedApp"; + @Override public Assembly instantiate(AssemblyTemplate template, CampPlatform platform) { Application app = create(template, platform); @@ -56,32 +57,80 @@ public Assembly instantiate(AssemblyTemplate template, CampPlatform platform) { return platform.assemblies().get(app.getApplicationId()); } + public EntitySpec createSpec(AssemblyTemplate template, CampPlatform platform) { + return createAppOrSpec(template, platform, true).getSpec(); + } + // note: based on BrooklynRestResourceUtils, but modified to not allow child entities (yet) // (will want to revise that when building up from a non-brooklyn template) public Application create(AssemblyTemplate template, CampPlatform platform) { + ManagementContext mgmt = getBrooklynManagementContext(platform); + + AppOrSpec appOrSpec = createAppOrSpec(template, platform, false); + + if (appOrSpec.hasApp()) + return appOrSpec.getApp(); + if (!appOrSpec.hasSpec()) + throw new IllegalStateException("No spec could be produced from "+template); + + EntitySpec spec = appOrSpec.getSpec(); + + Application instance = (Application) mgmt.getEntityManager().createEntity(spec); + log.info("CAMP placing '{}' under management", instance); + Entities.startManagement(instance, mgmt); + + return instance; + } + + protected AppOrSpec createAppOrSpec(AssemblyTemplate template, CampPlatform platform, boolean requireSpec) { log.debug("CAMP creating application instance for {} ({})", template.getId(), template); ManagementContext mgmt = getBrooklynManagementContext(platform); BrooklynCatalog catalog = mgmt.getCatalog(); - // TODO: item is always null because template.id is a random String, so - // createApplicationFromCatalog branch below is never taken. If `id' - // key is given in blueprint it is available with: - // Object customId = template.getCustomAttributes().get("id"); + CatalogItem item = catalog.getCatalogItem(template.getId()); - - if (item==null) { - return createApplicationFromNonCatalogCampTemplate(template, platform); + if (item!=null) { + // TODO legacy path - currently item is always null because template.id is a random String, + // and pretty sure that is the desired behaviour; we now (Jul 2014) automatically promote + // so fine for users always to put the catalog registeredType in the services block; + // if we did want to support users supplying an `id' that would be available not via the above + // but via template.getCustomAttributes().get("id"); + + return createApplicationFromCatalog(platform, item, template, requireSpec); } else { - return createApplicationFromCatalog(platform, item, template); + return new AppOrSpec(createApplicationFromNonCatalogCampTemplate(template, platform)); } } + + private static class AppOrSpec { + private final Application app; + private final EntitySpec spec; + + public AppOrSpec(Application app) { + this.app = app; + this.spec = null; + } - public EntitySpec createSpec(AssemblyTemplate template, CampPlatform platform) { - // TODO rewrite this class so everything below returns a spec, then rewrite above just to instantiate (and start?) the spec - throw new UnsupportedOperationException(); + public AppOrSpec(EntitySpec spec) { + this.app = null; + this.spec = spec; + } + + public boolean hasApp() { + return app!=null; + } + public boolean hasSpec() { + return spec!=null; + } + public Application getApp() { + return app; + } + public EntitySpec getSpec() { + return spec; + } } - protected Application createApplicationFromCatalog(CampPlatform platform, CatalogItem item, AssemblyTemplate template) { + protected AppOrSpec createApplicationFromCatalog(CampPlatform platform, CatalogItem item, AssemblyTemplate template, boolean requireSpec) { ManagementContext mgmt = getBrooklynManagementContext(platform); if (!template.getApplicationComponentTemplates().isEmpty() || @@ -93,7 +142,6 @@ protected Application createApplicationFromCatalog(CampPlatform platform, Catalo String name = template.getName(); String type = item.getJavaType(); - final Application instance; // Load the class; first try to use the appropriate catalog item; but then allow anything that is on the classpath final Class clazz; @@ -105,8 +153,17 @@ protected Application createApplicationFromCatalog(CampPlatform platform, Catalo try { if (ApplicationBuilder.class.isAssignableFrom(clazz)) { + if (requireSpec) { + // TODO we could enable this, by returning the spec from the ApplicationBuilder; + // note that we would have to set it up so that ApplicationBuilder.doBuild is called from an + // entity initializer set on the spec, and remove the doBuild call from ApplicationBuilder.manage. + // (this could also allow ApplicationBuilder instances to be used from catalog) + throw new IllegalStateException("ApplicationBuilder items cannot be used when specs have to be created"); + } Constructor constructor = clazz.getConstructor(); ApplicationBuilder appBuilder = (ApplicationBuilder) constructor.newInstance(); + // for builder, we (1) can't get a spec (so discourage use of Builder?), + // and (2) we have to manually extract key bits of the template if (!Strings.isEmpty(name)) appBuilder.appDisplayName(name); @@ -115,28 +172,26 @@ protected Application createApplicationFromCatalog(CampPlatform platform, Catalo log.info("CAMP placing '{}' under management", appBuilder); appBuilder.configure( convertFlagsToKeys(appBuilder.getType(), configO) ); - instance = appBuilder.manage(mgmt); + Application instance = appBuilder.manage(mgmt); applyLocations(mgmt, template, instance); + + return new AppOrSpec(instance); } else if (Application.class.isAssignableFrom(clazz)) { // TODO use resolver's configureEntitySpec instead final Map configO = (Map) template.getCustomAttributes().get("brooklyn.config"); - brooklyn.entity.proxying.EntitySpec coreSpec = toCoreEntitySpec(clazz, name, configO); - instance = (Application) mgmt.getEntityManager().createEntity(coreSpec); - - log.info("CAMP placing '{}' under management", instance); - Entities.startManagement(instance, mgmt); + @SuppressWarnings("unchecked") + brooklyn.entity.proxying.EntitySpec coreSpec = toCoreEntitySpec((Class)clazz, name, configO); + applyLocations(mgmt, template, coreSpec); - applyLocations(mgmt, template, instance); + return new AppOrSpec(coreSpec); } else { throw new IllegalArgumentException("Class "+clazz+" must extend one of ApplicationBuilder or Application"); } - return instance; - } catch (Exception e) { log.error("CAMP failed to create application: "+e, e); throw Exceptions.propagate(e); @@ -149,6 +204,12 @@ private void applyLocations(ManagementContext mgmt, AssemblyTemplate template, f ((EntityInternal)instance).addLocations(locations); } + private void applyLocations(ManagementContext mgmt, AssemblyTemplate template, final EntitySpec spec) { + List locations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(template.getCustomAttributes(), false); + if (locations!=null) + spec.locations(locations); + } + private ManagementContext getBrooklynManagementContext(CampPlatform platform) { return ((HasBrooklynManagementContext)platform).getBrooklynManagementContext(); } @@ -180,7 +241,7 @@ private static Map convertFlagsToKeys(Class javaType, Map // TODO exact copy of BRRU, as above @SuppressWarnings({ "rawtypes", "unchecked" }) - private EntitySpec toCoreEntitySpec(Class clazz, String name, Map configO) { + private EntitySpec toCoreEntitySpec(Class clazz, String name, Map configO) { Map config = (configO == null) ? Maps.newLinkedHashMap() : Maps.newLinkedHashMap(configO); EntitySpec result; @@ -199,151 +260,72 @@ private EntitySpec toCoreEntitySpec(Class clazz, String return result; } - /* - * recursive method that finds child entities referenced in the config map, side-effecting the entities and entitySpecs maps. - */ - protected void buildEntityHierarchy(ManagementContext mgmt, Map> entitySpecs, Entity parent, List> childConfig) { - if (childConfig != null) { - for (Map childAttrs : childConfig) { - BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, childAttrs); - EntitySpec spec = entityResolver.resolveSpec(); - - spec.parent(parent); - Entity entity = entityResolver.newEntity(spec); - entitySpecs.put(entity, spec); - buildEntityHierarchy(mgmt, entitySpecs, entity, entityResolver.getChildren(childAttrs)); - } - } - } - - protected Application createApplicationFromNonCatalogCampTemplate(AssemblyTemplate template, CampPlatform platform) { + @SuppressWarnings("unchecked") + protected EntitySpec createApplicationFromNonCatalogCampTemplate(AssemblyTemplate template, CampPlatform platform) { // AssemblyTemplates created via PDP, _specifying_ then entities to put in final ManagementContext mgmt = getBrooklynManagementContext(platform); - Map> allEntities = Maps.newLinkedHashMap(); - StartableApplication rootApp = buildRootApp(template, platform, allEntities); - initEntities(mgmt, allEntities); - log.info("CAMP placing '{}' under management", allEntities.get(rootApp)); - Entities.startManagement(rootApp, mgmt); - return rootApp; - } - - private StartableApplication buildRootApp(AssemblyTemplate template, CampPlatform platform, Map> allEntities) { - if (shouldWrapInApp(template, platform)) { - return buildWrappedApp(template, platform, allEntities); - } else { - return buildPromotedApp(template, platform, allEntities); - } - } - - private StartableApplication buildWrappedApp(AssemblyTemplate template, CampPlatform platform, Map> allEntities) { - final ManagementContext mgmt = getBrooklynManagementContext(platform); - - BrooklynComponentTemplateResolver appResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, template); - EntitySpec wrapAppSpec = appResolver.resolveSpec(StartableApplication.class, BasicApplicationImpl.class); - StartableApplication wrapApp = appResolver.newEntity(wrapAppSpec); - allEntities.put(wrapApp, wrapAppSpec); + BrooklynComponentTemplateResolver resolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, template); + EntitySpec app = resolver.resolveSpec(StartableApplication.class, BasicApplicationImpl.class); - buildEntities(template, wrapApp, wrapAppSpec, allEntities, mgmt); + // first build the children into an empty shell app + buildTemplateServicesAsSpecs(template, app, mgmt); - return wrapApp; - } - - private StartableApplication buildPromotedApp(AssemblyTemplate template, CampPlatform platform, Map> allEntities) { - final ManagementContext mgmt = getBrooklynManagementContext(platform); + if (shouldUnwrap(template, app)) { + app = (EntitySpec) Iterables.getOnlyElement( app.getChildren() ); + } - ResolvableLink promotedAppTemplate = template.getPlatformComponentTemplates().links().get(0); + // now apply template items to the root app (possibly promoted) - PlatformComponentTemplate appChildComponentTemplate = promotedAppTemplate.resolve(); - BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, appChildComponentTemplate); - EntitySpec spec = buildEntitySpecNonHierarchical(null, appChildComponentTemplate, allEntities, mgmt, entityResolver); - - // and this is needed in case 'name' was set at template level (eg ApplicationResourceTest.testDeployApplicationYaml) - if (spec.getDisplayName()==null && template.getName()!=null) - spec.displayName(template.getName()); - - StartableApplication app = (StartableApplication) buildEntityHierarchical(spec, appChildComponentTemplate, allEntities, mgmt, entityResolver); - - // TODO i (alex) think we need this because locations defined at the root of the template could have been lost otherwise? + // take name from template if not already set + if (app.getDisplayName()==null && template.getName()!=null) + app.displayName(template.getName()); + + // apply locations defined at the root of the template applyLocations(mgmt, template, app); return app; } - private void buildEntities(AssemblyTemplate template, StartableApplication app, EntitySpec appSpec, - Map> allEntities, ManagementContext mgmt) { - for (ResolvableLink ctl: template.getPlatformComponentTemplates().links()) { - buildEntity(app, ctl, allEntities, mgmt); - } - } - - private Entity buildEntity(StartableApplication parent, ResolvableLink ctl, - Map> allEntities, ManagementContext mgmt) { - PlatformComponentTemplate appChildComponentTemplate = ctl.resolve(); - BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, appChildComponentTemplate); - EntitySpec spec = buildEntitySpecNonHierarchical(parent, appChildComponentTemplate, allEntities, mgmt, entityResolver); - return buildEntityHierarchical(spec, appChildComponentTemplate, allEntities, mgmt, entityResolver); - } - private EntitySpec buildEntitySpecNonHierarchical(StartableApplication parent, PlatformComponentTemplate appChildComponentTemplate, - Map> allEntities, ManagementContext mgmt, BrooklynComponentTemplateResolver entityResolver) { - EntitySpec spec = entityResolver.resolveSpec(); - if(parent != null) { - spec.parent(parent); + protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec app) { + Object leaveWrapped = template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY); + if (leaveWrapped!=null) { + if (TypeCoercions.coerce(leaveWrapped, Boolean.class)) + return false; } - return spec; - } - private Entity buildEntityHierarchical(EntitySpec spec, PlatformComponentTemplate appChildComponentTemplate, - Map> allEntities, ManagementContext mgmt, BrooklynComponentTemplateResolver entityResolver) { - Entity entity = entityResolver.newEntity(spec); - allEntities.put(entity, spec); - buildEntityHierarchy(mgmt, allEntities, entity, entityResolver.getChildren(appChildComponentTemplate.getCustomAttributes())); - return entity; - } - - private boolean shouldWrapInApp(AssemblyTemplate template, CampPlatform platform) { - return isWrapAppRequested(template) || - !isSingleApp(template, platform); - } + + if (app.getChildren().size()!=1) + return false; + + EntitySpec childSpec = Iterables.getOnlyElement(app.getChildren()); + if (childSpec.getType()==null || !Application.class.isAssignableFrom(childSpec.getType())) + return false; - private boolean isWrapAppRequested(AssemblyTemplate template) { - return Boolean.TRUE.equals(template.getCustomAttributes().get("wrappedApp")); + return true; } - protected boolean isSingleApp(AssemblyTemplate template, CampPlatform platform) { - // AssemblyTemplates created via PDP, _specifying_ then entities to put in - final ManagementContext mgmt = getBrooklynManagementContext(platform); - - List> pct = template.getPlatformComponentTemplates().links(); - if(pct.size() == 1) { - ResolvableLink res = pct.get(0); - PlatformComponentTemplate templ = res.resolve(); - Class entity = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, templ).loadEntityClass(); - if(StartableApplication.class.isAssignableFrom(entity)) { - return true; - } - } - return false; - } - - private void initEntities(final ManagementContext mgmt, Map> entities) { - for (Entry> entry : entities.entrySet()) { - final Entity entity = entry.getKey(); + private void buildTemplateServicesAsSpecs(AssemblyTemplate template, EntitySpec root, ManagementContext mgmt) { + for (ResolvableLink ctl: template.getPlatformComponentTemplates().links()) { + PlatformComponentTemplate appChildComponentTemplate = ctl.resolve(); + BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, appChildComponentTemplate); - @SuppressWarnings("unchecked") - final EntitySpec spec = (EntitySpec)entry.getValue(); + EntitySpec spec = entityResolver.resolveSpec(); + root.child(spec); - ((EntityInternal) entity).getExecutionContext().submit(MutableMap.of(), new Runnable() { - @Override - public void run() { - initEntity(mgmt, entity, spec); - } - }).getUnchecked(); + buildChildrenEntitySpecs(mgmt, spec, entityResolver.getChildren(appChildComponentTemplate.getCustomAttributes())); } } - protected void initEntity(ManagementContext mgmt, T entity, EntitySpec spec) { - InternalEntityFactory entityFactory = ((ManagementContextInternal)mgmt).getEntityFactory(); - entityFactory.initEntity(entity, spec); + protected void buildChildrenEntitySpecs(ManagementContext mgmt, EntitySpec parent, List> childConfig) { + if (childConfig != null) { + for (Map childAttrs : childConfig) { + BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, childAttrs); + EntitySpec spec = entityResolver.resolveSpec(); + parent.child(spec); + + buildChildrenEntitySpecs(mgmt, spec, entityResolver.getChildren(childAttrs)); + } + } } } diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index 84e60f3c9b..51c56d8989 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -44,7 +44,7 @@ protected void setUpResources() throws Exception { addResource(new CatalogResource()); } - @Test(enabled=false, groups="WIP") + @Test public void testRegisterCustomEntity() { String registeredTypeName = "my.catalog.app.id"; String yaml = @@ -69,13 +69,19 @@ public void testRegisterCustomEntity() { CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+registeredTypeName) .get(CatalogEntitySummary.class); - // TODO more checks, when brooklyn.catalog working -// assertEquals(entityItem.getId(), registeredTypeName); -// assertEquals(entityItem.getName(), "My Catalog App"); -// assertEquals(entityItem.getDescription(), "My description"); -// assertEquals(entityItem.getIconUrl(), "classpath://path/to/myicon.jpg"); + assertEquals(entityItem.getRegisteredType(), registeredTypeName); - assertEquals(entityItem.getJavaType(), "brooklyn.test.entity.TestEntity"); + + // stored as yaml, not java +// assertEquals(entityItem.getJavaType(), "brooklyn.test.entity.TestEntity"); + Assert.assertNotNull(entityItem.getPlanYaml()); + Assert.assertTrue(entityItem.getPlanYaml().contains("brooklyn.test.entity.TestEntity")); + + // TODO @sjcorbett more checks, when brooklyn.catalog working +// assertEquals(entityItem.getId(), registeredTypeName); +// assertEquals(entityItem.getName(), "My Catalog App"); +// assertEquals(entityItem.getDescription(), "My description"); +// assertEquals(entityItem.getIconUrl(), "classpath://path/to/myicon.jpg"); } @Test From 8057579a2323064f2cb17ee1ba7cecc5e0cb847a Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 13:22:03 +0100 Subject: [PATCH 04/48] put more design, and comments, around order in which children are added vs parent set and initializers run --- .../proxying/InternalEntityFactory.java | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java index 6813efad35..4c9f44cffc 100644 --- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java +++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java @@ -79,7 +79,6 @@ static void setConstructing() { * @param managementContext * @param clazz */ - @SuppressWarnings({ "unchecked", "rawtypes" }) public static boolean isNewStyleEntity(ManagementContext managementContext, Class clazz) { try { return isNewStyleEntity(clazz); @@ -172,9 +171,22 @@ public T initEntity(final T entity, final EntitySpec spec) ((EntityLocal)entity).setConfig((ConfigKey)entry.getKey(), entry.getValue()); } - for (EntitySpec childSpec : spec.getChildren()) { - Entity child = createEntity(childSpec); - entity.addChild(child); + /* Order is important here. Changed Jul 2014 when supporting children in spec. + * (Previously parent was set *after* running initializers, and there were no children.) + *

+ * We may want access to the parent when running initializers (or in children initializers). + *

+ * Currently initializers will be run before children are added. + * on the basis that it is more likely children will want to assume parent is initialized, + * than initializers will want access to children. + *

+ * If this is not good enough we could do an additional pass where all children are built up + * before running any initializers. + */ + Entity parent = spec.getParent(); + if (parent != null) { + parent = (parent instanceof AbstractEntity) ? ((AbstractEntity)parent).getProxyIfAvailable() : parent; + entity.setParent(parent); } /* Marked transient so that the task is not needlessly kept around at the highest level. @@ -221,11 +233,19 @@ public void run() { } }).build()).get(); - Entity parent = spec.getParent(); - if (parent != null) { - parent = (parent instanceof AbstractEntity) ? ((AbstractEntity)parent).getProxyIfAvailable() : parent; - entity.setParent(parent); + for (EntitySpec childSpec : spec.getChildren()) { + if (childSpec.getParent()!=null) { + if (!childSpec.getParent().equals(entity)) { + throw new IllegalStateException("Spec "+childSpec+" has parent "+childSpec.getParent()+" defined, " + + "but it is defined as a child of "+entity); + } + log.warn("Child spec "+childSpec+" is already set with parent "+entity+"; how did this happen?!"); + } + childSpec.parent(entity); + Entity child = createEntity(childSpec); + entity.addChild(child); } + return entity; } catch (Exception e) { From 3b00374fd36f3f806377e90bdf76bee1b1fa81ed Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 13:23:37 +0100 Subject: [PATCH 05/48] when app is promoted, take some of the fields set at the template level (but if app is not promoted, don't *reapply* things like location) --- .../BrooklynAssemblyTemplateInstantiator.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index f8aa62f1dd..b5bc32d0c8 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -272,18 +272,18 @@ protected EntitySpec createApplicationFromNonCatalogCampT buildTemplateServicesAsSpecs(template, app, mgmt); if (shouldUnwrap(template, app)) { + EntitySpec oldApp = app; app = (EntitySpec) Iterables.getOnlyElement( app.getChildren() ); + // if promoted, apply the transformations done to the app + // (normally this will be done by the resolveSpec call above) + if (app.getDisplayName()==null) app.displayName(oldApp.getDisplayName()); + app.locations(oldApp.getLocations()); + app.configure(app.getConfig()); + app.addInitializers(oldApp.getInitializers()); + // TODO other things, as they are added (or refuse to promote if they are set) + // (it's a bit messy doing this copy) } - // now apply template items to the root app (possibly promoted) - - // take name from template if not already set - if (app.getDisplayName()==null && template.getName()!=null) - app.displayName(template.getName()); - - // apply locations defined at the root of the template - applyLocations(mgmt, template, app); - return app; } From 234a6e53ea525fe5bb426971063d45b051c2bcfe Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 14:15:14 +0100 Subject: [PATCH 06/48] don't promote yaml apps which define initializers and brooklyn.config at the root --- .../BrooklynAssemblyTemplateInstantiator.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index b5bc32d0c8..de9a63eb0e 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -10,6 +10,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -278,10 +279,6 @@ protected EntitySpec createApplicationFromNonCatalogCampT // (normally this will be done by the resolveSpec call above) if (app.getDisplayName()==null) app.displayName(oldApp.getDisplayName()); app.locations(oldApp.getLocations()); - app.configure(app.getConfig()); - app.addInitializers(oldApp.getInitializers()); - // TODO other things, as they are added (or refuse to promote if they are set) - // (it's a bit messy doing this copy) } return app; @@ -301,6 +298,22 @@ protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec rootAttrs = template.getCustomAttributes().keySet(); + for (String rootAttr: rootAttrs) { + if (rootAttr.equals("brooklyn.catalog")) { + // this attr does not block promotion + continue; + } + if (rootAttr.startsWith("brooklyn.")) { + // any others in 'brooklyn' namespace will block promotion + return false; + } + // location is allowed in both, and is copied on promotion + // (name also copied) + // others are root currently are ignored on promotion; they are usually metadata + // TODO might be nice to know what we are excluding + } + return true; } From 0b0d548e79142779e38b642f769213d6cd31fd02 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 14:15:53 +0100 Subject: [PATCH 07/48] expand create-entity-from-spec logic so that it does two passes, one to build up the hierarchy, and one to run initializers and policies (still simpler than rebind, but more complicated than it was, to support child specs with initializers which try to access their application) --- .../proxying/InternalEntityFactory.java | 175 +++++++++++------- 1 file changed, 104 insertions(+), 71 deletions(-) diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java index 4c9f44cffc..78f5a23fe2 100644 --- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java +++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java @@ -128,6 +128,26 @@ public T createEntityProxy(EntitySpec spec, T entity) { } public T createEntity(EntitySpec spec) { + /* Order is important here. Changed Jul 2014 when supporting children in spec. + * (Previously was much simpler, and parent was set right after running initializers; and there were no children.) + *

+ * It seems we need access to the parent (indeed the root application) when running some initializers (esp children initializers). + *

+ * Now we do two passes, so that hierarchy is fully populated before initialization and policies. + * (This is needed where some config or initializer might reference another entity by its ID, e.g. yaml $brooklyn:component("id"). + * Initialization is done in parent-first order with depth-first children traversal. + */ + + // (maps needed because we need the spec, and we need to keep the AbstractEntity to call init, not a proxy) + Map entitiesByEntityId = MutableMap.of(); + Map> specsByEntityId = MutableMap.of(); + + T entity = createEntityAndDescendantsUninitialized(spec, entitiesByEntityId, specsByEntityId); + initEntityAndDescendants(entity.getId(), entitiesByEntityId, specsByEntityId); + return entity; + } + + protected T createEntityAndDescendantsUninitialized(EntitySpec spec, Map entitiesByEntityId, Map> specsByEntityId) { if (spec.getFlags().containsKey("parent") || spec.getFlags().containsKey("owner")) { throw new IllegalArgumentException("Spec's flags must not contain parent or owner; use spec.parent() instead for "+spec); } @@ -150,7 +170,24 @@ public T createEntity(EntitySpec spec) { ((AbstractEntity)entity).setManagementContext(managementContext); managementContext.prePreManage(entity); - initEntity(entity, spec); + loadUnitializedEntity(entity, spec); + + entitiesByEntityId.put(entity.getId(), entity); + specsByEntityId.put(entity.getId(), spec); + + for (EntitySpec childSpec : spec.getChildren()) { + if (childSpec.getParent()!=null) { + if (!childSpec.getParent().equals(entity)) { + throw new IllegalStateException("Spec "+childSpec+" has parent "+childSpec.getParent()+" defined, " + + "but it is defined as a child of "+entity); + } + log.warn("Child spec "+childSpec+" is already set with parent "+entity+"; how did this happen?!"); + } + childSpec.parent(entity); + Entity child = createEntityAndDescendantsUninitialized(childSpec, entitiesByEntityId, specsByEntityId); + entity.addChild(child); + } + return entity; } catch (Exception e) { @@ -159,7 +196,7 @@ public T createEntity(EntitySpec spec) { } @SuppressWarnings({ "unchecked", "rawtypes" }) - public T initEntity(final T entity, final EntitySpec spec) { + protected T loadUnitializedEntity(final T entity, final EntitySpec spec) { try { if (spec.getDisplayName()!=null) ((AbstractEntity)entity).setDisplayName(spec.getDisplayName()); @@ -171,81 +208,12 @@ public T initEntity(final T entity, final EntitySpec spec) ((EntityLocal)entity).setConfig((ConfigKey)entry.getKey(), entry.getValue()); } - /* Order is important here. Changed Jul 2014 when supporting children in spec. - * (Previously parent was set *after* running initializers, and there were no children.) - *

- * We may want access to the parent when running initializers (or in children initializers). - *

- * Currently initializers will be run before children are added. - * on the basis that it is more likely children will want to assume parent is initialized, - * than initializers will want access to children. - *

- * If this is not good enough we could do an additional pass where all children are built up - * before running any initializers. - */ Entity parent = spec.getParent(); if (parent != null) { parent = (parent instanceof AbstractEntity) ? ((AbstractEntity)parent).getProxyIfAvailable() : parent; entity.setParent(parent); } - - /* Marked transient so that the task is not needlessly kept around at the highest level. - * Note that the task is not normally visible in the GUI, because - * (a) while it is running, the entity is parentless (and so not in the tree); - * and (b) when it is completed it is GC'd, as it is transient. - * However task info is available via the API if you know its ID, - * and if better subtask querying is available it will be picked up as a background task - * of the parent entity creating this child entity - * (note however such subtasks are currently filtered based on parent entity so is excluded). - */ - ((EntityInternal)entity).getExecutionContext().submit(Tasks.builder().dynamic(false).name("Entity initialization").tag(BrooklynTaskTags.TRANSIENT_TASK_TAG) - .body(new Runnable() { - @Override - public void run() { - ((AbstractEntity)entity).init(); - for (EntityInitializer initializer: spec.getInitializers()) - initializer.apply((EntityInternal)entity); - /* 31 Mar 2014, moved initialization (above) into this task: primarily for consistency and traceability on failure. - * TBC whether this is good/bad/indifferent. My (Alex) opinion is that whether it is done in a subtask - * should be the same as whether enricher/policy/etc (below) is done subtasks, which is was added recently - * in 249c96fbb18bd9d763029475e0a3dc251c01b287. @nakomis can you give exact reason code below is needed in a task - * commit message said was to do with wiring up yaml sensors and policies -- which makes sense but specifics would be handy! - * and would let me know if there is any reason to do / not_do the initializer code above also here! - */ - - for (Enricher enricher : spec.getEnrichers()) { - entity.addEnricher(enricher); - } - - for (EnricherSpec enricherSpec : spec.getEnricherSpecs()) { - entity.addEnricher(policyFactory.createEnricher(enricherSpec)); - } - - for (Policy policy : spec.getPolicies()) { - entity.addPolicy((AbstractPolicy)policy); - } - - for (PolicySpec policySpec : spec.getPolicySpecs()) { - entity.addPolicy(policyFactory.createPolicy(policySpec)); - } - - ((AbstractEntity)entity).addLocations(spec.getLocations()); - } - }).build()).get(); - for (EntitySpec childSpec : spec.getChildren()) { - if (childSpec.getParent()!=null) { - if (!childSpec.getParent().equals(entity)) { - throw new IllegalStateException("Spec "+childSpec+" has parent "+childSpec.getParent()+" defined, " - + "but it is defined as a child of "+entity); - } - log.warn("Child spec "+childSpec+" is already set with parent "+entity+"; how did this happen?!"); - } - childSpec.parent(entity); - Entity child = createEntity(childSpec); - entity.addChild(child); - } - return entity; } catch (Exception e) { @@ -253,6 +221,71 @@ public void run() { } } + protected void initEntityAndDescendants(String entityId, final Map entitiesByEntityId, final Map> specsByEntityId) { + final Entity entity = entitiesByEntityId.get(entityId); + final EntitySpec spec = specsByEntityId.get(entityId); + + if (entity==null || spec==null) { + log.debug("Skipping initialization of "+entityId+" found as child of entity being initialized, " + + "but this child is not one we created; likely it was created by an initializer, " + + "and thus it should be already fully initialized."); + return; + } + + /* Marked transient so that the task is not needlessly kept around at the highest level. + * Note that the task is not normally visible in the GUI, because + * (a) while it is running, the entity is parentless (and so not in the tree); + * and (b) when it is completed it is GC'd, as it is transient. + * However task info is available via the API if you know its ID, + * and if better subtask querying is available it will be picked up as a background task + * of the parent entity creating this child entity + * (note however such subtasks are currently filtered based on parent entity so is excluded). + */ + ((EntityInternal)entity).getExecutionContext().submit(Tasks.builder().dynamic(false).name("Entity initialization") + .tag(BrooklynTaskTags.tagForContextEntity(entity)) + .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG) + .body(new Runnable() { + @Override + public void run() { + ((AbstractEntity)entity).init(); + + ((AbstractEntity)entity).addLocations(spec.getLocations()); + + for (EntityInitializer initializer: spec.getInitializers()) + initializer.apply((EntityInternal)entity); + /* 31 Mar 2014, moved initialization (above) into this task: primarily for consistency and traceability on failure. + * TBC whether this is good/bad/indifferent. My (Alex) opinion is that whether it is done in a subtask + * should be the same as whether enricher/policy/etc (below) is done subtasks, which is was added recently + * in 249c96fbb18bd9d763029475e0a3dc251c01b287. @nakomis can you give exact reason code below is needed in a task + * commit message said was to do with wiring up yaml sensors and policies -- which makes sense but specifics would be handy! + * and would let me know if there is any reason to do / not_do the initializer code above also here! + */ + + for (Enricher enricher : spec.getEnrichers()) { + entity.addEnricher(enricher); + } + + for (EnricherSpec enricherSpec : spec.getEnricherSpecs()) { + entity.addEnricher(policyFactory.createEnricher(enricherSpec)); + } + + for (Policy policy : spec.getPolicies()) { + entity.addPolicy((AbstractPolicy)policy); + } + + for (PolicySpec policySpec : spec.getPolicySpecs()) { + entity.addPolicy(policyFactory.createPolicy(policySpec)); + } + + for (Entity child: entity.getChildren()) { + // right now descendants are initialized depth-first (see the getUnchecked() call below) + // they could be done in parallel, but OTOH initializers should be very quick + initEntityAndDescendants(child.getId(), entitiesByEntityId, specsByEntityId); + } + } + }).build()).getUnchecked(); + } + /** * Constructs an entity (if new-style, calls no-arg constructor; if old-style, uses spec to pass in config). */ From 92fc4cf05f6d90082fb8ee881324d19afe4148a9 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 14:30:10 +0100 Subject: [PATCH 08/48] fix integration test where assumption had been not notified on benign changes --- .../ControlledDynamicWebAppClusterTest.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java index edc4894255..d6bbaf09da 100644 --- a/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java +++ b/software/webapp/src/test/java/brooklyn/entity/webapp/ControlledDynamicWebAppClusterTest.java @@ -183,7 +183,7 @@ public void testSetsServiceLifecycle() { .configure("initialSize", 1) .configure("factory", new BasicConfigurableEntityFactory(TestJavaWebAppEntity.class))); - RecordingSensorEventListener listener = new RecordingSensorEventListener(); + RecordingSensorEventListener listener = new RecordingSensorEventListener(true); app.subscribe(cluster, Attributes.SERVICE_STATE, listener); app.start(locs); @@ -223,11 +223,24 @@ public Boolean call() throws Exception { public static class RecordingSensorEventListener implements SensorEventListener { private final List> events = Lists.newCopyOnWriteArrayList(); private final List values = Lists.newCopyOnWriteArrayList(); + private boolean skipDuplicateValues; + public RecordingSensorEventListener() { + this(false); + } + + public RecordingSensorEventListener(boolean skipDuplicateValues) { + this.skipDuplicateValues = skipDuplicateValues; + } + @Override public void onEvent(SensorEvent event) { events.add(event); - values.add(event.getValue()); + if (skipDuplicateValues && !values.isEmpty() && values.get(values.size()-1).equals(event.getValue())) { + // skip + } else { + values.add(event.getValue()); + } } public List> getEvents() { From dc6f5a6f6acecd5deb4001aa12c6b8bd6b3c5eee Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 15:51:08 +0100 Subject: [PATCH 09/48] extend brooklyn.catalog syntax to support name, description, and icon_url, in addition to the registered-type/id --- .../brooklyn/camp/spi/pdp/DeploymentPlan.java | 10 +++- .../internal/BasicBrooklynCatalog.java | 60 ++++++++++++------- .../rest/resources/CatalogResourceTest.java | 35 +++++++---- .../brooklyn/util/collections/MutableMap.java | 7 +++ 4 files changed, 76 insertions(+), 36 deletions(-) diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java index 1af3ff97f3..8052d0ce37 100644 --- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java +++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/DeploymentPlan.java @@ -98,13 +98,17 @@ public Map getCustomAttributes() { * Does not remove the attribute from the custom attribute map. */ @SuppressWarnings("unchecked") - public Maybe getCustomAttribute(String attributeName, Class type) { + public Maybe getCustomAttribute(String attributeName, Class type, boolean throwIfTypeMismatch) { Object attribute = customAttributes.get(attributeName); if (attribute == null) { return Maybe.absent("Custom attributes does not contain " + attributeName); } else if (!type.isAssignableFrom(attribute.getClass())) { - return Maybe.absent("Custom attribute " + attributeName + " is not of expected type: " + - "expected=" + type.getName() + " actual=" + attribute.getClass().getName()); + String message = "Custom attribute " + attributeName + " is not of expected type: " + + "expected=" + type.getName() + " actual=" + attribute.getClass().getName(); + if (throwIfTypeMismatch) { + throw new IllegalArgumentException(message); + } + return Maybe.absent(message); } else { return Maybe.of((T) attribute); } diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index 94118639a9..94c2ca5815 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -22,6 +22,7 @@ import brooklyn.catalog.CatalogPredicates; import brooklyn.config.BrooklynServerConfig; import brooklyn.management.ManagementContext; +import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.javalang.AggregateClassLoader; import brooklyn.util.javalang.LoadedClassLoader; @@ -189,42 +190,59 @@ private CatalogItemDtoAbstract getAbstractCatalogItem(Catalog private CatalogItemDtoAbstract getAbstractCatalogItem(String yaml) { DeploymentPlan plan = makePlanFromYaml(yaml); - String name = null; CatalogLibrariesDto libraries = null; - Maybe possibleCatalog = plan.getCustomAttribute("brooklyn.catalog", Map.class); + @SuppressWarnings("rawtypes") + Maybe possibleCatalog = plan.getCustomAttribute("brooklyn.catalog", Map.class, true); + MutableMap catalog = MutableMap.of(); if (possibleCatalog.isPresent()) { - Map catalog = possibleCatalog.get(); - Map cast = (Map) possibleCatalog.get(); - if (catalog.containsKey("name") && catalog.get("name") != null) { - name = String.valueOf(catalog.get("name")); - } - Object possibleLibraries = catalog.get("libraries"); - if (possibleLibraries != null) { - if (possibleLibraries instanceof List) { - libraries = CatalogLibrariesDto.fromList((List) possibleLibraries); - } - } + @SuppressWarnings("unchecked") + Map catalog2 = (Map) possibleCatalog.get(); + catalog.putAll(catalog2); } - // TODO #3 support version info + Maybe possibleLibraries = catalog.getMaybe("libraries"); + if (possibleLibraries.isAbsent()) possibleLibraries = catalog.getMaybe("brooklyn.libraries"); + if (possibleLibraries.isPresentAndNonNull()) { + if (!(possibleLibraries.get() instanceof List)) + throw new IllegalArgumentException("Libraries should be a list, not "+possibleLibraries.get()); + libraries = CatalogLibrariesDto.fromList((List) possibleLibraries.get()); + } + // TODO clear up the abundance of id, name, registered type, java type + String registeredTypeName = (String) catalog.getMaybe("id").orNull(); + if (Strings.isBlank(registeredTypeName)) + registeredTypeName = (String) catalog.getMaybe("name").orNull(); // take name from plan if not specified in brooklyn.catalog section not supplied - if (Strings.isBlank(name)) { - name = plan.getName(); - if (Strings.isBlank(name)) { + if (Strings.isBlank(registeredTypeName)) { + registeredTypeName = plan.getName(); + if (Strings.isBlank(registeredTypeName)) { if (plan.getServices().size()==1) { Service svc = Iterables.getOnlyElement(plan.getServices()); - name = svc.getServiceType(); + registeredTypeName = svc.getServiceType(); } } } + // TODO long-term: support applications / templates, policies + // build the catalog item from the plan (as CatalogItem for now) - // TODO applications / templates - // TODO long-term support policies etc + CatalogEntityItemDto dto = CatalogItems.newEntityFromPlan(registeredTypeName, libraries, plan, yaml); + + // and populate other fields + Maybe name = catalog.getMaybe("name"); + if (name.isPresent()) dto.name = (String)name.get(); + + Maybe description = catalog.getMaybe("description"); + if (description.isPresent()) dto.description = (String)description.get(); + + Maybe iconUrl = catalog.getMaybe("iconUrl"); + if (iconUrl.isAbsent()) iconUrl = catalog.getMaybe("icon_url"); + if (iconUrl.isPresent()) dto.iconUrl = (String)iconUrl.get(); + + // TODO #3 support version info - return CatalogItems.newEntityFromPlan(name, libraries, plan, yaml); + return dto; } private DeploymentPlan makePlanFromYaml(String yaml) { diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index 51c56d8989..64e3ffaf20 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -20,10 +20,12 @@ import org.testng.annotations.Test; import org.testng.reporters.Files; +import brooklyn.catalog.CatalogItem; import brooklyn.policy.autoscaling.AutoScalerPolicy; import brooklyn.rest.domain.CatalogEntitySummary; import brooklyn.rest.domain.CatalogItemSummary; import brooklyn.rest.testing.BrooklynRestResourceTest; +import brooklyn.util.collections.MutableList; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; @@ -47,17 +49,18 @@ protected void setUpResources() throws Exception { @Test public void testRegisterCustomEntity() { String registeredTypeName = "my.catalog.app.id"; + String bundleUrl = "http://myurl/my.jar"; String yaml = "name: "+registeredTypeName+"\n"+ // FIXME name above should be unnecessary when brooklyn.catalog below is working "brooklyn.catalog:\n"+ - "- id: " + registeredTypeName + "\n"+ - "- name: My Catalog App\n"+ - "- description: My description\n"+ - "- icon_url: classpath://path/to/myicon.jpg\n"+ - "- version: 0.1.2\n"+ - "- brooklyn.libraries:\n"+ - " - url: http://myurl/my.jar\n"+ + " id: " + registeredTypeName + "\n"+ + " name: My Catalog App\n"+ + " description: My description\n"+ + " icon_url: classpath://path/to/myicon.jpg\n"+ + " version: 0.1.2\n"+ + " libraries:\n"+ + " - url: " + bundleUrl + "\n"+ "\n"+ "services:\n"+ "- type: brooklyn.test.entity.TestEntity\n"; @@ -77,11 +80,19 @@ public void testRegisterCustomEntity() { Assert.assertNotNull(entityItem.getPlanYaml()); Assert.assertTrue(entityItem.getPlanYaml().contains("brooklyn.test.entity.TestEntity")); - // TODO @sjcorbett more checks, when brooklyn.catalog working -// assertEquals(entityItem.getId(), registeredTypeName); -// assertEquals(entityItem.getName(), "My Catalog App"); -// assertEquals(entityItem.getDescription(), "My description"); -// assertEquals(entityItem.getIconUrl(), "classpath://path/to/myicon.jpg"); + assertEquals(entityItem.getId(), registeredTypeName); + + // and internally let's check we have libraries + CatalogItem item = getManagementContext().getCatalog().getCatalogItem(registeredTypeName); + Assert.assertNotNull(item); + List libs = item.getLibraries().getBundles(); + assertEquals(libs, MutableList.of(bundleUrl)); + + // now let's check other things on the item + assertEquals(entityItem.getName(), "My Catalog App"); + assertEquals(entityItem.getDescription(), "My description"); + assertEquals(entityItem.getIconUrl(), "/v1/catalog/icon/my.catalog.app.id"); + assertEquals(item.getIconUrl(), "classpath://path/to/myicon.jpg"); } @Test diff --git a/utils/common/src/main/java/brooklyn/util/collections/MutableMap.java b/utils/common/src/main/java/brooklyn/util/collections/MutableMap.java index 76a2967d8c..dddd5bae5d 100644 --- a/utils/common/src/main/java/brooklyn/util/collections/MutableMap.java +++ b/utils/common/src/main/java/brooklyn/util/collections/MutableMap.java @@ -7,6 +7,8 @@ import javax.annotation.Nullable; +import brooklyn.util.guava.Maybe; + import com.google.common.base.Predicate; import com.google.common.collect.ImmutableMap; @@ -111,6 +113,11 @@ public MutableMap addIfNotNull(K key, V value) { return this; } + public Maybe getMaybe(K key) { + if (containsKey(key)) return Maybe.of(get(key)); + return Maybe.absent("No entry for key '"+key+"' in this map"); + } + public ImmutableMap toImmutable() { return ImmutableMap.copyOf(this); } From 1cb09a46553c5cecb91773650effdb5b427a6154 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 16:46:29 +0100 Subject: [PATCH 10/48] some helpers to extract exported packages from osgi --- .../main/java/brooklyn/util/osgi/Osgis.java | 64 +++++++++++++++++++ .../management/osgi/OsgiStandaloneTest.java | 26 ++++++++ 2 files changed, 90 insertions(+) diff --git a/core/src/main/java/brooklyn/util/osgi/Osgis.java b/core/src/main/java/brooklyn/util/osgi/Osgis.java index 05fac8e68d..d4734c3d42 100644 --- a/core/src/main/java/brooklyn/util/osgi/Osgis.java +++ b/core/src/main/java/brooklyn/util/osgi/Osgis.java @@ -1,18 +1,25 @@ package brooklyn.util.osgi; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.List; import java.util.Map; +import java.util.jar.Manifest; + +import javax.annotation.Nullable; import org.apache.felix.framework.FrameworkFactory; +import org.apache.felix.framework.util.StringMap; +import org.apache.felix.framework.util.manifestparser.ManifestParser; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.Version; import org.osgi.framework.launch.Framework; +import org.osgi.framework.wiring.BundleCapability; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,6 +28,7 @@ import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.guava.Maybe; +import brooklyn.util.stream.Streams; import com.google.common.annotations.Beta; import com.google.common.base.Joiner; @@ -154,4 +162,60 @@ public static Bundle install(Framework framework, String url) throws BundleExcep return framework.getBundleContext().installBundle(url, stream); } + public static class ManifestHelper { + + private static ManifestParser parse; + private Manifest manifest; + private String source; + + private static final String WIRING_PACKAGE = "osgi.wiring.package"; + + public static ManifestHelper forManifestContents(String contents) throws IOException, BundleException { + ManifestHelper result = forManifest(Streams.newInputStreamWithContents(contents)); + result.source = contents; + return result; + } + + public static ManifestHelper forManifest(InputStream in) throws IOException, BundleException { + return forManifest(new Manifest(in)); + } + + public static ManifestHelper forManifest(Manifest manifest) throws BundleException { + ManifestHelper result = new ManifestHelper(); + result.manifest = manifest; + parse = new ManifestParser(null, null, null, new StringMap(manifest.getMainAttributes())); + return result; + } + + public String getSymbolicName() { + return parse.getSymbolicName(); + } + + public Version getVersion() { + return parse.getBundleVersion(); + } + + public String getSymbolicNameVersion() { + return getSymbolicName()+":"+getVersion(); + } + + public List getExportedPackages() { + MutableList result = MutableList.of(); + for (BundleCapability c: parse.getCapabilities()) { + if (WIRING_PACKAGE.equals(c.getNamespace())) { + result.add((String)c.getAttributes().get(WIRING_PACKAGE)); + } + } + return result; + } + + @Nullable public String getSource() { + return source; + } + + public Manifest getManifest() { + return manifest; + } + } + } diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java index 2461f2754d..e4c788d3bd 100644 --- a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java +++ b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java @@ -2,18 +2,26 @@ import java.io.File; import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; import org.apache.commons.io.FileUtils; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; import org.osgi.framework.launch.Framework; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import brooklyn.util.collections.MutableSet; import brooklyn.util.os.Os; import brooklyn.util.osgi.Osgis; +import brooklyn.util.osgi.Osgis.ManifestHelper; +import brooklyn.util.stream.Streams; /** tests some assumptions about OSGi behaviour, in standalone mode (not part of brooklyn). * @@ -26,6 +34,8 @@ * */ public class OsgiStandaloneTest { + private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class); + public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:///brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar"; protected Framework framework = null; @@ -116,5 +126,21 @@ protected void setAMultiplier(Bundle bundle, int newMultiplier) throws Exception Class aClass = bundle.loadClass("brooklyn.test.osgi.TestA"); aClass.getField("multiplier").set(null, newMultiplier); } + + @Test + public void testReadAManifest() throws Exception { + Enumeration manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"); + log.info("Bundles and exported packages:"); + MutableSet allPackages = MutableSet.of(); + while (manifests.hasMoreElements()) { + ManifestHelper mf = Osgis.ManifestHelper.forManifestContents(Streams.readFullyString( manifests.nextElement().openStream() )); + List mfPackages = mf.getExportedPackages(); + log.info(" "+mf.getSymbolicNameVersion()+": "+mfPackages); + allPackages.addAll(mfPackages); + } + log.info("Total export package count: "+allPackages.size()); + Assert.assertTrue(allPackages.size()>20, "did not find enough packages"); // probably much larger + Assert.assertTrue(allPackages.contains(Osgis.class.getPackage().getName())); + } } From 8ef90a4b8a1ea0d794f82153e9c95ef0193224fe Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 4 Jul 2014 16:50:05 +0100 Subject: [PATCH 11/48] use official constant rather than magic string --- core/src/main/java/brooklyn/util/osgi/Osgis.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/brooklyn/util/osgi/Osgis.java b/core/src/main/java/brooklyn/util/osgi/Osgis.java index d4734c3d42..052113b41c 100644 --- a/core/src/main/java/brooklyn/util/osgi/Osgis.java +++ b/core/src/main/java/brooklyn/util/osgi/Osgis.java @@ -19,6 +19,7 @@ import org.osgi.framework.Constants; import org.osgi.framework.Version; import org.osgi.framework.launch.Framework; +import org.osgi.framework.namespace.PackageNamespace; import org.osgi.framework.wiring.BundleCapability; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -168,7 +169,7 @@ public static class ManifestHelper { private Manifest manifest; private String source; - private static final String WIRING_PACKAGE = "osgi.wiring.package"; + private static final String WIRING_PACKAGE = PackageNamespace.PACKAGE_NAMESPACE; public static ManifestHelper forManifestContents(String contents) throws IOException, BundleException { ManifestHelper result = forManifest(Streams.newInputStreamWithContents(contents)); From 04b7efb012fba7e3f7a9aba13a0eb4378ba2ed4b Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Fri, 4 Jul 2014 20:46:42 +0300 Subject: [PATCH 12/48] Register boot brooklyn bundles found on the classpath. --- .../main/java/brooklyn/util/osgi/Osgis.java | 54 +++++++++++++++++++ .../management/osgi/OsgiStandaloneTest.java | 23 ++++++++ 2 files changed, 77 insertions(+) diff --git a/core/src/main/java/brooklyn/util/osgi/Osgis.java b/core/src/main/java/brooklyn/util/osgi/Osgis.java index 052113b41c..eed5f95978 100644 --- a/core/src/main/java/brooklyn/util/osgi/Osgis.java +++ b/core/src/main/java/brooklyn/util/osgi/Osgis.java @@ -5,6 +5,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.jar.Manifest; @@ -35,6 +40,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.collect.Iterators; /** * utilities for working with osgi. @@ -45,6 +51,7 @@ @Beta public class Osgis { + private static final String BROOKLYN_PACKAGE_PREFIX = "brooklyn."; private static final Logger LOG = LoggerFactory.getLogger(Osgis.class); public static List getBundlesByName(Framework framework, String symbolicName, Predicate versionMatcher) { @@ -132,6 +139,7 @@ public static Framework newFrameworkStarted(String felixCacheDir, boolean clean, Map cfg = MutableMap.copyOf(extraStartupConfig); if (clean) cfg.put(Constants.FRAMEWORK_STORAGE_CLEAN, "onFirstInit"); if (felixCacheDir!=null) cfg.put(Constants.FRAMEWORK_STORAGE, felixCacheDir); + cfg.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, getBrooklynBootBundles()); FrameworkFactory factory = newFrameworkFactory(); Framework framework = factory.newFramework(cfg); @@ -147,6 +155,45 @@ public static Framework newFrameworkStarted(String felixCacheDir, boolean clean, return framework; } + private static String getBrooklynBootBundles() { + Enumeration resources; + try { + resources = Osgis.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); + } catch (IOException e) { + throw Exceptions.propagate(e); + } + + Collection exportPackages = new ArrayList(); + while(resources.hasMoreElements()) { + URL url = resources.nextElement(); + exportPackages.addAll(getBundleExportedPackages(url)); + } + + Iterator brooklynPackages = Iterators.filter(exportPackages.iterator(), new Predicate() { + @Override + public boolean apply(String input) { + return input.startsWith(BROOKLYN_PACKAGE_PREFIX); + } + }); + + String bootBundles = Joiner.on(",").join(brooklynPackages); + LOG.debug("Found the following boot OSGi packages: " + bootBundles); + return bootBundles; + } + + private static Collection getBundleExportedPackages(URL manifestUrl) { + try { + ManifestHelper helper = ManifestHelper.forManifest(manifestUrl); + return helper.getExportedPackages(); + } catch (IOException e) { + LOG.warn("Unable to load manifest from " + manifestUrl + ", ignoring.", e); + } catch (BundleException e) { + LOG.warn("Unable to load manifest from " + manifestUrl + ", ignoring.", e); + } + return Collections.emptyList(); + } + + /** * Installs a bundle from the given URL, doing a check if already installed, and * using the {@link ResourceUtils} loader for this project (brooklyn core) @@ -177,6 +224,13 @@ public static ManifestHelper forManifestContents(String contents) throws IOExcep return result; } + public static ManifestHelper forManifest(URL url) throws IOException, BundleException { + InputStream in = url.openStream(); + ManifestHelper helper = forManifest(in); + in.close(); + return helper; + } + public static ManifestHelper forManifest(InputStream in) throws IOException, BundleException { return forManifest(new Manifest(in)); } diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java index e4c788d3bd..2d4423259f 100644 --- a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java +++ b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java @@ -2,9 +2,11 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.List; +import java.util.jar.JarInputStream; import org.apache.commons.io.FileUtils; import org.osgi.framework.Bundle; @@ -34,9 +36,11 @@ * */ public class OsgiStandaloneTest { + private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class); public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:///brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar"; + private static final String BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL = "/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar"; protected Framework framework = null; private File storageTempDir; @@ -143,4 +147,23 @@ public void testReadAManifest() throws Exception { Assert.assertTrue(allPackages.contains(Osgis.class.getPackage().getName())); } + @Test + public void testReadKnownManifest() throws Exception { + InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL); + JarInputStream jarIn = new JarInputStream(in); + ManifestHelper helper = Osgis.ManifestHelper.forManifest(jarIn.getManifest()); + jarIn.close(); + Assert.assertEquals(helper.getVersion().toString(), "0.1.0"); + Assert.assertTrue(helper.getExportedPackages().contains("brooklyn.osgi.tests")); + } + + @Test + public void testLoadOsgiBundleDependencies() throws Exception { + Bundle bundle = install("classpath:/" + BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL); + Assert.assertNotNull(bundle); + Class aClass = bundle.loadClass("brooklyn.osgi.tests.SimpleApplicationImpl"); + Object aInst = aClass.newInstance(); + Assert.assertNotNull(aInst); + } + } From 27fba7d642044e4225858618e44608a0a38eba9f Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Fri, 4 Jul 2014 22:15:09 +0300 Subject: [PATCH 13/48] Wait for OSGi framework to stop before trying to delete its cache. --- core/src/main/java/brooklyn/management/ha/OsgiManager.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java index 709bf347b9..0129481e80 100644 --- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java +++ b/core/src/main/java/brooklyn/management/ha/OsgiManager.java @@ -47,10 +47,14 @@ public void start() { public void stop() { try { - if (framework!=null) + if (framework!=null) { framework.stop(); + framework.waitForStop(0); + } } catch (BundleException e) { throw Exceptions.propagate(e); + } catch (InterruptedException e) { + throw Exceptions.propagate(e); } osgiTempDir = Os.deleteRecursively(osgiTempDir).asNullOrThrowing(); framework = null; From e73f4c6fd4eb290ac74c73100b972d8c6c16ef40 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Sat, 5 Jul 2014 04:46:26 +0100 Subject: [PATCH 14/48] add BrooklynClassLoadingContext and pass it around when we are instantiating, fixing BrooklynComponentTemplateResolver.loadEntitySpec to use new loaders from CatalogItem. currently BCLC does not really pull his weight, as he is just used for instantiation and is never combined or kept; but as we fix deeper bugs in trying to resolve classes from the purview of another, i think it will become very useful. (already it should be the case -- but needs testing -- that BCLC allows children to be resolved WRT the parent's bundle if they are not declared as catalog items) --- .../java/brooklyn/catalog/CatalogItem.java | 4 + .../BrooklynClassLoadingContext.java | 19 +++ .../brooklyn/catalog/internal/CatalogDo.java | 6 + .../catalog/internal/CatalogItemDo.java | 53 +++----- .../internal/CatalogItemDtoAbstract.java | 15 +++ .../AbstractBrooklynClassLoadingContext.java | 60 +++++++++ ...BrooklynClassLoadingContextSequential.java | 85 ++++++++++++ .../JavaBrooklynClassLoadingContext.java | 50 +++++++ .../OsgiBrooklynClassLoadingContext.java | 64 +++++++++ .../BrooklynAssemblyTemplateInstantiator.java | 27 ++-- .../BrooklynComponentTemplateResolver.java | 117 ++++++++++------- .../creation/BrooklynEntityClassResolver.java | 123 ------------------ .../BrooklynEntityDecorationResolver.java | 20 +-- .../spi/creation/BrooklynEntityMatcher.java | 12 +- ...java => BrooklynYamlTypeInstantiator.java} | 74 +++++------ .../ChefComponentTemplateResolver.java | 15 ++- ... => BrooklynYamlTypeInstantiatorTest.java} | 26 ++-- 17 files changed, 481 insertions(+), 289 deletions(-) create mode 100644 api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java create mode 100644 core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java create mode 100644 core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java create mode 100644 core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java create mode 100644 core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java delete mode 100644 usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityClassResolver.java rename usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/{BrooklynYamlTypeLoader.java => BrooklynYamlTypeInstantiator.java} (69%) rename usage/camp/src/test/java/io/brooklyn/camp/brooklyn/{BrooklynYamlTypeLoaderTest.java => BrooklynYamlTypeInstantiatorTest.java} (54%) diff --git a/api/src/main/java/brooklyn/catalog/CatalogItem.java b/api/src/main/java/brooklyn/catalog/CatalogItem.java index ebcdae8f5f..15f30c1bbd 100644 --- a/api/src/main/java/brooklyn/catalog/CatalogItem.java +++ b/api/src/main/java/brooklyn/catalog/CatalogItem.java @@ -5,6 +5,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; + import com.google.common.annotations.Beta; @Beta @@ -48,5 +51,6 @@ public static interface CatalogItemLibraries { /** return underlying YAML for this item, if known */ @Nullable public String getPlanYaml(); + BrooklynClassLoadingContext newClassLoadingContext(final ManagementContext mgmt); } diff --git a/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java b/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java new file mode 100644 index 0000000000..e1ba3b5405 --- /dev/null +++ b/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java @@ -0,0 +1,19 @@ +package brooklyn.management.classloading; + +import brooklyn.management.ManagementContext; +import brooklyn.util.guava.Maybe; + +/** + * Provides functionality for loading classes based on the current context + * (e.g. catalog item, entity, etc). + */ +public interface BrooklynClassLoadingContext { + + public ManagementContext getManagementContext(); + public Class loadClass(String className); + public Class loadClass(String className, Class type); + + public Maybe> tryLoadClass(String className); + public Maybe> tryLoadClass(String className, Class type); + +} diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java index 5cda68dc61..9ae2f623a9 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java @@ -11,6 +11,8 @@ import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes; import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.management.internal.ManagementContextInternal; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.javalang.AggregateClassLoader; @@ -277,5 +279,9 @@ public ClassLoader getRootClassLoader() { if (parent!=null) return parent.getRootClassLoader(); return getRecursiveClassLoader(); } + + public BrooklynClassLoadingContext newClassLoadingContext() { + return new JavaBrooklynClassLoadingContext(mgmt, getRootClassLoader()); + } } diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java index 6939a5534a..e275a3cb70 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java @@ -1,16 +1,12 @@ package brooklyn.catalog.internal; -import java.util.List; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import brooklyn.catalog.CatalogItem; import brooklyn.management.ManagementContext; -import brooklyn.management.ha.OsgiManager; -import brooklyn.management.internal.ManagementContextInternal; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.guava.Maybe; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.BrooklynClassLoadingContextSequential; import com.google.common.base.Preconditions; @@ -82,45 +78,26 @@ public CatalogItemLibraries getLibraries() { } /** @deprecated since 0.7.0 this is the legacy mechanism; still needed for policies and apps, but being phased out. - * new items should use {@link #getYaml()} */ + * new items should use {@link #getYaml()} and {@link #newClassLoadingContext(ManagementContext, BrooklynClassLoadingContext)} */ @Deprecated public Class getJavaClass() { if (javaClass==null) loadJavaClass(null); return javaClass; } + @SuppressWarnings("deprecation") + public BrooklynClassLoadingContext newClassLoadingContext(final ManagementContext mgmt) { + BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt); + result.add(itemDto.newClassLoadingContext(mgmt)); + result.addSecondary(catalog.newClassLoadingContext()); + return result; + } + @SuppressWarnings("unchecked") - protected Class loadJavaClass(ManagementContext mgmt) { - Maybe> clazz = null; - try { - if (javaClass!=null) return javaClass; - - if (mgmt!=null) { - Maybe osgi = ((ManagementContextInternal)mgmt).getOsgiManager(); - if (osgi.isPresent() && getLibraries()!=null) { - // TODO getLibraries() should never be null but sometimes it is still - // e.g. run CatalogResourceTest without the above check - List bundles = getLibraries().getBundles(); - if (bundles!=null && !bundles.isEmpty()) { - clazz = osgi.get().tryResolveClass(getJavaType(), bundles); - if (clazz.isPresent()) { - return (Class) clazz.get(); - } - } - } - } - - javaClass = (Class) catalog.getRootClassLoader().loadClass(getJavaType()); - return javaClass; - } catch (Throwable e) { - Exceptions.propagateIfFatal(e); - if (clazz!=null) { - // if OSGi bundles were defined and failed, then prefer to throw its error message - clazz.get(); - } - // else throw the java error - throw Exceptions.propagate(e); - } + Class loadJavaClass(final ManagementContext mgmt) { + if (javaClass!=null) return javaClass; + javaClass = (Class)newClassLoadingContext(mgmt).loadClass(getJavaType()); + return javaClass; } @Override diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java index 21f0091907..93675ffa3b 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java @@ -4,6 +4,10 @@ import javax.annotation.Nullable; import brooklyn.catalog.CatalogItem; +import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.BrooklynClassLoadingContextSequential; +import brooklyn.management.classloading.OsgiBrooklynClassLoadingContext; public abstract class CatalogItemDtoAbstract implements CatalogItem { @@ -91,6 +95,17 @@ private synchronized void loadSerializer() { serializer = new CatalogXmlSerializer(); } + public BrooklynClassLoadingContext newClassLoadingContext(final ManagementContext mgmt) { + BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt); + + if (getLibraries()!=null && getLibraries().getBundles()!=null && !getLibraries().getBundles().isEmpty()) + // TODO getLibraries() should never be null but sometimes it is still + // e.g. run CatalogResourceTest without the above check + result.add(new OsgiBrooklynClassLoadingContext(mgmt, getLibraries().getBundles())); + + return result; + } + public abstract Class getSpecType(); } diff --git a/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java new file mode 100644 index 0000000000..dcf4437851 --- /dev/null +++ b/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java @@ -0,0 +1,60 @@ +package brooklyn.management.classloading; + +import brooklyn.management.ManagementContext; +import brooklyn.util.guava.Maybe; + +import com.google.common.base.Objects; + +public abstract class AbstractBrooklynClassLoadingContext implements BrooklynClassLoadingContext { + + protected final ManagementContext mgmt; + + public AbstractBrooklynClassLoadingContext(ManagementContext mgmt) { + this.mgmt = mgmt; + } + + @Override + public ManagementContext getManagementContext() { + return mgmt; + } + + @Override + public Class loadClass(String className) { + return tryLoadClass(className).get(); + } + + @Override + // this is the only one left for subclasses + public abstract Maybe> tryLoadClass(String className); + + @Override + public Class loadClass(String className, Class type) { + return tryLoadClass(className, type).get(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Maybe> tryLoadClass(String className, Class type) { + Maybe> result = tryLoadClass(className); + if (result.isAbsent()) return (Maybe)result; + Class clazz = result.get(); + if (type.isAssignableFrom(clazz)) return (Maybe)result; + throw new ClassCastException(className+" is not an instance of "+type); + } + + @Override + public abstract String toString(); + + @Override + public int hashCode() { + return Objects.hashCode(mgmt); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BrooklynClassLoadingContext)) return false; + if (!Objects.equal(mgmt, ((BrooklynClassLoadingContext)obj).getManagementContext())) return false; + return true; + } + +} diff --git a/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java b/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java new file mode 100644 index 0000000000..4b714eb73e --- /dev/null +++ b/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java @@ -0,0 +1,85 @@ +package brooklyn.management.classloading; + +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Objects; + +import brooklyn.management.ManagementContext; +import brooklyn.util.collections.MutableList; +import brooklyn.util.collections.MutableSet; +import brooklyn.util.guava.Maybe; + +public final class BrooklynClassLoadingContextSequential extends AbstractBrooklynClassLoadingContext { + + private static final Logger log = LoggerFactory.getLogger(BrooklynClassLoadingContextSequential.class); + + private final List primaries = MutableList.of(); + // secondaries used to put java classloader last + private final Set secondaries = MutableSet.of(); + + public BrooklynClassLoadingContextSequential(ManagementContext mgmt, BrooklynClassLoadingContext ...targets) { + super(mgmt); + for (BrooklynClassLoadingContext target: targets) + add(target); + } + + public void add(BrooklynClassLoadingContext target) { + if (target instanceof BrooklynClassLoadingContextSequential) { + for (BrooklynClassLoadingContext targetN: ((BrooklynClassLoadingContextSequential)target).primaries ) + add(targetN); + for (BrooklynClassLoadingContext targetN: ((BrooklynClassLoadingContextSequential)target).secondaries ) + addSecondary(targetN); + } else { + this.primaries.add( target ); + } + } + + /** @since 0.7.0 only for supporting legacy java-classloading based catalog */ + @Deprecated + public void addSecondary(BrooklynClassLoadingContext target) { + if (!(target instanceof JavaBrooklynClassLoadingContext)) { + // support for legacy catalog classloader only + log.warn("Only Java classloaders should be secondary"); + } + this.secondaries.add( target ); + } + + public Maybe> tryLoadClass(String className) { + for (BrooklynClassLoadingContext target: primaries) { + Maybe> clazz = target.tryLoadClass(className); + if (clazz.isPresent()) + return clazz; + } + for (BrooklynClassLoadingContext target: secondaries) { + Maybe> clazz = target.tryLoadClass(className); + if (clazz.isPresent()) + return clazz; + } + + return Maybe.absent("Unable to load "+className+" from "+primaries); + } + + @Override + public String toString() { + return "classload:"+primaries+";"+secondaries; + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), primaries, secondaries); + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) return false; + if (!(obj instanceof BrooklynClassLoadingContextSequential)) return false; + if (!Objects.equal(primaries, ((BrooklynClassLoadingContextSequential)obj).primaries)) return false; + if (!Objects.equal(secondaries, ((BrooklynClassLoadingContextSequential)obj).secondaries)) return false; + return true; + } + +} diff --git a/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java new file mode 100644 index 0000000000..8cae3bea8d --- /dev/null +++ b/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java @@ -0,0 +1,50 @@ +package brooklyn.management.classloading; + +import com.google.common.base.Objects; + +import brooklyn.management.ManagementContext; +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.guava.Maybe; + +public class JavaBrooklynClassLoadingContext extends AbstractBrooklynClassLoadingContext { + + private final ClassLoader loader; + + public JavaBrooklynClassLoadingContext(ManagementContext mgmt, ClassLoader loader) { + super(mgmt); + this.loader = loader; + } + + public static JavaBrooklynClassLoadingContext newDefault(ManagementContext mgmt) { + return new JavaBrooklynClassLoadingContext(mgmt, JavaBrooklynClassLoadingContext.class.getClassLoader()); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Maybe> tryLoadClass(String className) { + try { + return (Maybe) Maybe.of(loader.loadClass(className)); + } catch (Exception e) { + Exceptions.propagateIfFatal(e); + return Maybe.absent(e); + } + } + + @Override + public String toString() { + return "java:"+loader; + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), loader); + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) return false; + if (!(obj instanceof JavaBrooklynClassLoadingContext)) return false; + if (!Objects.equal(loader, ((JavaBrooklynClassLoadingContext)obj).loader)) return false; + return true; + } + +} diff --git a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java new file mode 100644 index 0000000000..50650780a4 --- /dev/null +++ b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java @@ -0,0 +1,64 @@ +package brooklyn.management.classloading; + +import java.util.List; + +import brooklyn.management.ManagementContext; +import brooklyn.management.ha.OsgiManager; +import brooklyn.management.internal.ManagementContextInternal; +import brooklyn.util.guava.Maybe; + +import com.google.common.base.Objects; + +public class OsgiBrooklynClassLoadingContext extends AbstractBrooklynClassLoadingContext { + + private final List bundles; + + public OsgiBrooklynClassLoadingContext(ManagementContext mgmt, List bundles) { + super(mgmt); + this.bundles = bundles; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Maybe> tryLoadClass(String className) { + Maybe> clazz = null; + Maybe osgi = null; + if (mgmt!=null) { + osgi = ((ManagementContextInternal)mgmt).getOsgiManager(); + if (osgi.isPresent() && bundles!=null && !bundles.isEmpty()) { + clazz = osgi.get().tryResolveClass(className, bundles); + if (clazz.isPresent()) + return (Maybe)clazz; + } + } + + if (clazz!=null) { + // if OSGi bundles were defined and failed, then use its error message + return (Maybe)clazz; + } + // else determine best message + if (mgmt==null) return Maybe.absent("No mgmt context available for loading "+className); + if (osgi!=null && osgi.isAbsent()) return Maybe.absent("OSGi not available on mgmt for loading "+className); + if (bundles==null || bundles.isEmpty()) + return Maybe.absent("No bundles available for loading "+className); + return Maybe.absent("Inconsistent state ("+mgmt+"/"+osgi+"/"+bundles+" loading "+className); + } + + @Override + public String toString() { + return "OSGi:"+bundles; + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), bundles); + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) return false; + if (!(obj instanceof OsgiBrooklynClassLoadingContext)) return false; + if (!Objects.equal(bundles, ((OsgiBrooklynClassLoadingContext)obj).bundles)) return false; + return true; + } + +} diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index de9a63eb0e..50fa508a0b 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -35,6 +35,8 @@ import brooklyn.location.Location; import brooklyn.management.ManagementContext; import brooklyn.management.Task; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.util.collections.MutableList; import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; @@ -149,7 +151,7 @@ protected AppOrSpec createApplicationFromCatalog(CampPlatform platform, CatalogI if (Strings.isEmpty(type)) { clazz = BasicApplication.class; } else { - clazz = BrooklynEntityClassResolver.resolveEntity(type, mgmt); + clazz = item.newClassLoadingContext(mgmt).loadClass(type, Entity.class); } try { @@ -266,11 +268,12 @@ protected EntitySpec createApplicationFromNonCatalogCampT // AssemblyTemplates created via PDP, _specifying_ then entities to put in final ManagementContext mgmt = getBrooklynManagementContext(platform); - BrooklynComponentTemplateResolver resolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, template); + BrooklynComponentTemplateResolver resolver = BrooklynComponentTemplateResolver.Factory.newInstance( + JavaBrooklynClassLoadingContext.newDefault(mgmt), template); EntitySpec app = resolver.resolveSpec(StartableApplication.class, BasicApplicationImpl.class); // first build the children into an empty shell app - buildTemplateServicesAsSpecs(template, app, mgmt); + buildTemplateServicesAsSpecs(JavaBrooklynClassLoadingContext.newDefault(mgmt), template, app); if (shouldUnwrap(template, app)) { EntitySpec oldApp = app; @@ -317,26 +320,30 @@ protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec root, ManagementContext mgmt) { + private void buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, EntitySpec root) { for (ResolvableLink ctl: template.getPlatformComponentTemplates().links()) { PlatformComponentTemplate appChildComponentTemplate = ctl.resolve(); - BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, appChildComponentTemplate); + BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(loader, appChildComponentTemplate); EntitySpec spec = entityResolver.resolveSpec(); root.child(spec); - buildChildrenEntitySpecs(mgmt, spec, entityResolver.getChildren(appChildComponentTemplate.getCustomAttributes())); + BrooklynClassLoadingContext newLoader = entityResolver.loader; + buildChildrenEntitySpecs(newLoader, spec, entityResolver.getChildren(appChildComponentTemplate.getCustomAttributes())); } } - protected void buildChildrenEntitySpecs(ManagementContext mgmt, EntitySpec parent, List> childConfig) { + protected void buildChildrenEntitySpecs(BrooklynClassLoadingContext loader, EntitySpec parent, List> childConfig) { if (childConfig != null) { for (Map childAttrs : childConfig) { - BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, childAttrs); + BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(loader, childAttrs); EntitySpec spec = entityResolver.resolveSpec(); parent.child(spec); - - buildChildrenEntitySpecs(mgmt, spec, entityResolver.getChildren(childAttrs)); + + // get the new loader in case the OSGi bundles from parent were added; + // not so important now but if we start working with versions this may be important + BrooklynClassLoadingContext newLoader = entityResolver.loader; + buildChildrenEntitySpecs(newLoader, spec, entityResolver.getChildren(childAttrs)); } } } diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index c0667ca418..c1867375d2 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -14,6 +14,7 @@ import javax.annotation.Nullable; +import brooklyn.catalog.CatalogItem; import brooklyn.config.ConfigKey; import brooklyn.entity.Application; import brooklyn.entity.Entity; @@ -28,6 +29,8 @@ import brooklyn.entity.proxying.InternalEntityFactory; import brooklyn.location.Location; import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.BrooklynClassLoadingContextSequential; import brooklyn.management.internal.ManagementContextInternal; import brooklyn.util.collections.MutableList; import brooklyn.util.collections.MutableSet; @@ -52,10 +55,11 @@ */ public class BrooklynComponentTemplateResolver { + BrooklynClassLoadingContext loader; final ManagementContext mgmt; final ConfigBag attrs; final Maybe template; - final BrooklynYamlTypeLoader.Factory loader; + final BrooklynYamlTypeInstantiator.Factory yamlLoader; AtomicBoolean alreadyBuilt = new AtomicBoolean(false); public static class Factory { @@ -73,25 +77,25 @@ private static Class computeResolve return null; } - public static BrooklynComponentTemplateResolver newInstance(ManagementContext mgmt, Map childAttrs) { - return newInstance(mgmt, ConfigBag.newInstance(childAttrs), null); + public static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext loader, Map childAttrs) { + return newInstance(loader, ConfigBag.newInstance(childAttrs), null); } - public static BrooklynComponentTemplateResolver newInstance(ManagementContext mgmt, AbstractResource template) { - return newInstance(mgmt, ConfigBag.newInstance(template.getCustomAttributes()), template); + public static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext loader, AbstractResource template) { + return newInstance(loader, ConfigBag.newInstance(template.getCustomAttributes()), template); } - public static BrooklynComponentTemplateResolver newInstance(ManagementContext mgmt, String serviceType) { - return newInstance(mgmt, ConfigBag.newInstance().configureStringKey("serviceType", serviceType), null); + public static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext loader, String serviceType) { + return newInstance(loader, ConfigBag.newInstance().configureStringKey("serviceType", serviceType), null); } - private static BrooklynComponentTemplateResolver newInstance(ManagementContext mgmt, ConfigBag attrs, AbstractResource optionalTemplate) { + private static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext loader, ConfigBag attrs, AbstractResource optionalTemplate) { Class rt = computeResolverType(null, optionalTemplate, attrs); if (rt==null) // use default rt = BrooklynComponentTemplateResolver.class; try { - return (BrooklynComponentTemplateResolver) rt.getConstructors()[0].newInstance(mgmt, attrs, optionalTemplate); + return (BrooklynComponentTemplateResolver) rt.getConstructors()[0].newInstance(loader, attrs, optionalTemplate); } catch (Exception e) { throw Exceptions.propagate(e); } } @@ -108,35 +112,29 @@ private static String getDeclaredType(String knownServiceType, AbstractResource } private static String extractServiceTypeAttribute(@Nullable ConfigBag attrs) { - return BrooklynYamlTypeLoader.LoaderFromKey.extractTypeName("service", attrs).orNull(); + return BrooklynYamlTypeInstantiator.InstantiatorFromKey.extractTypeName("service", attrs).orNull(); } - public static boolean supportsType(ManagementContext mgmt, String serviceType) { + public static boolean supportsType(BrooklynClassLoadingContext loader, String serviceType) { Class type = computeResolverType(serviceType, null, null); if (type!=null) return true; - // can't tell by a prefix; try looking it up - try { - newInstance(mgmt, serviceType).loadEntityClass(); - return true; - } catch (Exception e) { - Exceptions.propagateIfFatal(e); - return false; - } + return newInstance(loader, serviceType).canResolve(); } } - public BrooklynComponentTemplateResolver(ManagementContext mgmt, ConfigBag attrs, AbstractResource optionalTemplate) { - this.mgmt = mgmt; + public BrooklynComponentTemplateResolver(BrooklynClassLoadingContext loader, ConfigBag attrs, AbstractResource optionalTemplate) { + this.loader = loader; + this.mgmt = loader.getManagementContext(); this.attrs = ConfigBag.newInstanceCopying(attrs); this.template = Maybe.fromNullable(optionalTemplate); - this.loader = new BrooklynYamlTypeLoader.Factory(mgmt, this); + this.yamlLoader = new BrooklynYamlTypeInstantiator.Factory(loader, this); } protected String getDeclaredType() { return Factory.getDeclaredType(null, template.orNull(), attrs); } - protected String getJavaType() { + protected String getCatalogIdOrJavaType() { String type = getDeclaredType(); type = Strings.removeFromStart(type, "brooklyn:", "java:"); @@ -150,18 +148,42 @@ protected String getJavaType() { return type; } + + /** Returns the CatalogItem if there is one for the given type; + * (if no type, callers should fall back to default classloading) + */ + @Nullable + public CatalogItem> getCatalogItem() { + return loader.getManagementContext().getCatalog().getCatalogItem(Entity.class, getCatalogIdOrJavaType()); + } - @SuppressWarnings("unchecked") - public Class loadEntityClass() { - return (Class) loader.type(getJavaType()).getType(Entity.class); + public boolean canResolve() { + if (getCatalogItem()!=null) + return true; + if (loader.tryLoadClass(getCatalogIdOrJavaType(), Entity.class).isPresent()) + return true; + return false; } + /** returns the entity class, if needed in contexts which scan its statics for example */ + public Class loadEntityClass() { + CatalogItem> item = getCatalogItem(); + String typeName = getCatalogIdOrJavaType(); + if (item!=null) { + loader = new BrooklynClassLoadingContextSequential(mgmt, item.newClassLoadingContext(mgmt), loader); + typeName = item.getJavaType(); + } + return loader.loadClass(typeName, Entity.class); + } + + /** resolves the spec, updating the loader if a catalog item is loaded */ + @SuppressWarnings("unchecked") public EntitySpec resolveSpec() { - return resolveSpec(this.loadEntityClass(), null); + return (EntitySpec)resolveSpec(loadEntityClass(), null); } @SuppressWarnings({ "rawtypes", "unchecked" }) - public EntitySpec resolveSpec(Class type, Class optionalImpl) { + public EntitySpec resolveSpec(Class type, @Nullable Class optionalImpl) { if (alreadyBuilt.getAndSet(true)) throw new IllegalStateException("Spec can only be used once: "+this); @@ -206,9 +228,9 @@ public EntitySpec resolveSpec(Class type, Class void decorateSpec(EntitySpec spec) { - new BrooklynEntityDecorationResolver.PolicySpecResolver(loader).decorate(spec, attrs); - new BrooklynEntityDecorationResolver.EnricherSpecResolver(loader).decorate(spec, attrs); - new BrooklynEntityDecorationResolver.InitializerResolver(loader).decorate(spec, attrs); + new BrooklynEntityDecorationResolver.PolicySpecResolver(yamlLoader).decorate(spec, attrs); + new BrooklynEntityDecorationResolver.EnricherSpecResolver(yamlLoader).decorate(spec, attrs); + new BrooklynEntityDecorationResolver.InitializerResolver(yamlLoader).decorate(spec, attrs); configureEntityConfig(spec); } @@ -255,12 +277,12 @@ private void configureEntityConfig(EntitySpec spec) { Set keyNamesUsed = new LinkedHashSet(); for (FlagConfigKeyAndValueRecord r: records) { if (r.getFlagMaybeValue().isPresent()) { - Object transformed = new SpecialFlagsTransformer(mgmt).transformSpecialFlags(r.getFlagMaybeValue().get(), mgmt); + Object transformed = new SpecialFlagsTransformer(loader).transformSpecialFlags(r.getFlagMaybeValue().get()); spec.configure(r.getFlagName(), transformed); keyNamesUsed.add(r.getFlagName()); } if (r.getConfigKeyMaybeValue().isPresent()) { - Object transformed = new SpecialFlagsTransformer(mgmt).transformSpecialFlags(r.getConfigKeyMaybeValue().get(), mgmt); + Object transformed = new SpecialFlagsTransformer(loader).transformSpecialFlags(r.getConfigKeyMaybeValue().get()); spec.configure((ConfigKey)r.getConfigKey(), transformed); keyNamesUsed.add(r.getConfigKey().getName()); } @@ -273,35 +295,36 @@ private void configureEntityConfig(EntitySpec spec) { // we don't let a flag with the same name as a config key override the config key // (that's why we check whether it is used) if (!keyNamesUsed.contains(key)) { - Object transformed = new SpecialFlagsTransformer(mgmt).transformSpecialFlags(bag.getStringKey(key), mgmt); + Object transformed = new SpecialFlagsTransformer(loader).transformSpecialFlags(bag.getStringKey(key)); spec.configure(ConfigKeys.newConfigKey(Object.class, key.toString()), transformed); } } } protected static class SpecialFlagsTransformer implements Function { - final ManagementContext mgmt; - public SpecialFlagsTransformer(ManagementContext mgmt) { - this.mgmt = mgmt; + // TODO this may be large when serialized as it includes the context search bundles + final BrooklynClassLoadingContext loader; + public SpecialFlagsTransformer(BrooklynClassLoadingContext loader) { + this.loader = loader; } public Object apply(Object input) { if (input instanceof Map) - return transformSpecialFlags((Map)input, mgmt); + return transformSpecialFlags((Map)input); else if (input instanceof Set) - return MutableSet.of(transformSpecialFlags((Iterable)input, mgmt)); + return MutableSet.of(transformSpecialFlags((Iterable)input)); else if (input instanceof List) - return MutableList.copyOf(transformSpecialFlags((Iterable)input, mgmt)); + return MutableList.copyOf(transformSpecialFlags((Iterable)input)); else if (input instanceof Iterable) - return transformSpecialFlags((Iterable)input, mgmt); + return transformSpecialFlags((Iterable)input); else - return transformSpecialFlags((Object)input, mgmt); + return transformSpecialFlags((Object)input); } - protected Map transformSpecialFlags(Map flag, final ManagementContext mgmt) { + protected Map transformSpecialFlags(Map flag) { return Maps.transformValues(flag, this); } - protected Iterable transformSpecialFlags(Iterable flag, final ManagementContext mgmt) { + protected Iterable transformSpecialFlags(Iterable flag) { return Iterables.transform(flag, this); } @@ -309,15 +332,15 @@ protected Iterable transformSpecialFlags(Iterable flag, final ManagementCo * Makes additional transformations to the given flag with the extra knowledge of the flag's management context. * @return The modified flag, or the flag unchanged. */ - protected Object transformSpecialFlags(Object flag, ManagementContext mgmt) { + protected Object transformSpecialFlags(Object flag) { if (flag instanceof EntitySpecConfiguration) { EntitySpecConfiguration specConfig = (EntitySpecConfiguration) flag; // TODO: This should called from BrooklynAssemblyTemplateInstantiator.configureEntityConfig // And have transformSpecialFlags(Object flag, ManagementContext mgmt) drill into the Object flag if it's a map or iterable? @SuppressWarnings("unchecked") - Map resolvedConfig = (Map)transformSpecialFlags(specConfig.getSpecConfiguration(), mgmt); + Map resolvedConfig = (Map)transformSpecialFlags(specConfig.getSpecConfiguration()); specConfig.setSpecConfiguration(resolvedConfig); - return Factory.newInstance(mgmt, specConfig.getSpecConfiguration()).resolveSpec(); + return Factory.newInstance(loader, specConfig.getSpecConfiguration()).resolveSpec(); } return flag; } diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityClassResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityClassResolver.java deleted file mode 100644 index 97df22750e..0000000000 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityClassResolver.java +++ /dev/null @@ -1,123 +0,0 @@ -package io.brooklyn.camp.brooklyn.spi.creation; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.NoSuchElementException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.entity.Entity; -import brooklyn.management.ManagementContext; -import brooklyn.management.ha.OsgiManager; -import brooklyn.management.internal.ManagementContextInternal; -import brooklyn.util.guava.Maybe; - -/** - * Resolves a class name to a Class<? extends Entity> with a given - * {@link brooklyn.management.ManagementContext management context}. - */ -public class BrooklynEntityClassResolver { - - private static final Logger LOG = LoggerFactory.getLogger(BrooklynEntityClassResolver.class); - - /** - * Loads the class represented by entityTypeName with the given management context. - * Tries the context's catalogue first, then from its root classloader. - * @throws java.lang.IllegalStateException if no class extending {@link Entity} is found - */ - public static Class resolveEntity(String entityTypeName, ManagementContext mgmt) { - return resolveEntity(entityTypeName, mgmt, Collections.emptyList()); - } - - /** - * Loads the class represented by entityTypeName with the given management context. - * Tries the context's catalogue first, then from its root classloader. - * @throws java.lang.IllegalStateException if no class extending {@link Entity} is found - */ - public static Class resolveEntity(String entityTypeName, ManagementContext mgmt, List context) { - checkNotNull(mgmt, "management context"); - Maybe> entityClazz = Maybe.absent("No bundles defined"); - Maybe> bestError = null; - - if (context!=null && !context.isEmpty()) { - entityClazz = tryLoadEntityFromBundle(entityTypeName, mgmt, context); - if (!entityClazz.isPresent()) { - LOG.warn("Unable to find class "+entityTypeName+" in suggested bundles "+context+"; continuing other mechanisms"); - // we should prefer the error message from above if context is non-empty but class can't be found - bestError = entityClazz; - } - } - - - if (!entityClazz.isPresent()) { - entityClazz = tryLoadEntityFromCatalogue(entityTypeName, mgmt); - } - if (!entityClazz.isPresent()) { - entityClazz = tryLoadFromClasspath(entityTypeName, mgmt); - } - if (!entityClazz.isPresent()) { - LOG.warn("No catalog item for {} and could not load class directly; throwing", entityTypeName); - throw new IllegalStateException("Unable to load class "+ entityTypeName +" (extending Entity) from catalogue or classpath: not found"); - } - if (!Entity.class.isAssignableFrom(entityClazz.get())) { - LOG.warn("Found class {} on classpath but it is not assignable to {}", entityTypeName, Entity.class); - throw new IllegalStateException("Unable to load class "+ entityTypeName +" (extending Entity) from catalogue or classpath: wrong type "+entityClazz.get()); - } - if (!entityClazz.isPresent() && bestError!=null) { - // prefer best error if not found - bestError.get(); - } - return entityClazz.get(); - } - - // TODO deprecate the ones below, make them protected or private - - /** Tries to load the entity with the given class name from the given bundle. */ - public static Maybe> tryLoadEntityFromBundle(String entityTypeName, ManagementContext mgmt, String... bundleUrls) { - return tryLoadEntityFromBundle(entityTypeName, mgmt, Arrays.asList(bundleUrls)); - } - public static Maybe> tryLoadEntityFromBundle(String entityTypeName, ManagementContext mgmt, Iterable bundleUrls) { - LOG.debug("Trying to resolve class {} from bundle {}", entityTypeName, bundleUrls); - Maybe osgiManager = ((ManagementContextInternal) mgmt).getOsgiManager(); - if (!osgiManager.isPresentAndNonNull()) { - if (LOG.isDebugEnabled()) - LOG.debug("Asked to resolve class {} from bundles {} but osgi manager is unavailable in context {}", - new Object[]{entityTypeName, bundleUrls, mgmt}); - return Maybe.absent(); - } - Maybe> clazz = osgiManager.get().tryResolveClass(entityTypeName, bundleUrls); - if (!clazz.isPresent() || !Entity.class.isAssignableFrom(clazz.get())) { - return Maybe.absent(); - } - return clazz; - } - - @SuppressWarnings("unchecked") - public static Maybe> tryLoadEntityFromCatalogue(String entityTypeName, ManagementContext mgmt) { - LOG.debug("Trying to resolve class {} from catalog", entityTypeName); - try { - return (Maybe>)(Maybe) Maybe.>of(mgmt.getCatalog().loadClassByType(entityTypeName, Entity.class)); - } catch (NoSuchElementException e) { - LOG.debug("Class {} not found in catalogue classpath", entityTypeName); - return Maybe.absent(); - } - } - - @SuppressWarnings("unchecked") - public static Maybe> tryLoadFromClasspath(String typeName, ManagementContext mgmt) { - LOG.debug("Trying to resolve class {} from classpath", typeName); - Class clazz; - try { - clazz = (Class) mgmt.getCatalog().getRootClassLoader().loadClass(typeName); - } catch (ClassNotFoundException e) { - LOG.debug("Class {} not found on classpath", typeName); - return Maybe.absent(new Throwable("Could not find "+typeName+" on classpath")); - } - - return Maybe.of(clazz); - } -} diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java index 0ee07125f8..7eb2fe557e 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java @@ -1,6 +1,6 @@ package io.brooklyn.camp.brooklyn.spi.creation; -import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeLoader.LoaderFromKey; +import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.InstantiatorFromKey; import java.util.List; import java.util.Map; @@ -23,10 +23,10 @@ @Beta public abstract class BrooklynEntityDecorationResolver
{ - public final BrooklynYamlTypeLoader.Factory loader; + public final BrooklynYamlTypeInstantiator.Factory instantiator; - protected BrooklynEntityDecorationResolver(BrooklynYamlTypeLoader.Factory loader) { - this.loader = loader; + protected BrooklynEntityDecorationResolver(BrooklynYamlTypeInstantiator.Factory instantiator) { + this.instantiator = instantiator; } public abstract void decorate(EntitySpec entitySpec, ConfigBag attrs); @@ -66,7 +66,7 @@ protected void addDecorationFromJson(Object decorationJson, List
decorations public static class PolicySpecResolver extends BrooklynEntityDecorationResolver> { - protected PolicySpecResolver(BrooklynYamlTypeLoader.Factory loader) { super(loader); } + protected PolicySpecResolver(BrooklynYamlTypeInstantiator.Factory loader) { super(loader); } @Override protected String getDecorationKind() { return "Policy"; } @Override @@ -81,7 +81,7 @@ protected Object getDecorationAttributeJsonValue(ConfigBag attrs) { @Override protected void addDecorationFromJsonMap(Map decorationJson, List> decorations) { - LoaderFromKey decoLoader = loader.from(decorationJson).prefix("policy"); + InstantiatorFromKey decoLoader = instantiator.from(decorationJson).prefix("policy"); // this pattern of creating a spec could be simplified with a "Configurable" superinterface on *Spec decorations.add(PolicySpec.create(decoLoader.getType(Policy.class)) .configure( decoLoader.getConfigMap() )); @@ -90,7 +90,7 @@ protected void addDecorationFromJsonMap(Map decorationJson, List> { - protected EnricherSpecResolver(BrooklynYamlTypeLoader.Factory loader) { super(loader); } + protected EnricherSpecResolver(BrooklynYamlTypeInstantiator.Factory loader) { super(loader); } @Override protected String getDecorationKind() { return "Enricher"; } @Override @@ -105,7 +105,7 @@ protected Object getDecorationAttributeJsonValue(ConfigBag attrs) { @Override protected void addDecorationFromJsonMap(Map decorationJson, List> decorations) { - LoaderFromKey decoLoader = loader.from(decorationJson).prefix("enricher"); + InstantiatorFromKey decoLoader = instantiator.from(decorationJson).prefix("enricher"); decorations.add(EnricherSpec.create(decoLoader.getType(Enricher.class)) .configure( decoLoader.getConfigMap() )); } @@ -113,7 +113,7 @@ protected void addDecorationFromJsonMap(Map decorationJson, List { - protected InitializerResolver(BrooklynYamlTypeLoader.Factory loader) { super(loader); } + protected InitializerResolver(BrooklynYamlTypeInstantiator.Factory loader) { super(loader); } @Override protected String getDecorationKind() { return "Entity initializer"; } @Override @@ -128,7 +128,7 @@ protected Object getDecorationAttributeJsonValue(ConfigBag attrs) { @Override protected void addDecorationFromJsonMap(Map decorationJson, List decorations) { - decorations.add(loader.from(decorationJson).prefix("initializer").newInstance(EntityInitializer.class)); + decorations.add(instantiator.from(decorationJson).prefix("initializer").newInstance(EntityInitializer.class)); } } diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java index 76ffbe3b5e..04ceb66a6c 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java @@ -14,6 +14,7 @@ import brooklyn.entity.Entity; import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.util.collections.MutableMap; import brooklyn.util.config.ConfigBag; import brooklyn.util.exceptions.Exceptions; @@ -44,9 +45,10 @@ public boolean accepts(Object deploymentPlanItem) { * or null if not supported */ protected String lookupType(Object deploymentPlanItem) { if (deploymentPlanItem instanceof Service) { - if (!BrooklynComponentTemplateResolver.Factory.supportsType(mgmt, ((Service)deploymentPlanItem).getServiceType())) + String serviceType = ((Service)deploymentPlanItem).getServiceType(); + if (!BrooklynComponentTemplateResolver.Factory.supportsType(JavaBrooklynClassLoadingContext.newDefault(mgmt), serviceType)) return null; - return ((Service)deploymentPlanItem).getServiceType(); + return serviceType; } return null; } @@ -132,7 +134,7 @@ public boolean apply(Object deploymentPlanItem, AssemblyTemplateConstructor atc) * as a custom attribute with type List. * @throws java.lang.IllegalArgumentException if map[key] is not an instance of List */ - private void addCustomListAttributeIfNonNull(Builder builder, Map attrs, String key) { + private void addCustomListAttributeIfNonNull(Builder builder, Map attrs, String key) { Object items = attrs.remove(key); if (items != null) { if (items instanceof List) { @@ -151,7 +153,7 @@ private void addCustomListAttributeIfNonNull(Builder builder, Map attrs, String key) { + private void addCustomMapAttributeIfNonNull(Builder builder, Map attrs, String key) { Object items = attrs.remove(key); if (items != null) { if (items instanceof Map) { @@ -172,7 +174,7 @@ protected List extractValidConfigFlagsOrKeys(String if (attrs==null || attrs.isEmpty()) return null; try { - Class type = BrooklynComponentTemplateResolver.Factory.newInstance(mgmt, typeName).loadEntityClass(); + Class type = BrooklynComponentTemplateResolver.Factory.newInstance(JavaBrooklynClassLoadingContext.newDefault(mgmt), typeName).loadEntityClass(); ConfigBag bag = ConfigBag.newInstance(attrs); List values = FlagUtils.findAllFlagsAndConfigKeys(null, type, bag); diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeLoader.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java similarity index 69% rename from usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeLoader.java rename to usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java index 747e507a47..73869788b8 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeLoader.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java @@ -8,65 +8,65 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import brooklyn.entity.Entity; -import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; import brooklyn.util.collections.MutableMap; import brooklyn.util.config.ConfigBag; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.guava.Maybe; import brooklyn.util.javalang.Reflections; -import brooklyn.util.osgi.Osgis; import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -public abstract class BrooklynYamlTypeLoader { +/** Assists in loading types referenced from YAML; + * mainly as a way to share logic used in very different contexts. */ +public abstract class BrooklynYamlTypeInstantiator { - private static final Logger log = LoggerFactory.getLogger(BrooklynYamlTypeLoader.class); + private static final Logger log = LoggerFactory.getLogger(BrooklynYamlTypeInstantiator.class); protected final Factory factory; @Beta public static class Factory { - final ManagementContext mgmt; + final BrooklynClassLoadingContext loader; final Object contextForLogging; - public Factory(ManagementContext mgmt, Object contextForLogging) { - this.mgmt = mgmt; + public Factory(BrooklynClassLoadingContext loader, Object contextForLogging) { + this.loader = loader; this.contextForLogging = contextForLogging; } - public LoaderFromKey from(Map data) { - return new LoaderFromKey(this, ConfigBag.newInstance(data)); + public InstantiatorFromKey from(Map data) { + return new InstantiatorFromKey(this, ConfigBag.newInstance(data)); } - public LoaderFromKey from(ConfigBag data) { - return new LoaderFromKey(this, data); + public InstantiatorFromKey from(ConfigBag data) { + return new InstantiatorFromKey(this, data); } - public LoaderFromName type(String typeName) { - return new LoaderFromName(this, typeName); + public InstantiatorFromName type(String typeName) { + return new InstantiatorFromName(this, typeName); } } - public static class LoaderFromKey extends BrooklynYamlTypeLoader { + public static class InstantiatorFromKey extends BrooklynYamlTypeInstantiator { protected final ConfigBag data; protected String typeKeyPrefix = null; /** Nullable only permitted for instances which do not do loading, e.g. LoaderFromKey#lookup */ - protected LoaderFromKey(@Nullable Factory factory, ConfigBag data) { + protected InstantiatorFromKey(@Nullable Factory factory, ConfigBag data) { super(factory); this.data = data; } public static Maybe extractTypeName(String prefix, ConfigBag data) { if (data==null) return Maybe.absent(); - return new LoaderFromKey(null, data).prefix(prefix).getTypeName(); + return new InstantiatorFromKey(null, data).prefix(prefix).getTypeName(); } - public LoaderFromKey prefix(String prefix) { + public InstantiatorFromKey prefix(String prefix) { typeKeyPrefix = prefix; return this; } @@ -141,9 +141,9 @@ public Map getConfigMap() { } - public static class LoaderFromName extends BrooklynYamlTypeLoader { + public static class InstantiatorFromName extends BrooklynYamlTypeInstantiator { protected final String typeName; - protected LoaderFromName(Factory factory, String typeName) { + protected InstantiatorFromName(Factory factory, String typeName) { super(factory); this.typeName = typeName; } @@ -153,40 +153,30 @@ public Maybe getTypeName() { } } - protected BrooklynYamlTypeLoader(Factory factory) { + protected BrooklynYamlTypeInstantiator(Factory factory) { this.factory = factory; } public abstract Maybe getTypeName(); - public Class getType() { - return getType(null); + public BrooklynClassLoadingContext getClassLoadingContext() { + Preconditions.checkNotNull("No factory set; cannot use this instance for type loading"); + return factory.loader; } - public Class getType(@Nullable Class type) { - Preconditions.checkNotNull("No factory set; cannot use this instance for type loading"); - return loadClass(type, getTypeName().get(), factory.mgmt, factory.contextForLogging); + public Class getType() { + return getType(Object.class); } - - /** - * TODO in future will want OSGi-based resolver here (eg create from osgi:: prefix - * would use that OSGi mechanism here - */ - @SuppressWarnings("unchecked") - private static Class loadClass(@Nullable Class optionalSupertype, String typeName, ManagementContext mgmt, Object otherContext) { + + public Class getType(@Nonnull Class type) { try { - if (optionalSupertype != null && Entity.class.isAssignableFrom(optionalSupertype)) - return (Class) BrooklynEntityClassResolver.resolveEntity(typeName, mgmt); - else - return BrooklynEntityClassResolver.tryLoadFromClasspath(typeName, mgmt).get(); + return getClassLoadingContext().loadClass(getTypeName().get(), type); +// return loadClass(type, getTypeName().get(), factory.mgmt, factory.contextForLogging); } catch (Exception e) { Exceptions.propagateIfFatal(e); - log.warn("Unable to resolve " + typeName + " in spec " + otherContext); - throw Exceptions.propagate(new IllegalStateException("Unable to resolve " - + (optionalSupertype != null ? optionalSupertype.getSimpleName() + " " : "") - + "type '" + typeName + "'", e)); + log.warn("Unable to resolve " + type + " " + getTypeName().get() + " (rethrowing) in spec " + factory.contextForLogging); + throw Exceptions.propagate(e); } } - } diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/ChefComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/ChefComponentTemplateResolver.java index 0837ecb105..770d1cc529 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/ChefComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/ChefComponentTemplateResolver.java @@ -1,24 +1,31 @@ package io.brooklyn.camp.brooklyn.spi.creation; import io.brooklyn.camp.spi.AbstractResource; +import brooklyn.catalog.CatalogItem; import brooklyn.entity.Entity; import brooklyn.entity.chef.ChefConfig; import brooklyn.entity.chef.ChefEntity; import brooklyn.entity.proxying.EntitySpec; -import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; import brooklyn.util.config.ConfigBag; import brooklyn.util.text.Strings; public class ChefComponentTemplateResolver extends BrooklynComponentTemplateResolver { - public ChefComponentTemplateResolver(ManagementContext mgmt, ConfigBag attrs, AbstractResource optionalTemplate) { - super(mgmt, attrs, optionalTemplate); + public ChefComponentTemplateResolver(BrooklynClassLoadingContext loader, ConfigBag attrs, AbstractResource optionalTemplate) { + super(loader, attrs, optionalTemplate); } @Override - protected String getJavaType() { + protected String getCatalogIdOrJavaType() { return ChefEntity.class.getName(); } + + // chef: items are not in catalog + @Override + public CatalogItem> getCatalogItem() { + return null; + } @Override protected void decorateSpec(EntitySpec spec) { diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/BrooklynYamlTypeLoaderTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java similarity index 54% rename from usage/camp/src/test/java/io/brooklyn/camp/brooklyn/BrooklynYamlTypeLoaderTest.java rename to usage/camp/src/test/java/io/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java index b13720c600..74ea8b8130 100644 --- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/BrooklynYamlTypeLoaderTest.java +++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/BrooklynYamlTypeInstantiatorTest.java @@ -1,24 +1,30 @@ package io.brooklyn.camp.brooklyn; -import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeLoader; -import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeLoader.Factory; -import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeLoader.LoaderFromKey; +import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator; +import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.Factory; +import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.InstantiatorFromKey; import org.testng.Assert; import org.testng.annotations.Test; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.policy.Policy; import brooklyn.policy.ha.ServiceRestarter; import brooklyn.util.collections.MutableMap; import brooklyn.util.javalang.JavaClassNames; import brooklyn.util.time.Duration; -public class BrooklynYamlTypeLoaderTest extends AbstractYamlTest { +public class BrooklynYamlTypeInstantiatorTest extends AbstractYamlTest { + protected BrooklynClassLoadingContext loader() { + return JavaBrooklynClassLoadingContext.newDefault(mgmt()); + } + @Test public void testLoadPolicySpecProgrammatically() { - Factory loader = new BrooklynYamlTypeLoader.Factory(mgmt(), "test:"+JavaClassNames.niceClassAndMethod()); - LoaderFromKey decoL = loader.from(MutableMap.of("some_type", ServiceRestarter.class.getName())).prefix("some"); + Factory loader = new BrooklynYamlTypeInstantiator.Factory(loader(), "test:"+JavaClassNames.niceClassAndMethod()); + InstantiatorFromKey decoL = loader.from(MutableMap.of("some_type", ServiceRestarter.class.getName())).prefix("some"); Assert.assertTrue(decoL.getConfigMap().isEmpty()); Assert.assertEquals(decoL.getTypeName().get(), ServiceRestarter.class.getName()); @@ -33,8 +39,8 @@ public void testLoadPolicySpecProgrammatically() { @Test public void testLoadPolicySpecWithBrooklynConfig() { - Factory loader = new BrooklynYamlTypeLoader.Factory(mgmt(), "test:"+JavaClassNames.niceClassAndMethod()); - LoaderFromKey decoL = loader.from(MutableMap.of("some_type", ServiceRestarter.class.getName(), + Factory loader = new BrooklynYamlTypeInstantiator.Factory(loader(), "test:"+JavaClassNames.niceClassAndMethod()); + InstantiatorFromKey decoL = loader.from(MutableMap.of("some_type", ServiceRestarter.class.getName(), "brooklyn.config", MutableMap.of("failOnRecurringFailuresInThisDuration", Duration.seconds(42)))).prefix("some"); Policy sl2 = decoL.newInstance(Policy.class); Assert.assertEquals(sl2.getConfig(ServiceRestarter.FAIL_ON_RECURRING_FAILURES_IN_THIS_DURATION).toSeconds(), 42); @@ -42,8 +48,8 @@ public void testLoadPolicySpecWithBrooklynConfig() { @Test(groups = "WIP") public void testLoadPolicySpecWithFlag() { - Factory loader = new BrooklynYamlTypeLoader.Factory(mgmt(), "test:"+JavaClassNames.niceClassAndMethod()); - LoaderFromKey decoL = loader.from(MutableMap.of("some_type", ServiceRestarter.class.getName(), + Factory loader = new BrooklynYamlTypeInstantiator.Factory(loader(), "test:"+JavaClassNames.niceClassAndMethod()); + InstantiatorFromKey decoL = loader.from(MutableMap.of("some_type", ServiceRestarter.class.getName(), "failOnRecurringFailuresInThisDuration", Duration.seconds(42))).prefix("some"); Policy sl2 = decoL.newInstance(Policy.class); Assert.assertEquals(sl2.getConfig(ServiceRestarter.FAIL_ON_RECURRING_FAILURES_IN_THIS_DURATION).toSeconds(), 42); From 40ed35fd1f6834120f339023ace1508e8de43733 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Sat, 5 Jul 2014 05:09:02 +0100 Subject: [PATCH 15/48] fix NPE --- .../java/brooklyn/catalog/internal/BasicBrooklynCatalog.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index 94c2ca5815..53333d8cd8 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -86,6 +86,7 @@ public CatalogItem getCatalogItem(String id) { public CatalogItem getCatalogItem(Class type, String id) { if (id==null) return null; CatalogItem result = getCatalogItem(id); + if (result==null) return null; if (type==null || type.isAssignableFrom(result.getCatalogItemJavaType())) return (CatalogItem)result; return null; From 51e5e01e8c00bebfa7868126acc3aaef13be3210 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Mon, 7 Jul 2014 12:43:22 +0300 Subject: [PATCH 16/48] Test adding an YAML OSGi backed item to the catalog and launching it using YAML. --- .../resources/CatalogBundleResourceTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java new file mode 100644 index 0000000000..fda34b0fcb --- /dev/null +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -0,0 +1,57 @@ +package brooklyn.rest.resources; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.net.URI; + +import javax.ws.rs.core.Response; + +import org.testng.annotations.Test; + +import brooklyn.entity.basic.BasicApplication; +import brooklyn.management.osgi.OsgiStandaloneTest; +import brooklyn.rest.domain.ApplicationSummary; +import brooklyn.rest.testing.BrooklynRestResourceTest; + +import com.sun.jersey.api.client.ClientResponse; + +public class CatalogBundleResourceTest extends BrooklynRestResourceTest { + + @Test + public void testDeployApplicationYaml() throws Exception { + String registeredTypeName = "my.catalog.app.id"; + String catalogYaml = + "name: "+registeredTypeName+"\n"+ + // FIXME name above should be unnecessary when brooklyn.catalog below is working + "brooklyn.catalog:\n"+ + " id: " + registeredTypeName + "\n"+ + " name: My Catalog App\n"+ + " description: My description\n"+ + " icon_url: classpath://path/to/myicon.jpg\n"+ + " version: 0.1.2\n"+ + " libraries:\n"+ + " - url: classpath:/" + OsgiStandaloneTest.BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL + "\n"+ + "\n"+ + "services:\n"+ + "- type: brooklyn.osgi.tests.SimpleEntity\n"; + + ClientResponse catalogResponse = client().resource("/v1/catalog") + .post(ClientResponse.class, catalogYaml); + + assertEquals(catalogResponse.getStatus(), Response.Status.CREATED.getStatusCode()); + + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; + + ClientResponse response = client().resource("/v1/applications") + .entity(yaml, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response.getStatus()/100 == 2, "response is "+response); + + // Expect app to be running + URI appUri = response.getLocation(); + waitForApplicationToBeRunning(response.getLocation()); + assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); + } + +} From 403c34886751ead2b70fc1b85f6363ae7a13b8d6 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Mon, 7 Jul 2014 12:59:56 +0300 Subject: [PATCH 17/48] Add test - get entity definition from OSGi based YAML. --- .../resources/CatalogBundleResourceTest.java | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index fda34b0fcb..f4884e85b7 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -9,9 +9,9 @@ import org.testng.annotations.Test; -import brooklyn.entity.basic.BasicApplication; import brooklyn.management.osgi.OsgiStandaloneTest; import brooklyn.rest.domain.ApplicationSummary; +import brooklyn.rest.domain.CatalogEntitySummary; import brooklyn.rest.testing.BrooklynRestResourceTest; import com.sun.jersey.api.client.ClientResponse; @@ -19,8 +19,36 @@ public class CatalogBundleResourceTest extends BrooklynRestResourceTest { @Test - public void testDeployApplicationYaml() throws Exception { - String registeredTypeName = "my.catalog.app.id"; + public void testListApplicationYaml() throws Exception { + String registeredTypeName = "my.catalog.app.id.load"; + addCatalogOSGiEntity(registeredTypeName); + + CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+registeredTypeName) + .get(CatalogEntitySummary.class); + + assertEquals(entityItem.getRegisteredType(), registeredTypeName); + + } + + @Test + public void testLaunchApplicationYaml() throws Exception { + String registeredTypeName = "my.catalog.app.id.launch"; + addCatalogOSGiEntity(registeredTypeName); + + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; + + ClientResponse response = client().resource("/v1/applications") + .entity(yaml, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response.getStatus()/100 == 2, "response is "+response); + + // Expect app to be running + URI appUri = response.getLocation(); + waitForApplicationToBeRunning(response.getLocation()); + assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); + } + + private void addCatalogOSGiEntity(String registeredTypeName) { String catalogYaml = "name: "+registeredTypeName+"\n"+ // FIXME name above should be unnecessary when brooklyn.catalog below is working @@ -40,18 +68,6 @@ public void testDeployApplicationYaml() throws Exception { .post(ClientResponse.class, catalogYaml); assertEquals(catalogResponse.getStatus(), Response.Status.CREATED.getStatusCode()); - - String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; - - ClientResponse response = client().resource("/v1/applications") - .entity(yaml, "application/x-yaml") - .post(ClientResponse.class); - assertTrue(response.getStatus()/100 == 2, "response is "+response); - - // Expect app to be running - URI appUri = response.getLocation(); - waitForApplicationToBeRunning(response.getLocation()); - assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); - } + } } From 53d176333a1b1b713ed5e336bed03164661e743f Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Mon, 7 Jul 2014 11:04:37 +0100 Subject: [PATCH 18/48] temporary fix for yaml/java-type resolution, and notes on the better solution --- .../BrooklynComponentTemplateResolver.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index c1867375d2..7f7f503072 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -1,11 +1,14 @@ package io.brooklyn.camp.brooklyn.spi.creation; +import io.brooklyn.camp.CampPlatform; import io.brooklyn.camp.brooklyn.BrooklynCampConstants; import io.brooklyn.camp.spi.AbstractResource; import io.brooklyn.camp.spi.ApplicationComponentTemplate; +import io.brooklyn.camp.spi.Assembly; import io.brooklyn.camp.spi.AssemblyTemplate; import io.brooklyn.camp.spi.PlatformComponentTemplate; +import java.io.StringReader; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -14,7 +17,9 @@ import javax.annotation.Nullable; +import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator; import brooklyn.catalog.CatalogItem; +import brooklyn.config.BrooklynServerConfig; import brooklyn.config.ConfigKey; import brooklyn.entity.Application; import brooklyn.entity.Entity; @@ -170,9 +175,26 @@ public Class loadEntityClass() { CatalogItem> item = getCatalogItem(); String typeName = getCatalogIdOrJavaType(); if (item!=null) { + // add additional bundles loader = new BrooklynClassLoadingContextSequential(mgmt, item.newClassLoadingContext(mgmt), loader); + +// if (item.getPlanYaml()!=null) { +// // TODO if yaml refers to *another* catalog item, or remote yaml reference, or even have config +// // then we will need to parse that YAML +// // (but NB as it stands this code might cause infinite looping?) +// CampPlatform platform = BrooklynServerConfig.getCampPlatform(mgmt).get(); +// AssemblyTemplate template2 = platform.pdp().registerDeploymentPlan( new StringReader(item.getPlanYaml()) ); +// return ((AssemblyTemplateSpecInstantiator) template2.getInstantiator().newInstance()).createSpec(template2, platform); +// } + typeName = item.getJavaType(); + if (typeName==null) { + // FIXME temporary fix, until we parse the YAML above + // i think even config items in yaml on the catalog item will be ignored if we don't do the above + typeName = item.getRegisteredTypeName(); + } } + return loader.loadClass(typeName, Entity.class); } From 00b5237dccd1475447cf6d860d2a507e3d320a28 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Mon, 7 Jul 2014 11:47:31 +0100 Subject: [PATCH 19/48] make constant string public for use in other tests --- .../test/java/brooklyn/management/osgi/OsgiStandaloneTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java index 2d4423259f..072a4d2f6f 100644 --- a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java +++ b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java @@ -40,7 +40,7 @@ public class OsgiStandaloneTest { private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class); public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:///brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar"; - private static final String BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL = "/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar"; + public static final String BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL = "/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar"; protected Framework framework = null; private File storageTempDir; From 03ec9796cf2b6dcd835726f8e88d95879962fa59 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 7 Jul 2014 16:49:22 +0100 Subject: [PATCH 20/48] Fixing catalog listing/instantating from REST via yaml --- .../internal/BasicBrooklynCatalog.java | 38 ++++++++++- .../proxying/InternalEntityFactory.java | 13 +++- .../brooklyn-tests-osgi-entities-0.1.0.jar | Bin 4810 -> 10752 bytes .../BrooklynAssemblyTemplateInstantiator.java | 63 +++++++++++++++--- .../BrooklynComponentTemplateResolver.java | 14 ++-- .../spi/creation/BrooklynEntityMatcher.java | 13 +++- .../resources/CatalogBundleResourceTest.java | 62 +++++++++++++++-- 7 files changed, 180 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index 53333d8cd8..9a89a2e93a 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -1,6 +1,5 @@ package brooklyn.catalog.internal; -import brooklyn.util.guava.Maybe; import io.brooklyn.camp.CampPlatform; import io.brooklyn.camp.spi.AssemblyTemplate; import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator; @@ -22,8 +21,10 @@ import brooklyn.catalog.CatalogPredicates; import brooklyn.config.BrooklynServerConfig; import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.guava.Maybe; import brooklyn.util.javalang.AggregateClassLoader; import brooklyn.util.javalang.LoadedClassLoader; import brooklyn.util.javalang.Reflections; @@ -42,7 +43,24 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { private static final Logger log = LoggerFactory.getLogger(BasicBrooklynCatalog.class); - + + public static class BrooklynLoaderTracker { + public static final ThreadLocal loader = new ThreadLocal(); + + public static void setLoader(BrooklynClassLoadingContext val) { + loader.set(val); + } + + // TODO Stack, for recursive calls? + public static void unsetLoader(BrooklynClassLoadingContext val) { + loader.set(null); + } + + public static BrooklynClassLoadingContext getLoader() { + return loader.get(); + } + } + private final ManagementContext mgmt; private final CatalogDo catalog; private volatile CatalogDo manualAdditionsCatalog; @@ -123,7 +141,14 @@ public SpecT createSpec(CatalogItem item) { CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get(); // TODO should not register new AT each time we instantiate from the same plan; use some kind of cache - AssemblyTemplate at = camp.pdp().registerDeploymentPlan(plan); + AssemblyTemplate at; + BrooklynClassLoadingContext loader = loadedItem.newClassLoadingContext(mgmt); + BrooklynLoaderTracker.setLoader(loader); + try { + at = camp.pdp().registerDeploymentPlan(plan); + } finally { + BrooklynLoaderTracker.unsetLoader(loader); + } try { AssemblyTemplateInstantiator instantiator = at.getInstantiator().newInstance(); @@ -258,6 +283,13 @@ public CatalogItem addItem(String yaml) { if (manualAdditionsCatalog==null) loadManualAdditionsCatalog(); CatalogItemDtoAbstract itemDto = getAbstractCatalogItem(yaml); manualAdditionsCatalog.addEntry(itemDto); + + // Load the libraries now. + // Otherwise, when CAMP looks up BrooklynEntityMatcher.accepts then it + // won't know about this bundle:class (via the catalog item's + // BrooklynClassLoadingContext) so will reject it as not-for-brooklyn. + new CatalogLibrariesDo(itemDto.getLibrariesDto()).load(mgmt); + return itemDto; } diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java index 78f5a23fe2..55535e9004 100644 --- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java +++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java @@ -27,6 +27,7 @@ import brooklyn.util.collections.MutableSet; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.flags.FlagUtils; +import brooklyn.util.javalang.AggregateClassLoader; import brooklyn.util.javalang.Reflections; import brooklyn.util.task.Tasks; @@ -121,8 +122,18 @@ public T createEntityProxy(EntitySpec spec, T entity) { builder.addAll(spec.getAdditionalInterfaces()); Set> interfaces = builder.build(); + // TODO OSGi strangeness! The classloader obtained from the type should be enough. + // If an OSGi class loader, it should delegate to find things like Entity.class etc. + // However, we get errors such as: + // NoClassDefFoundError: brooklyn.event.AttributeSensor not found by io.brooklyn.brooklyn-test-osgi-entities + // Building our own aggregating class loader gets around this. + // But we really should not have to do this! What are the consequences? + AggregateClassLoader aggregateClassLoader = AggregateClassLoader.newInstanceWithNoLoaders(); + aggregateClassLoader.addFirst(classloader); + aggregateClassLoader.addLast(Entity.class.getClassLoader()); + return (T) java.lang.reflect.Proxy.newProxyInstance( - classloader, + aggregateClassLoader, interfaces.toArray(new Class[interfaces.size()]), new EntityProxyImpl(entity)); } diff --git a/core/src/test/resources/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar b/core/src/test/resources/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar index 82d4f2e3dbcc21c85166aa1b9cc946d6524d8851..fa1396d723fcfb3dfb7f3356ff77adba3c43daa5 100644 GIT binary patch literal 10752 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 literal 4810 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 MQ;r`{ item = catalog.getCatalogItem(template.getId()); + CatalogItem item = catalog.getCatalogItem(template.getName()); + BrooklynClassLoadingContext loader; if (item!=null) { + loader = item.newClassLoadingContext(mgmt); // TODO legacy path - currently item is always null because template.id is a random String, // and pretty sure that is the desired behaviour; we now (Jul 2014) automatically promote // so fine for users always to put the catalog registeredType in the services block; // if we did want to support users supplying an `id' that would be available not via the above // but via template.getCustomAttributes().get("id"); - return createApplicationFromCatalog(platform, item, template, requireSpec); + // FIXME No longer called; delete: + // return createApplicationFromCatalog(platform, item, template, requireSpec); } else { - return new AppOrSpec(createApplicationFromNonCatalogCampTemplate(template, platform)); + loader = JavaBrooklynClassLoadingContext.newDefault(mgmt); } + return new AppOrSpec(createApplicationFromNonCatalogCampTemplate(template, platform, loader)); } private static class AppOrSpec { @@ -264,16 +272,16 @@ private EntitySpec toCoreEntitySpec(Class createApplicationFromNonCatalogCampTemplate(AssemblyTemplate template, CampPlatform platform) { + protected EntitySpec createApplicationFromNonCatalogCampTemplate(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader) { // AssemblyTemplates created via PDP, _specifying_ then entities to put in final ManagementContext mgmt = getBrooklynManagementContext(platform); BrooklynComponentTemplateResolver resolver = BrooklynComponentTemplateResolver.Factory.newInstance( - JavaBrooklynClassLoadingContext.newDefault(mgmt), template); + loader, template); EntitySpec app = resolver.resolveSpec(StartableApplication.class, BasicApplicationImpl.class); // first build the children into an empty shell app - buildTemplateServicesAsSpecs(JavaBrooklynClassLoadingContext.newDefault(mgmt), template, app); + buildTemplateServicesAsSpecs(loader, template, platform, app); if (shouldUnwrap(template, app)) { EntitySpec oldApp = app; @@ -320,12 +328,51 @@ protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec root) { + private void buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform, EntitySpec root) { for (ResolvableLink ctl: template.getPlatformComponentTemplates().links()) { PlatformComponentTemplate appChildComponentTemplate = ctl.resolve(); BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(loader, appChildComponentTemplate); - EntitySpec spec = entityResolver.resolveSpec(); + EntitySpec spec; + + ManagementContext mgmt = loader.getManagementContext(); + + CatalogItem> item = entityResolver.getCatalogItem(); + if (item != null) { + String yaml = item.getPlanYaml(); + Reader input = new StringReader(yaml); + + AssemblyTemplate at; + BrooklynClassLoadingContext itemLoader = item.newClassLoadingContext(mgmt); + BrooklynLoaderTracker.setLoader(itemLoader); + try { + at = platform.pdp().registerDeploymentPlan(input); + } finally { + BrooklynLoaderTracker.unsetLoader(itemLoader); + } + + + // FIXME Entitlement? +// if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, at)) { +// throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s", +// Entitlements.getEntitlementContext().user(), yaml); +// } + + try { + AssemblyTemplateInstantiator ati = at.getInstantiator().newInstance(); + if (ati instanceof BrooklynAssemblyTemplateInstantiator) { + spec = EntitySpec.create(BasicApplication.class); + ((BrooklynAssemblyTemplateInstantiator)ati).buildTemplateServicesAsSpecs(itemLoader, at, platform, (EntitySpec)spec); + } else { + throw new IllegalStateException("Cannot create application with instantiator: " + ati); + } + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } else { + spec = entityResolver.resolveSpec(); + } + root.child(spec); BrooklynClassLoadingContext newLoader = entityResolver.loader; diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index 7f7f503072..e30d49e9e8 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -1,14 +1,11 @@ package io.brooklyn.camp.brooklyn.spi.creation; -import io.brooklyn.camp.CampPlatform; import io.brooklyn.camp.brooklyn.BrooklynCampConstants; import io.brooklyn.camp.spi.AbstractResource; import io.brooklyn.camp.spi.ApplicationComponentTemplate; -import io.brooklyn.camp.spi.Assembly; import io.brooklyn.camp.spi.AssemblyTemplate; import io.brooklyn.camp.spi.PlatformComponentTemplate; -import java.io.StringReader; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -17,9 +14,7 @@ import javax.annotation.Nullable; -import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator; import brooklyn.catalog.CatalogItem; -import brooklyn.config.BrooklynServerConfig; import brooklyn.config.ConfigKey; import brooklyn.entity.Application; import brooklyn.entity.Entity; @@ -142,6 +137,7 @@ protected String getDeclaredType() { protected String getCatalogIdOrJavaType() { String type = getDeclaredType(); type = Strings.removeFromStart(type, "brooklyn:", "java:"); + if (type == null) return null; // TODO currently a hardcoded list of aliases; would like that to come from mgmt somehow if (type.equals("cluster") || type.equals("Cluster")) return DynamicCluster.class.getName(); @@ -159,7 +155,12 @@ protected String getCatalogIdOrJavaType() { */ @Nullable public CatalogItem> getCatalogItem() { - return loader.getManagementContext().getCatalog().getCatalogItem(Entity.class, getCatalogIdOrJavaType()); + String type = getCatalogIdOrJavaType(); + if (type != null) { + return loader.getManagementContext().getCatalog().getCatalogItem(Entity.class, type); + } else { + return null; + } } public boolean canResolve() { @@ -201,6 +202,7 @@ public Class loadEntityClass() { /** resolves the spec, updating the loader if a catalog item is loaded */ @SuppressWarnings("unchecked") public EntitySpec resolveSpec() { + CatalogItem> item = getCatalogItem(); return (EntitySpec)resolveSpec(loadEntityClass(), null); } diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java index 04ceb66a6c..484ff93e74 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java @@ -1,5 +1,6 @@ package io.brooklyn.camp.brooklyn.spi.creation; +import io.brooklyn.camp.brooklyn.BrooklynCampPlatform; import io.brooklyn.camp.spi.PlatformComponentTemplate; import io.brooklyn.camp.spi.PlatformComponentTemplate.Builder; import io.brooklyn.camp.spi.pdp.AssemblyTemplateConstructor; @@ -12,8 +13,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import brooklyn.catalog.internal.BasicBrooklynCatalog; import brooklyn.entity.Entity; import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.util.collections.MutableMap; import brooklyn.util.config.ConfigBag; @@ -44,9 +47,17 @@ public boolean accepts(Object deploymentPlanItem) { * typically whether a Service can be matched to a Brooklyn entity, * or null if not supported */ protected String lookupType(Object deploymentPlanItem) { + if (deploymentPlanItem instanceof Service) { + String name = ((Service)deploymentPlanItem).getName(); + if (mgmt.getCatalog().getCatalogItem(name) != null) { + return name; + } + } if (deploymentPlanItem instanceof Service) { String serviceType = ((Service)deploymentPlanItem).getServiceType(); - if (!BrooklynComponentTemplateResolver.Factory.supportsType(JavaBrooklynClassLoadingContext.newDefault(mgmt), serviceType)) + BrooklynClassLoadingContext loader = BasicBrooklynCatalog.BrooklynLoaderTracker.getLoader(); + if (loader == null) loader = JavaBrooklynClassLoadingContext.newDefault(mgmt); + if (!BrooklynComponentTemplateResolver.Factory.supportsType(loader, serviceType)) return null; return serviceType; } diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index f4884e85b7..c3ccacaa8f 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -9,11 +9,14 @@ import org.testng.annotations.Test; +import brooklyn.entity.Application; +import brooklyn.entity.Entity; import brooklyn.management.osgi.OsgiStandaloneTest; import brooklyn.rest.domain.ApplicationSummary; import brooklyn.rest.domain.CatalogEntitySummary; import brooklyn.rest.testing.BrooklynRestResourceTest; +import com.google.common.collect.Iterables; import com.sun.jersey.api.client.ClientResponse; public class CatalogBundleResourceTest extends BrooklynRestResourceTest { @@ -22,7 +25,6 @@ public class CatalogBundleResourceTest extends BrooklynRestResourceTest { public void testListApplicationYaml() throws Exception { String registeredTypeName = "my.catalog.app.id.load"; addCatalogOSGiEntity(registeredTypeName); - CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+registeredTypeName) .get(CatalogEntitySummary.class); @@ -45,8 +47,40 @@ public void testLaunchApplicationYaml() throws Exception { // Expect app to be running URI appUri = response.getLocation(); waitForApplicationToBeRunning(response.getLocation()); - assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); - } + ApplicationSummary appSummary = client().resource(appUri).get(ApplicationSummary.class); + String appId = appSummary.getId(); + assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); + + Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); + Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); + assertEquals(simpleEntity.getEntityType().getName(), "brooklyn.osgi.tests.SimpleEntity"); + } + +// @Test +// public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws Exception { +// String referencedRegisteredTypeName = "my.catalog.app.id.referenced"; +// String referrerRegisteredTypeName = "my.catalog.app.id.referring"; +// addCatalogOSGiEntity(referencedRegisteredTypeName); +// addCatalogEntityReferencingCatalogEntry(referrerRegisteredTypeName, referencedRegisteredTypeName); +// +// String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+referrerRegisteredTypeName+" } ] }"; +// +// ClientResponse response = client().resource("/v1/applications") +// .entity(yaml, "application/x-yaml") +// .post(ClientResponse.class); +// assertTrue(response.getStatus()/100 == 2, "response is "+response); +// +// // Expect app to be running +// URI appUri = response.getLocation(); +// waitForApplicationToBeRunning(response.getLocation()); +// ApplicationSummary appSummary = client().resource(appUri).get(ApplicationSummary.class); +// String appId = appSummary.getId(); +// assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); +// +// Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); +// Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); +// assertEquals(simpleEntity.getEntityType().getName(), "brooklyn.osgi.tests.SimpleEntity"); +// } private void addCatalogOSGiEntity(String registeredTypeName) { String catalogYaml = @@ -64,10 +98,30 @@ private void addCatalogOSGiEntity(String registeredTypeName) { "services:\n"+ "- type: brooklyn.osgi.tests.SimpleEntity\n"; + addCatalogEntity(registeredTypeName, catalogYaml); + } + + private void addCatalogEntityReferencingCatalogEntry(String ownRegisteredTypeName, String otherRegisteredTypeName) { + String catalogYaml = + "name: "+ownRegisteredTypeName+"\n"+ + // FIXME name above should be unnecessary when brooklyn.catalog below is working + "brooklyn.catalog:\n"+ + " id: " + ownRegisteredTypeName + "\n"+ + " name: My Referrer Catalog App\n"+ + " description: My referrer description\n"+ + " icon_url: classpath://path/to/myicon.jpg\n"+ + " version: 0.2.1\n"+ + "\n"+ + "services:\n"+ + "- type: "+otherRegisteredTypeName+"\n"; + + addCatalogEntity(ownRegisteredTypeName, catalogYaml); + } + + private void addCatalogEntity(String registeredTypeName, String catalogYaml) { ClientResponse catalogResponse = client().resource("/v1/catalog") .post(ClientResponse.class, catalogYaml); assertEquals(catalogResponse.getStatus(), Response.Status.CREATED.getStatusCode()); } - } From db0724ae4fc8ebfa2d8cfcf82be22c031f51a8ed Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 7 Jul 2014 19:31:53 +0100 Subject: [PATCH 21/48] On catalog.addItem, ensure cache is updated --- .../java/brooklyn/catalog/internal/BasicBrooklynCatalog.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index 9a89a2e93a..b77ee6a414 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -289,7 +289,10 @@ public CatalogItem addItem(String yaml) { // won't know about this bundle:class (via the catalog item's // BrooklynClassLoadingContext) so will reject it as not-for-brooklyn. new CatalogLibrariesDo(itemDto.getLibrariesDto()).load(mgmt); - + + // Ensure the cache is populated + getCatalog().addEntry(itemDto); + return itemDto; } From 6e253cf7aeb225db898cc7f39488414116ae02fb Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Mon, 7 Jul 2014 19:32:18 +0100 Subject: [PATCH 22/48] Handle catalog item referencing other catalog item --- .../BrooklynAssemblyTemplateInstantiator.java | 37 ++++++++------ .../resources/CatalogBundleResourceTest.java | 50 +++++++++---------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index 326597ff71..b2fa56e3fb 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -48,6 +48,7 @@ import brooklyn.util.text.Strings; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpecInstantiator { @@ -281,7 +282,10 @@ protected EntitySpec createApplicationFromNonCatalogCampT EntitySpec app = resolver.resolveSpec(StartableApplication.class, BasicApplicationImpl.class); // first build the children into an empty shell app - buildTemplateServicesAsSpecs(loader, template, platform, app); + List> childSpecs = buildTemplateServicesAsSpecs(loader, template, platform); + for (EntitySpec childSpec : childSpecs) { + app.child(childSpec); + } if (shouldUnwrap(template, app)) { EntitySpec oldApp = app; @@ -328,15 +332,16 @@ protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec root) { + private List> buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform) { + List> result = Lists.newArrayList(); + for (ResolvableLink ctl: template.getPlatformComponentTemplates().links()) { PlatformComponentTemplate appChildComponentTemplate = ctl.resolve(); BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(loader, appChildComponentTemplate); - - EntitySpec spec; - ManagementContext mgmt = loader.getManagementContext(); + EntitySpec spec; + CatalogItem> item = entityResolver.getCatalogItem(); if (item != null) { String yaml = item.getPlanYaml(); @@ -351,18 +356,17 @@ private void buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, As BrooklynLoaderTracker.unsetLoader(itemLoader); } - - // FIXME Entitlement? -// if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, at)) { -// throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s", -// Entitlements.getEntitlementContext().user(), yaml); -// } - + // TODO Should we check entitlements, or sufficient to do once for this being called in the first place? + // TODO Is it acceptable to only allow a single top-level entity in a catalog? If not, we need to think + // about what it would mean to subsequently call buildChildrenEntitySpecs on the list of top-level entities! try { AssemblyTemplateInstantiator ati = at.getInstantiator().newInstance(); if (ati instanceof BrooklynAssemblyTemplateInstantiator) { - spec = EntitySpec.create(BasicApplication.class); - ((BrooklynAssemblyTemplateInstantiator)ati).buildTemplateServicesAsSpecs(itemLoader, at, platform, (EntitySpec)spec); + List> specs = ((BrooklynAssemblyTemplateInstantiator)ati).buildTemplateServicesAsSpecs(itemLoader, at, platform); + if (specs.size() > 1) { + throw new UnsupportedOperationException("Only supporting single service in catalog item currently"); + } + spec = specs.get(0); } else { throw new IllegalStateException("Cannot create application with instantiator: " + ati); } @@ -373,11 +377,12 @@ private void buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, As spec = entityResolver.resolveSpec(); } - root.child(spec); - BrooklynClassLoadingContext newLoader = entityResolver.loader; buildChildrenEntitySpecs(newLoader, spec, entityResolver.getChildren(appChildComponentTemplate.getCustomAttributes())); + + result.add(spec); } + return result; } protected void buildChildrenEntitySpecs(BrooklynClassLoadingContext loader, EntitySpec parent, List> childConfig) { diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index c3ccacaa8f..2161464813 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -56,31 +56,31 @@ public void testLaunchApplicationYaml() throws Exception { assertEquals(simpleEntity.getEntityType().getName(), "brooklyn.osgi.tests.SimpleEntity"); } -// @Test -// public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws Exception { -// String referencedRegisteredTypeName = "my.catalog.app.id.referenced"; -// String referrerRegisteredTypeName = "my.catalog.app.id.referring"; -// addCatalogOSGiEntity(referencedRegisteredTypeName); -// addCatalogEntityReferencingCatalogEntry(referrerRegisteredTypeName, referencedRegisteredTypeName); -// -// String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+referrerRegisteredTypeName+" } ] }"; -// -// ClientResponse response = client().resource("/v1/applications") -// .entity(yaml, "application/x-yaml") -// .post(ClientResponse.class); -// assertTrue(response.getStatus()/100 == 2, "response is "+response); -// -// // Expect app to be running -// URI appUri = response.getLocation(); -// waitForApplicationToBeRunning(response.getLocation()); -// ApplicationSummary appSummary = client().resource(appUri).get(ApplicationSummary.class); -// String appId = appSummary.getId(); -// assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); -// -// Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); -// Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); -// assertEquals(simpleEntity.getEntityType().getName(), "brooklyn.osgi.tests.SimpleEntity"); -// } + @Test + public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws Exception { + String referencedRegisteredTypeName = "my.catalog.app.id.referenced"; + String referrerRegisteredTypeName = "my.catalog.app.id.referring"; + addCatalogOSGiEntity(referencedRegisteredTypeName); + addCatalogEntityReferencingCatalogEntry(referrerRegisteredTypeName, referencedRegisteredTypeName); + + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+referrerRegisteredTypeName+" } ] }"; + + ClientResponse response = client().resource("/v1/applications") + .entity(yaml, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response.getStatus()/100 == 2, "response is "+response); + + // Expect app to be running + URI appUri = response.getLocation(); + waitForApplicationToBeRunning(response.getLocation()); + ApplicationSummary appSummary = client().resource(appUri).get(ApplicationSummary.class); + String appId = appSummary.getId(); + assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); + + Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); + Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); + assertEquals(simpleEntity.getEntityType().getName(), "brooklyn.osgi.tests.SimpleEntity"); + } private void addCatalogOSGiEntity(String registeredTypeName) { String catalogYaml = From 01ded28d18f0c001a31e6a2a25a54769a76e50d4 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 8 Jul 2014 05:19:34 +0100 Subject: [PATCH 23/48] remove old brooklyn-tests jar --- .../osgi/brooklyn-tests-osgi-entities-0.1.0.jar | Bin 10752 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/src/test/resources/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar diff --git a/core/src/test/resources/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar b/core/src/test/resources/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar deleted file mode 100644 index fa1396d723fcfb3dfb7f3356ff77adba3c43daa5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10752 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 From 9407ed71fab5903a49dae82d86d2e83e652c7db6 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Mon, 7 Jul 2014 19:37:32 +0300 Subject: [PATCH 24/48] Add project for generating a test OSGi bundle. The project is included as a resource, not part of the normal build cycle. --- .../test/dependencies/osgi/entities/pom.xml | 62 ++++++++++++++++++ .../osgi/tests/SimpleApplication.java | 10 +++ .../osgi/tests/SimpleApplicationImpl.java | 9 +++ .../brooklyn/osgi/tests/SimpleEntity.java | 10 +++ .../brooklyn/osgi/tests/SimpleEntityImpl.java | 8 +++ .../management/osgi/OsgiStandaloneTest.java | 6 +- .../osgi/brooklyn-test-osgi-entities.jar | Bin 0 -> 10752 bytes .../resources/CatalogBundleResourceTest.java | 2 +- 8 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 core/src/test/dependencies/osgi/entities/pom.xml create mode 100644 core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplication.java create mode 100644 core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplicationImpl.java create mode 100644 core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntity.java create mode 100644 core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntityImpl.java create mode 100644 core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar diff --git a/core/src/test/dependencies/osgi/entities/pom.xml b/core/src/test/dependencies/osgi/entities/pom.xml new file mode 100644 index 0000000000..8fa76f09fb --- /dev/null +++ b/core/src/test/dependencies/osgi/entities/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + jar + + brooklyn-test-osgi-entities + + OSGi bundled test entities + + + Simple entities for testing the OSGi functionality + + + + io.brooklyn + brooklyn-parent + 0.7.0-SNAPSHOT + ../../../../../../pom.xml + + + + io.brooklyn + brooklyn-core + ${brooklyn.version} + + + io.brooklyn + brooklyn-api + ${brooklyn.version} + + + io.brooklyn + brooklyn-utils-common + ${brooklyn.version} + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.3.2 + + ../../../resources/brooklyn/osgi + brooklyn-test-osgi-entities + + + + org.apache.felix + maven-bundle-plugin + 1.4.0 + true + + + 0.1.0 + + + + + + diff --git a/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplication.java b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplication.java new file mode 100644 index 0000000000..a590a5982c --- /dev/null +++ b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplication.java @@ -0,0 +1,10 @@ +package brooklyn.osgi.tests; + + +import brooklyn.entity.basic.StartableApplication; +import brooklyn.entity.proxying.ImplementedBy; + +@ImplementedBy(SimpleApplicationImpl.class) +public interface SimpleApplication extends StartableApplication { + +} diff --git a/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplicationImpl.java b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplicationImpl.java new file mode 100644 index 0000000000..dcc2ce4ab2 --- /dev/null +++ b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleApplicationImpl.java @@ -0,0 +1,9 @@ +package brooklyn.osgi.tests; + + +import brooklyn.entity.basic.AbstractApplication; +import brooklyn.entity.basic.StartableApplication; + +public class SimpleApplicationImpl extends AbstractApplication implements StartableApplication { + +} diff --git a/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntity.java b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntity.java new file mode 100644 index 0000000000..6761ef3fae --- /dev/null +++ b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntity.java @@ -0,0 +1,10 @@ +package brooklyn.osgi.tests; + + +import brooklyn.entity.Entity; +import brooklyn.entity.proxying.ImplementedBy; + +@ImplementedBy(SimpleEntityImpl.class) +public interface SimpleEntity extends Entity { + +} diff --git a/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntityImpl.java b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntityImpl.java new file mode 100644 index 0000000000..04b22516d4 --- /dev/null +++ b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimpleEntityImpl.java @@ -0,0 +1,8 @@ +package brooklyn.osgi.tests; + +import brooklyn.entity.basic.AbstractEntity; + + +public class SimpleEntityImpl extends AbstractEntity implements SimpleEntity { + +} diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java index 072a4d2f6f..151b2bbeca 100644 --- a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java +++ b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java @@ -40,7 +40,7 @@ public class OsgiStandaloneTest { private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class); public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:///brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar"; - public static final String BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL = "/brooklyn/osgi/brooklyn-tests-osgi-entities-0.1.0.jar"; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "/brooklyn/osgi/brooklyn-test-osgi-entities.jar"; protected Framework framework = null; private File storageTempDir; @@ -149,7 +149,7 @@ public void testReadAManifest() throws Exception { @Test public void testReadKnownManifest() throws Exception { - InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL); + InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TEST_OSGI_ENTITIES_URL); JarInputStream jarIn = new JarInputStream(in); ManifestHelper helper = Osgis.ManifestHelper.forManifest(jarIn.getManifest()); jarIn.close(); @@ -159,7 +159,7 @@ public void testReadKnownManifest() throws Exception { @Test public void testLoadOsgiBundleDependencies() throws Exception { - Bundle bundle = install("classpath:/" + BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL); + Bundle bundle = install("classpath:/" + BROOKLYN_TEST_OSGI_ENTITIES_URL); Assert.assertNotNull(bundle); Class aClass = bundle.loadClass("brooklyn.osgi.tests.SimpleApplicationImpl"); Object aInst = aClass.newInstance(); diff --git a/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar b/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar new file mode 100644 index 0000000000000000000000000000000000000000..b2bc7ebc109d31dc48c0ee747f347cbfb2b13c82 GIT binary patch literal 10752 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 V#w`GV_V|bUxGE+dbtVA7{{s*qas>bY literal 0 HcmV?d00001 diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index 2161464813..3066ad22fa 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -93,7 +93,7 @@ private void addCatalogOSGiEntity(String registeredTypeName) { " icon_url: classpath://path/to/myicon.jpg\n"+ " version: 0.1.2\n"+ " libraries:\n"+ - " - url: classpath:/" + OsgiStandaloneTest.BROOKLYN_TESTS_OSGI_ENTITIES_0_1_0_URL + "\n"+ + " - url: classpath:/" + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL + "\n"+ "\n"+ "services:\n"+ "- type: brooklyn.osgi.tests.SimpleEntity\n"; From c61e85467e44d60f828f51fa236543d73ab59b77 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 8 Jul 2014 05:26:27 +0100 Subject: [PATCH 25/48] fix CampYamlLiteTest with Sam's suggestion to use a classpath url which we know will be valid --- .../test/java/brooklyn/camp/lite/CampYamlLiteTest.java | 3 ++- .../brooklyn/management/osgi/OsgiStandaloneTest.java | 10 ++++++---- .../brooklyn/camp/lite/test-app-service-blueprint.yaml | 2 +- .../creation/BrooklynComponentTemplateResolver.java | 4 +++- .../rest/resources/CatalogBundleResourceTest.java | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java index de89b0f6a4..17187f4b0b 100644 --- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java +++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java @@ -22,6 +22,7 @@ import brooklyn.entity.Entity; import brooklyn.entity.proxying.EntitySpec; import brooklyn.management.internal.LocalManagementContext; +import brooklyn.management.osgi.OsgiStandaloneTest; import brooklyn.test.entity.LocalManagementContextForTests; import brooklyn.test.entity.TestApplication; import brooklyn.test.entity.TestEntity; @@ -92,7 +93,7 @@ public void testYamlServiceForCatalog() { CatalogItem retrievedItem = Iterables.getOnlyElement(retrievedItems); Assert.assertEquals(retrievedItem, realItem); - Set expectedBundles = Sets.newHashSet("http://www.example.com/bundle.jar"); + Set expectedBundles = Sets.newHashSet(OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL); Assert.assertEquals(retrievedItem.getLibraries().getBundles(), expectedBundles); // Assert.assertEquals(retrievedItem.getVersion(), "0.9"); diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java index 151b2bbeca..722d2567d1 100644 --- a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java +++ b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java @@ -39,8 +39,10 @@ public class OsgiStandaloneTest { private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class); - public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:///brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar"; - public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "/brooklyn/osgi/brooklyn-test-osgi-entities.jar"; + public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:/brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar"; + + public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = "/brooklyn/osgi/brooklyn-test-osgi-entities.jar"; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+BROOKLYN_TEST_OSGI_ENTITIES_PATH; protected Framework framework = null; private File storageTempDir; @@ -149,7 +151,7 @@ public void testReadAManifest() throws Exception { @Test public void testReadKnownManifest() throws Exception { - InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TEST_OSGI_ENTITIES_URL); + InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TEST_OSGI_ENTITIES_PATH); JarInputStream jarIn = new JarInputStream(in); ManifestHelper helper = Osgis.ManifestHelper.forManifest(jarIn.getManifest()); jarIn.close(); @@ -159,7 +161,7 @@ public void testReadKnownManifest() throws Exception { @Test public void testLoadOsgiBundleDependencies() throws Exception { - Bundle bundle = install("classpath:/" + BROOKLYN_TEST_OSGI_ENTITIES_URL); + Bundle bundle = install(BROOKLYN_TEST_OSGI_ENTITIES_URL); Assert.assertNotNull(bundle); Class aClass = bundle.loadClass("brooklyn.osgi.tests.SimpleApplicationImpl"); Object aInst = aClass.newInstance(); diff --git a/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml b/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml index 13a4189642..ef83393b12 100644 --- a/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml +++ b/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml @@ -17,4 +17,4 @@ brooklyn.catalog: libraries: - name: lib1 version: v1 - url: http://www.example.com/bundle.jar \ No newline at end of file + url: classpath:/brooklyn/osgi/brooklyn-test-osgi-entities.jar \ No newline at end of file diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index e30d49e9e8..4180a1a313 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -202,7 +202,9 @@ public Class loadEntityClass() { /** resolves the spec, updating the loader if a catalog item is loaded */ @SuppressWarnings("unchecked") public EntitySpec resolveSpec() { - CatalogItem> item = getCatalogItem(); + // ensure loader is updated + getCatalogItem(); + return (EntitySpec)resolveSpec(loadEntityClass(), null); } diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index 3066ad22fa..ad5948df16 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -93,7 +93,7 @@ private void addCatalogOSGiEntity(String registeredTypeName) { " icon_url: classpath://path/to/myicon.jpg\n"+ " version: 0.1.2\n"+ " libraries:\n"+ - " - url: classpath:/" + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL + "\n"+ + " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL + "\n"+ "\n"+ "services:\n"+ "- type: brooklyn.osgi.tests.SimpleEntity\n"; From c59edfbc24d93302a082daf897225a91fa47dd67 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 8 Jul 2014 05:40:40 +0100 Subject: [PATCH 26/48] fix NPE in BrooklynYamlTypeInstantiatorTest (supertype is null) --- .../classloading/BrooklynClassLoadingContext.java | 6 ++++-- .../AbstractBrooklynClassLoadingContext.java | 12 +++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java b/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java index e1ba3b5405..290876b35f 100644 --- a/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java +++ b/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java @@ -1,5 +1,7 @@ package brooklyn.management.classloading; +import javax.annotation.Nullable; + import brooklyn.management.ManagementContext; import brooklyn.util.guava.Maybe; @@ -11,9 +13,9 @@ public interface BrooklynClassLoadingContext { public ManagementContext getManagementContext(); public Class loadClass(String className); - public Class loadClass(String className, Class type); + public Class loadClass(String className, @Nullable Class supertype); public Maybe> tryLoadClass(String className); - public Maybe> tryLoadClass(String className, Class type); + public Maybe> tryLoadClass(String className, @Nullable Class supertype); } diff --git a/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java index dcf4437851..fe1b45e83b 100644 --- a/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java +++ b/core/src/main/java/brooklyn/management/classloading/AbstractBrooklynClassLoadingContext.java @@ -1,5 +1,7 @@ package brooklyn.management.classloading; +import javax.annotation.Nullable; + import brooklyn.management.ManagementContext; import brooklyn.util.guava.Maybe; @@ -28,18 +30,18 @@ public Class loadClass(String className) { public abstract Maybe> tryLoadClass(String className); @Override - public Class loadClass(String className, Class type) { - return tryLoadClass(className, type).get(); + public Class loadClass(String className, @Nullable Class supertype) { + return tryLoadClass(className, supertype).get(); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override - public Maybe> tryLoadClass(String className, Class type) { + public Maybe> tryLoadClass(String className, @Nullable Class supertype) { Maybe> result = tryLoadClass(className); if (result.isAbsent()) return (Maybe)result; Class clazz = result.get(); - if (type.isAssignableFrom(clazz)) return (Maybe)result; - throw new ClassCastException(className+" is not an instance of "+type); + if (supertype==null || supertype.isAssignableFrom(clazz)) return (Maybe)result; + throw new ClassCastException(className+" is not an instance of "+supertype); } @Override From 9528445c297d9a909f7a0852e561c8ebf989e407 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 8 Jul 2014 06:03:25 +0100 Subject: [PATCH 27/48] fix CatalogResourceTest failure where 1) bundle was not resolved and 2) the default classloader was not used --- .../brooklyn/catalog/internal/CatalogItemDtoAbstract.java | 6 ++++++ .../classloading/BrooklynClassLoadingContextSequential.java | 2 -- .../java/brooklyn/rest/resources/CatalogResourceTest.java | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java index 93675ffa3b..9009889869 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java @@ -4,9 +4,11 @@ import javax.annotation.Nullable; import brooklyn.catalog.CatalogItem; +import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker; import brooklyn.management.ManagementContext; import brooklyn.management.classloading.BrooklynClassLoadingContext; import brooklyn.management.classloading.BrooklynClassLoadingContextSequential; +import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.management.classloading.OsgiBrooklynClassLoadingContext; public abstract class CatalogItemDtoAbstract implements CatalogItem { @@ -103,6 +105,10 @@ public BrooklynClassLoadingContext newClassLoadingContext(final ManagementContex // e.g. run CatalogResourceTest without the above check result.add(new OsgiBrooklynClassLoadingContext(mgmt, getLibraries().getBundles())); + BrooklynClassLoadingContext next = BrooklynLoaderTracker.getLoader(); + if (next==null) next = JavaBrooklynClassLoadingContext.newDefault(mgmt); + result.add(next); + return result; } diff --git a/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java b/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java index 4b714eb73e..cb79b1c382 100644 --- a/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java +++ b/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java @@ -38,8 +38,6 @@ public void add(BrooklynClassLoadingContext target) { } } - /** @since 0.7.0 only for supporting legacy java-classloading based catalog */ - @Deprecated public void addSecondary(BrooklynClassLoadingContext target) { if (!(target instanceof JavaBrooklynClassLoadingContext)) { // support for legacy catalog classloader only diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index 64e3ffaf20..fd83f1d8e7 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -21,6 +21,7 @@ import org.testng.reporters.Files; import brooklyn.catalog.CatalogItem; +import brooklyn.management.osgi.OsgiStandaloneTest; import brooklyn.policy.autoscaling.AutoScalerPolicy; import brooklyn.rest.domain.CatalogEntitySummary; import brooklyn.rest.domain.CatalogItemSummary; @@ -47,9 +48,9 @@ protected void setUpResources() throws Exception { } @Test - public void testRegisterCustomEntity() { + public void testRegisterCustomEntityWithBundleWhereEntityIsFromCore() { String registeredTypeName = "my.catalog.app.id"; - String bundleUrl = "http://myurl/my.jar"; + String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String yaml = "name: "+registeredTypeName+"\n"+ // FIXME name above should be unnecessary when brooklyn.catalog below is working From 4449ab1aee971044349217e926946fe261e4396d Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 8 Jul 2014 06:48:45 +0100 Subject: [PATCH 28/48] specify fixed (snapshot) version of osgi bundle, add an icon, and a readme saying where they come from --- .../test/dependencies/osgi/entities/pom.xml | 2 ++ .../resources/brooklyn/osgi/tests/icon.gif | Bin 0 -> 43 bytes .../test/resources/brooklyn/osgi/README.md | 1 + .../osgi/brooklyn-test-osgi-entities.jar | Bin 10752 -> 10891 bytes 4 files changed, 3 insertions(+) create mode 100644 core/src/test/dependencies/osgi/entities/src/main/resources/brooklyn/osgi/tests/icon.gif create mode 100644 core/src/test/resources/brooklyn/osgi/README.md diff --git a/core/src/test/dependencies/osgi/entities/pom.xml b/core/src/test/dependencies/osgi/entities/pom.xml index 8fa76f09fb..a8dfe199f0 100644 --- a/core/src/test/dependencies/osgi/entities/pom.xml +++ b/core/src/test/dependencies/osgi/entities/pom.xml @@ -4,6 +4,8 @@ jar brooklyn-test-osgi-entities + + 0.1.0-SNAPSHOT OSGi bundled test entities diff --git a/core/src/test/dependencies/osgi/entities/src/main/resources/brooklyn/osgi/tests/icon.gif b/core/src/test/dependencies/osgi/entities/src/main/resources/brooklyn/osgi/tests/icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..e565824aafafe632011b281cba976baf8b3ba89a GIT binary patch literal 43 qcmZ?wbhEHbWMp7uXkcLY4+e@qSs1y10y+#p0Fq%~V)9{Rum%7ZWeN!Z literal 0 HcmV?d00001 diff --git a/core/src/test/resources/brooklyn/osgi/README.md b/core/src/test/resources/brooklyn/osgi/README.md new file mode 100644 index 0000000000..5db7db2ff4 --- /dev/null +++ b/core/src/test/resources/brooklyn/osgi/README.md @@ -0,0 +1 @@ +Sampe OSGi bundle files included here, either are built from /src/dependencies in this project, or contain their sources, or both. diff --git a/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar b/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar index b2bc7ebc109d31dc48c0ee747f347cbfb2b13c82..4093cd308b246d104f240feee86ed3478a1b4643 100644 GIT binary patch delta 2102 zcmZWq2|Qcb8oycWBq5d=5u#!V8iG)5ErVKW9ZOr&3c=X7%9KGhjHR}sZYf50w6)c~ zb}$WXEm2E5Q5_Asd9RDMJkdvWZZg$AZ_e*`?m6e)|M&mCbME*3Pl{)*mz0|$0x1sg z+cTrZ(^5pKr$&o5JwJ5!FTenRaoQZmJmu~fy`YcjwHszO?rL+7beFercz|$=JDYRX z_!aG7{*#o9Y_&(Er(8~4$YRlf5mG@}^z)=qrC{Bv{VWSlZH;EkpI7>MnMDx~b&6kb zbzBNE;7dYWoMR5vdG1{VwSf{+&4|$Q*XNuCxL2 zwz6Lh0dc9)mH}5#=If;R@#}$)HFcliAe(*u}EWF;Y$cATT zrixtZ5qdKV$68A!$b_*9tLm7_68pT??$uyo{_em;6wlFvUmFT z{pm!mRQJd4nB(}Ll7h{br{+0pEmeb$j375DW@3woY&Er;+@aQvX9U!aY%XJTxO$AWz4$<@ z4NT~=q~iR!MZ0I_^0tA+(qa3qvEv}Z74;i*f=-4r5AO?27u&WHOv zWSb`4zSNrUy?=21oy+iX6g_jb6n^GP%t_c2u_GS}VuY3E@}w9iWGm?^D3O>>odl0N zW&KM9xt1b9cQriiZLIeyd;fQW!uX!d)IF3%q_5~1w~{IQ`ybyk9=i>*lRouZEY>7I z+J#dpQNL6yLw-fitj?ghS%oZ^slB7+@pAis)r=Ey;I^lX zLt_m6XajbhP`$x+}y=u zNm`i=MquL~=A6fS#E4WQgpCFYCrXJqYNnmE*wUh|A{8-Cyy%mwtK+e{rOX9Y`9@>B zeA-mT3u#t2Wr7juJvvm&>ZtlyW`47%Pgw!s+;!c9;xhEE8ddgau3f{qzpk^7Y$B$j zUOia;mUu8x{8$PzJoIQV!;7{PAtUwrv1dzE^=760iX{FqjK*EKJ@z2$nrrwmZE93+ zdU#(#HVf$u5-fV^$j%+s8AaHd0jV z$%Qjbk|lXW&?@={j{DxrF0b?#KlH35N1yIltS5Tb;FsCdS)yA|XnTZ@Xp6*N4;UIG8_JuZ|a#AX^;= z&xeC=;POQpl7xTg1#|#gtunP=fK%9#kFUf6Z2-|4IJgP|qS^_1LtKzb*rbVf`12qF z5W1xFwuqpyGSvbJqL37w|KqS>6e>isApj9F(AZ{>2F`0x1tEW{iG!aJhWsdjOkoWA ze<}F;JF1C;4S}aLi7Ncvw6-o}Mxp_LrV9X=ZG?xxaZMEgCbe+SXa@AH1L_d;ORG#> z1yNec0;hUfIM_AN@$)JIT0H%b#$+!(jC4L4Xq5PCV$pA8dzYqXm`DzdR1)y|4Qvd(} delta 1961 zcmZWq2{@bS7XFjiHH#%KjaowlEn#f8D4I%9o^w%Lpw}rPa~J{gd14j^}>=^L*#L-}%n_Jm-JTdB1{#S#+eY z7eYn}kgQ^^h=arBm}EY&mF=Qe_uuX}VTs!y06)U67~ zsoB}YTmz@=g$8YgPl+>=quxJ0^oCm(-T_5$VvyzXv(s)6;-1GyXJ3NxIwH_ z1@GeEEc}PVSH=5`jLgGjqT5ws%SNIUChptGy~rmx(6Vf)us_P9+#fmg9YBsDm4dIA z$JB^l9qs&q;C4E=yEfR5$;>q1yhNN=Psc_acrcr`QZpPCaz2nM+P18J$@`L#jL1Ww z)TK?GdFj_c_=PtYcX;XB{2u(%Kv~E~mK8>^gcL?>>h?T9}yo}_p&zWJ>^PnSbgjr_hGsDux$FE`wi0e$lB%ve6xMqqP-o@ z_P1VL=Bp2>DDmYSYHpmt;zd_NkJ_gncQHfAyga^x=NhmGL{fy_>Fn%}>HkchsT4OG z4V9{t|N44Epf6aH|8HZ+0>G16j)oEzcmqd7l=z;ZRO_MgTVl4CkG%09p1z-);n>9|t=8sfunZXgu+sIoHOE+pEF>N4 za~$rRn1B;hs1?k=?GAwz*_A5uPk_;nTXRYrcfQRwa5EHXCE#rL#Kn2ay(m68cmaK) zeY%j}f8<+F^SZlaGL>q?kx|C+QWDMgDJBJotw~AwawJrt`FLdZF*h2z^|gaXILLUH zg7*jwkk`^@O?MTJjGs-5nOU!{$<#Q*G#IVdUU>Nkdwl1k@lL}L`jrR9+vwOQt#JPy zeS%U)(A&iR>aU){#aZFsH=(1&1t0F6#>V!NXw#qXCid=PUFC6oe4giYTT$Dag$Y*~ zAR@@HD;db*`~0F>#h~DlQc4h&%#L1S1FS*7YvZ%_{cy>D&Ss+U$+UG5Wq4k5_1zIH z+;i^mw4d46_N6b)G_w5OFhtkzd8cCc7F(|0JaxTl_O0oKr2PKM9L^n2AFcbNHCbtq z1M*I*SN=S_?PtHJ;d27g?Y@WYBj#|6l=0+Qw+6Yysg9rB1}rhjtc zGbSxFV_75C1KGr-P?*_9h%MvjW45LWDo-b)DW*I~dmMSR3ZBv^P3$ zvDtK2ky*Em?8?He?y;Te#ibDiw>SawT94qLJGy?OjULQIBJ&>he5k3TWjIjV!_wvk zbzY;n0nhrCRNs8l;Z;UE*gaAi>NBsdyS|n6H#$i2c9#cKb3KdX;dWr2A`tS&aYOML$>aef`iIOfV0TA{|`#vWt&>K=P_mWH-9 zWUbO8Qd8~S0y}qv8Q-h_hD4?atZt6Ecd%EQ_|ChD4K71#w)CR!baS0oct>noaf{sK zjap{mRTFRT>LUZFT6N4GUoRM39kHq5AlC>7IvHtg6>>1t$VNK9P}H6VO&0T5TwHWa zYOKXy)1iSTOW%So8)_3^fJN8uz$K#{(&0xLL43KaFQxV+;NZ5h(2)ViCaCH{f Date: Tue, 8 Jul 2014 06:50:10 +0100 Subject: [PATCH 29/48] switch ResourceUtils so it can use BrooklynClassLoadingContext, and extend BCLC so it can support getResource, and use a ResourceUtils with item-specific BCLC when resolving icons, with test in CatalogResourceTest). (this is just one place of many that i expect we will have to do some tweaking to ResourceUtils, but it confirms/shows how it can be done elsewhere) --- .../BrooklynClassLoadingContext.java | 5 ++ ...BrooklynClassLoadingContextSequential.java | 15 ++++++ .../JavaBrooklynClassLoadingContext.java | 12 ++++- .../OsgiBrooklynClassLoadingContext.java | 12 +++++ .../brooklyn/management/ha/OsgiManager.java | 21 ++++++++ .../internal/AbstractManagementContext.java | 13 +++-- .../java/brooklyn/util/ResourceUtils.java | 54 +++++++++++++++---- .../BrooklynAssemblyTemplateInstantiator.java | 1 - .../cli/src/main/java/brooklyn/cli/Main.java | 3 +- .../rest/resources/CatalogResource.java | 2 +- .../rest/resources/CatalogResourceTest.java | 9 ++-- 11 files changed, 124 insertions(+), 23 deletions(-) diff --git a/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java b/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java index 290876b35f..cba2bc5633 100644 --- a/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java +++ b/api/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContext.java @@ -1,5 +1,7 @@ package brooklyn.management.classloading; +import java.net.URL; + import javax.annotation.Nullable; import brooklyn.management.ManagementContext; @@ -18,4 +20,7 @@ public interface BrooklynClassLoadingContext { public Maybe> tryLoadClass(String className); public Maybe> tryLoadClass(String className, @Nullable Class supertype); + /** as {@link ClassLoader#getResource(String)} */ + public URL getResource(String name); + } diff --git a/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java b/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java index cb79b1c382..7889de4493 100644 --- a/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java +++ b/core/src/main/java/brooklyn/management/classloading/BrooklynClassLoadingContextSequential.java @@ -1,5 +1,6 @@ package brooklyn.management.classloading; +import java.net.URL; import java.util.List; import java.util.Set; @@ -61,6 +62,20 @@ public Maybe> tryLoadClass(String className) { return Maybe.absent("Unable to load "+className+" from "+primaries); } + + @Override + public URL getResource(String resourceInThatDir) { + for (BrooklynClassLoadingContext target: primaries) { + URL result = target.getResource(resourceInThatDir); + if (result!=null) return result; + } + for (BrooklynClassLoadingContext target: secondaries) { + URL result = target.getResource(resourceInThatDir); + if (result!=null) return result; + } + return null; + } + @Override public String toString() { return "classload:"+primaries+";"+secondaries; diff --git a/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java index 8cae3bea8d..bc6b4d202e 100644 --- a/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java +++ b/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java @@ -1,5 +1,7 @@ package brooklyn.management.classloading; +import java.net.URL; + import com.google.common.base.Objects; import brooklyn.management.ManagementContext; @@ -16,7 +18,10 @@ public JavaBrooklynClassLoadingContext(ManagementContext mgmt, ClassLoader loade } public static JavaBrooklynClassLoadingContext newDefault(ManagementContext mgmt) { - return new JavaBrooklynClassLoadingContext(mgmt, JavaBrooklynClassLoadingContext.class.getClassLoader()); + ClassLoader cl = null; + if (mgmt!=null) cl = mgmt.getCatalog().getRootClassLoader(); + if (cl==null) cl = JavaBrooklynClassLoadingContext.class.getClassLoader(); + return new JavaBrooklynClassLoadingContext(mgmt, cl); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -46,5 +51,10 @@ public boolean equals(Object obj) { if (!Objects.equal(loader, ((JavaBrooklynClassLoadingContext)obj).loader)) return false; return true; } + + @Override + public URL getResource(String name) { + return loader.getResource(name); + } } diff --git a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java index 50650780a4..7011354184 100644 --- a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java +++ b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java @@ -1,5 +1,6 @@ package brooklyn.management.classloading; +import java.net.URL; import java.util.List; import brooklyn.management.ManagementContext; @@ -60,5 +61,16 @@ public boolean equals(Object obj) { if (!Objects.equal(bundles, ((OsgiBrooklynClassLoadingContext)obj).bundles)) return false; return true; } + + @Override + public URL getResource(String name) { + if (mgmt!=null) { + Maybe osgi = ((ManagementContextInternal)mgmt).getOsgiManager(); + if (osgi.isPresent() && bundles!=null && !bundles.isEmpty()) { + return osgi.get().getResource(name, bundles); + } + } + return null; + } } diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java index 0129481e80..7ab61eb757 100644 --- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java +++ b/core/src/main/java/brooklyn/management/ha/OsgiManager.java @@ -1,6 +1,7 @@ package brooklyn.management.ha; import java.io.File; +import java.net.URL; import java.util.Arrays; import java.util.Map; @@ -106,4 +107,24 @@ public Maybe> tryResolveClass(String type, Iterable bundleU return Maybe.absent("Unable to resolve class "+type+": "+bundleProblems); } + public URL getResource(String name, Iterable bundleUrlsOrNameVersionString) { + for (String bundleUrlOrNameVersionString: bundleUrlsOrNameVersionString) { + try { + String bundleNameVersion = bundleUrlToNameVersionString.get(bundleUrlOrNameVersionString); + if (bundleNameVersion==null) { + bundleNameVersion = bundleUrlOrNameVersionString; + } + + Maybe bundle = Osgis.getBundle(framework, bundleNameVersion); + if (bundle.isPresent()) { + URL result = bundle.get().getResource(name); + if (result!=null) return result; + } + } catch (Throwable e) { + Exceptions.propagateIfFatal(e); + } + } + return null; + } + } diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java index ae94534d62..8c1e579a18 100644 --- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java +++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java @@ -40,8 +40,11 @@ import brooklyn.location.LocationRegistry; import brooklyn.location.basic.BasicLocationRegistry; import brooklyn.management.ExecutionContext; +import brooklyn.management.ManagementContext; import brooklyn.management.SubscriptionContext; import brooklyn.management.Task; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.management.entitlement.EntitlementManager; import brooklyn.management.entitlement.Entitlements; import brooklyn.management.ha.HighAvailabilityManager; @@ -93,15 +96,17 @@ private static DataGridFactory loadDataGridFactory(BrooklynProperties properties static { // ensure that if ResourceUtils is given an entity as context, // we use the catalog class loader (e.g. to resolve classpath URLs) - ResourceUtils.addClassLoaderProvider(new Function() { + ResourceUtils.addClassLoaderProvider(new Function() { @Override - public ClassLoader apply(@Nullable Object input) { + public BrooklynClassLoadingContext apply(@Nullable Object input) { + // TODO for entities, this should get its originating catalog item's loader if (input instanceof EntityInternal) return apply(((EntityInternal)input).getManagementSupport()); + if (input instanceof EntityManagementSupport) return apply(((EntityManagementSupport)input).getManagementContext()); - if (input instanceof AbstractManagementContext) - return ((AbstractManagementContext)input).getCatalog().getRootClassLoader(); + if (input instanceof ManagementContext) + return JavaBrooklynClassLoadingContext.newDefault((ManagementContext) input); return null; } }); diff --git a/core/src/main/java/brooklyn/util/ResourceUtils.java b/core/src/main/java/brooklyn/util/ResourceUtils.java index 347e0a2c15..1b38dbc204 100644 --- a/core/src/main/java/brooklyn/util/ResourceUtils.java +++ b/core/src/main/java/brooklyn/util/ResourceUtils.java @@ -20,7 +20,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker; import brooklyn.location.basic.SshMachineLocation; +import brooklyn.management.ManagementContext; +import brooklyn.management.classloading.BrooklynClassLoadingContext; +import brooklyn.management.classloading.JavaBrooklynClassLoadingContext; import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.javalang.Threads; @@ -38,9 +42,9 @@ public class ResourceUtils { private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class); - private static final List> classLoaderProviders = Lists.newCopyOnWriteArrayList(); + private static final List> classLoaderProviders = Lists.newCopyOnWriteArrayList(); - private ClassLoader loader = null; + private BrooklynClassLoadingContext loader = null; private String context = null; private Object contextObject = null; @@ -58,6 +62,20 @@ public static final ResourceUtils create(ClassLoader loader, Object contextObjec return new ResourceUtils(loader, contextObject, contextMessage); } + /** + * Creates a {@link ResourceUtils} object with a specific class loader and context. + *

+ * Use the provided {@link BrooklynClassLoadingContext} object for class loading with the + * {@code contextObject} for context and the {@code contextMessage} string for + * error messages. + * + * @see ResourceUtils#create(Object, String) + * @see ResourceUtils#create(Object) + */ + public static final ResourceUtils create(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) { + return new ResourceUtils(loader, contextObject, contextMessage); + } + /** * Creates a {@link ResourceUtils} object with the given context. *

@@ -95,13 +113,16 @@ public static final ResourceUtils create() { } public ResourceUtils(ClassLoader loader, Object contextObject, String contextMessage) { + } + + public ResourceUtils(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) { this.loader = loader; this.contextObject = contextObject; this.context = contextMessage; } public ResourceUtils(Object contextObject, String contextMessage) { - this(contextObject==null ? null : getClassLoaderForObject(contextObject), contextObject, contextMessage); + this(contextObject==null ? null : getClassLoadingContextForObject(contextObject), contextObject, contextMessage); } public ResourceUtils(Object contextObject) { @@ -109,23 +130,34 @@ public ResourceUtils(Object contextObject) { } /** used to register custom mechanisms for getting classloaders given an object */ - public static void addClassLoaderProvider(Function provider) { + public static void addClassLoaderProvider(Function provider) { classLoaderProviders.add(provider); } - public static ClassLoader getClassLoaderForObject(Object contextObject) { - for (Function provider: classLoaderProviders) { - ClassLoader result = provider.apply(contextObject); + public static BrooklynClassLoadingContext getClassLoadingContextForObject(Object contextObject) { + if (contextObject instanceof BrooklynClassLoadingContext) + return (BrooklynClassLoadingContext) contextObject; + + for (Function provider: classLoaderProviders) { + BrooklynClassLoadingContext result = provider.apply(contextObject); if (result!=null) return result; } - return contextObject instanceof Class ? ((Class)contextObject).getClassLoader() : + + ClassLoader cl = contextObject instanceof Class ? ((Class)contextObject).getClassLoader() : contextObject instanceof ClassLoader ? ((ClassLoader)contextObject) : contextObject.getClass().getClassLoader(); + return getClassLoadingContextForClassLoader(cl); + } + + protected static BrooklynClassLoadingContext getClassLoadingContextForClassLoader(ClassLoader loader) { + ManagementContext mgmt = null; + BrooklynClassLoadingContext bl = BrooklynLoaderTracker.getLoader(); + if (bl!=null) mgmt = bl.getManagementContext(); + return new JavaBrooklynClassLoadingContext(mgmt, loader); } - public ClassLoader getLoader() { - //TODO allow a sequence of loaders? - return (loader!=null ? loader : getClass().getClassLoader()); + public BrooklynClassLoadingContext getLoader() { + return (loader!=null ? loader : getClassLoadingContextForClassLoader(getClass().getClassLoader())); } /** diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index b2fa56e3fb..dcd0e79353 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -275,7 +275,6 @@ private EntitySpec toCoreEntitySpec(Class createApplicationFromNonCatalogCampTemplate(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader) { // AssemblyTemplates created via PDP, _specifying_ then entities to put in - final ManagementContext mgmt = getBrooklynManagementContext(platform); BrooklynComponentTemplateResolver resolver = BrooklynComponentTemplateResolver.Factory.newInstance( loader, template); diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java index 0a0e67bb03..61c1edffd7 100644 --- a/usage/cli/src/main/java/brooklyn/cli/Main.java +++ b/usage/cli/src/main/java/brooklyn/cli/Main.java @@ -354,8 +354,7 @@ public Void call() throws Exception { computeLocations(); ResourceUtils utils = ResourceUtils.create(this); - ClassLoader parent = utils.getLoader(); - GroovyClassLoader loader = new GroovyClassLoader(parent); + GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader()); // First, run a setup script if the user has provided one if (script != null) { diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java index 78ca63c176..e0df7e3387 100644 --- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java +++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java @@ -155,7 +155,7 @@ public Response getIcon(String itemId) { log.debug("Loading and returning "+url+" as icon for "+result); MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(Files.getFileExtension(url)); - Object content = ResourceUtils.create(brooklyn().getCatalog().getRootClassLoader()).getResourceFromUrl(url); + Object content = ResourceUtils.create(result.newClassLoadingContext(mgmt())).getResourceFromUrl(url); return Response.ok(content, mime).build(); } diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index fd83f1d8e7..1ecd05958c 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -48,7 +48,7 @@ protected void setUpResources() throws Exception { } @Test - public void testRegisterCustomEntityWithBundleWhereEntityIsFromCore() { + public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBundle() { String registeredTypeName = "my.catalog.app.id"; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String yaml = @@ -58,7 +58,7 @@ public void testRegisterCustomEntityWithBundleWhereEntityIsFromCore() { " id: " + registeredTypeName + "\n"+ " name: My Catalog App\n"+ " description: My description\n"+ - " icon_url: classpath://path/to/myicon.jpg\n"+ + " icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n"+ " version: 0.1.2\n"+ " libraries:\n"+ " - url: " + bundleUrl + "\n"+ @@ -93,7 +93,10 @@ public void testRegisterCustomEntityWithBundleWhereEntityIsFromCore() { assertEquals(entityItem.getName(), "My Catalog App"); assertEquals(entityItem.getDescription(), "My description"); assertEquals(entityItem.getIconUrl(), "/v1/catalog/icon/my.catalog.app.id"); - assertEquals(item.getIconUrl(), "classpath://path/to/myicon.jpg"); + assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif"); + + byte[] iconData = client().resource("/v1/catalog/icon/"+registeredTypeName).get(byte[].class); + log.info("ICON: "+new String(iconData)); } @Test From 5ec0b8a3746d7a82fe9cd97fb4f560876c6d49ef Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 8 Jul 2014 07:14:41 +0100 Subject: [PATCH 30/48] more tests and TODOs of tests we need --- .../brooklyn/camp/lite/CampYamlLiteTest.java | 48 +++++++++++++++++++ .../rest/resources/CatalogResourceTest.java | 6 ++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java index 17187f4b0b..120ee9d576 100644 --- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java +++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java @@ -1,12 +1,15 @@ package brooklyn.camp.lite; +import static org.testng.Assert.assertEquals; import io.brooklyn.camp.spi.Assembly; import io.brooklyn.camp.spi.AssemblyTemplate; import io.brooklyn.camp.spi.pdp.PdpYamlTest; import io.brooklyn.camp.test.mock.web.MockWebPlatform; +import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.util.List; import java.util.Map; import java.util.Set; @@ -26,6 +29,8 @@ import brooklyn.test.entity.LocalManagementContextForTests; import brooklyn.test.entity.TestApplication; import brooklyn.test.entity.TestEntity; +import brooklyn.util.ResourceUtils; +import brooklyn.util.collections.MutableList; import brooklyn.util.stream.Streams; import com.google.common.base.Predicates; @@ -104,5 +109,48 @@ public void testYamlServiceForCatalog() { // TODO other assertions, about children } + + @Test + public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBundle() throws IOException { + String registeredTypeName = "my.catalog.app.id"; + String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; + String yaml = + "brooklyn.catalog:\n"+ + " id: " + registeredTypeName + "\n"+ + " name: My Catalog App\n"+ + " description: My description\n"+ + " icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n"+ + " version: 0.1.2\n"+ + " libraries:\n"+ + " - url: " + bundleUrl + "\n"+ + "\n"+ + "services:\n"+ + "- type: brooklyn.test.entity.TestEntity\n"; + + mgmt.getCatalog().addItem(yaml); + + CatalogItem item = mgmt.getCatalog().getCatalogItem(registeredTypeName); + assertEquals(item.getRegisteredTypeName(), registeredTypeName); + + // stored as yaml, not java +// assertEquals(entityItem.getJavaType(), "brooklyn.test.entity.TestEntity"); + Assert.assertNotNull(item.getPlanYaml()); + Assert.assertTrue(item.getPlanYaml().contains("brooklyn.test.entity.TestEntity")); + + assertEquals(item.getId(), registeredTypeName); + + // and let's check we have libraries + List libs = item.getLibraries().getBundles(); + assertEquals(libs, MutableList.of(bundleUrl)); + // now let's check other things on the item + assertEquals(item.getName(), "My Catalog App"); + assertEquals(item.getDescription(), "My description"); + assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif"); + + // and confirm we can resolve ICON + byte[] iconData = Streams.readFully( ResourceUtils.create(item.newClassLoadingContext(mgmt)).getResourceFromUrl(item.getIconUrl()) ); + assertEquals(iconData.length, 43); + } + } diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index 1ecd05958c..708915c1c7 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -48,6 +48,7 @@ protected void setUpResources() throws Exception { } @Test + /** based on CampYamlLiteTest */ public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBundle() { String registeredTypeName = "my.catalog.app.id"; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; @@ -96,7 +97,7 @@ public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBu assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif"); byte[] iconData = client().resource("/v1/catalog/icon/"+registeredTypeName).get(byte[].class); - log.info("ICON: "+new String(iconData)); + assertEquals(iconData.length, 43); } @Test @@ -107,6 +108,9 @@ public void testListAllEntities() { assertTrue(entities.size() > 0); } + // TODO test registering entity from the bundle using new registered name + // TODO test registering entity from the bundle using type of item and the registered name (check endless loop) + @Test public void testFilterListOfEntitiesByName() { List entities = client().resource("/v1/catalog/entities") From 5ecf7b4b4cc63f1e024df6db460b6fd346124906 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 10:27:33 +0300 Subject: [PATCH 31/48] Support for referencing Java catalog entities in YAML --- .../BrooklynAssemblyTemplateInstantiator.java | 68 ++++++++++--------- .../resources/ApplicationResourceTest.java | 18 +++++ 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index dcd0e79353..5f4b0b53fb 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -342,38 +342,10 @@ private List> buildTemplateServicesAsSpecs(BrooklynClassLoadingCon EntitySpec spec; CatalogItem> item = entityResolver.getCatalogItem(); - if (item != null) { - String yaml = item.getPlanYaml(); - Reader input = new StringReader(yaml); - - AssemblyTemplate at; - BrooklynClassLoadingContext itemLoader = item.newClassLoadingContext(mgmt); - BrooklynLoaderTracker.setLoader(itemLoader); - try { - at = platform.pdp().registerDeploymentPlan(input); - } finally { - BrooklynLoaderTracker.unsetLoader(itemLoader); - } - - // TODO Should we check entitlements, or sufficient to do once for this being called in the first place? - // TODO Is it acceptable to only allow a single top-level entity in a catalog? If not, we need to think - // about what it would mean to subsequently call buildChildrenEntitySpecs on the list of top-level entities! - try { - AssemblyTemplateInstantiator ati = at.getInstantiator().newInstance(); - if (ati instanceof BrooklynAssemblyTemplateInstantiator) { - List> specs = ((BrooklynAssemblyTemplateInstantiator)ati).buildTemplateServicesAsSpecs(itemLoader, at, platform); - if (specs.size() > 1) { - throw new UnsupportedOperationException("Only supporting single service in catalog item currently"); - } - spec = specs.get(0); - } else { - throw new IllegalStateException("Cannot create application with instantiator: " + ati); - } - } catch (Exception e) { - throw Exceptions.propagate(e); - } - } else { + if (item == null || item.getJavaType() != null) { spec = entityResolver.resolveSpec(); + } else { + spec = resolveCatalogYamlReferenceSpec(platform, mgmt, item); } BrooklynClassLoadingContext newLoader = entityResolver.loader; @@ -384,6 +356,40 @@ private List> buildTemplateServicesAsSpecs(BrooklynClassLoadingCon return result; } + private EntitySpec resolveCatalogYamlReferenceSpec(CampPlatform platform, + ManagementContext mgmt, + CatalogItem> item) { + String yaml = item.getPlanYaml(); + Reader input = new StringReader(yaml); + + AssemblyTemplate at; + BrooklynClassLoadingContext itemLoader = item.newClassLoadingContext(mgmt); + BrooklynLoaderTracker.setLoader(itemLoader); + try { + at = platform.pdp().registerDeploymentPlan(input); + } finally { + BrooklynLoaderTracker.unsetLoader(itemLoader); + } + + // TODO Should we check entitlements, or sufficient to do once for this being called in the first place? + // TODO Is it acceptable to only allow a single top-level entity in a catalog? If not, we need to think + // about what it would mean to subsequently call buildChildrenEntitySpecs on the list of top-level entities! + try { + AssemblyTemplateInstantiator ati = at.getInstantiator().newInstance(); + if (ati instanceof BrooklynAssemblyTemplateInstantiator) { + List> specs = ((BrooklynAssemblyTemplateInstantiator)ati).buildTemplateServicesAsSpecs(itemLoader, at, platform); + if (specs.size() > 1) { + throw new UnsupportedOperationException("Only supporting single service in catalog item currently"); + } + return specs.get(0); + } else { + throw new IllegalStateException("Cannot create application with instantiator: " + ati); + } + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + protected void buildChildrenEntitySpecs(BrooklynClassLoadingContext loader, EntitySpec parent, List> childConfig) { if (childConfig != null) { for (Map childAttrs : childConfig) { diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java index 9a0558d25b..c2820d5555 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java @@ -23,6 +23,7 @@ import brooklyn.entity.Application; import brooklyn.entity.basic.BasicApplication; +import brooklyn.entity.basic.BasicEntity; import brooklyn.entity.basic.EntityFunctions; import brooklyn.entity.basic.Lifecycle; import brooklyn.location.Location; @@ -200,6 +201,23 @@ public void testDeployApplicationYaml() throws Exception { assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); } + @Test + public void testReferenceCatalogEntity() throws Exception { + getManagementContext().getCatalog().addItem(BasicEntity.class); + + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: " + BasicEntity.class.getName() + " } ] }"; + + ClientResponse response = client().resource("/v1/applications") + .entity(yaml, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response.getStatus()/100 == 2, "response is "+response); + + // Expect app to be running + URI appUri = response.getLocation(); + waitForApplicationToBeRunning(response.getLocation()); + assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); + } + @Test public void testDeployWithInvalidEntityType() { try { From b919b8b4ae42bf5bacb083d1c2bc920854f10f53 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 10:33:28 +0300 Subject: [PATCH 32/48] Aggregate the class loaders of classes involved in the proxy creation. Should be more generic than the previous approach. --- .../brooklyn/entity/proxying/InternalEntityFactory.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java index 55535e9004..4188a93bb5 100644 --- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java +++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java @@ -129,8 +129,10 @@ public T createEntityProxy(EntitySpec spec, T entity) { // Building our own aggregating class loader gets around this. // But we really should not have to do this! What are the consequences? AggregateClassLoader aggregateClassLoader = AggregateClassLoader.newInstanceWithNoLoaders(); - aggregateClassLoader.addFirst(classloader); - aggregateClassLoader.addLast(Entity.class.getClassLoader()); + aggregateClassLoader.addFirst(entity.getClass().getClassLoader()); + for(Class iface : interfaces) { + aggregateClassLoader.addLast(iface.getClassLoader()); + } return (T) java.lang.reflect.Proxy.newProxyInstance( aggregateClassLoader, From 10cc3727bbcd730b63311804e287e9dba302dc31 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 10:36:25 +0300 Subject: [PATCH 33/48] Remove unused var --- .../java/brooklyn/entity/proxying/InternalEntityFactory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java index 4188a93bb5..1e953b6ed5 100644 --- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java +++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java @@ -111,7 +111,6 @@ public InternalEntityFactory(ManagementContextInternal managementContext, Entity public T createEntityProxy(EntitySpec spec, T entity) { // TODO Don't want the proxy to have to implement EntityLocal, but required by how // AbstractEntity.parent is used (e.g. parent.getAllConfig) - ClassLoader classloader = (spec.getImplementation() != null ? spec.getImplementation() : spec.getType()).getClassLoader(); MutableSet.Builder> builder = MutableSet.>builder() .add(EntityProxy.class, Entity.class, EntityLocal.class, EntityInternal.class); if (spec.getType().isInterface()) { From 86ce92210ccd7defaae8187cd30f259b393bb973 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 10:43:53 +0300 Subject: [PATCH 34/48] Remove entitlements TODO (no requirement to do per-entity instantiation); Multiple root entities not to be supported for the time being. --- .../spi/creation/BrooklynAssemblyTemplateInstantiator.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index 5f4b0b53fb..fed65184c5 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -371,9 +371,8 @@ private EntitySpec resolveCatalogYamlReferenceSpec(CampPlatform platform, BrooklynLoaderTracker.unsetLoader(itemLoader); } - // TODO Should we check entitlements, or sufficient to do once for this being called in the first place? - // TODO Is it acceptable to only allow a single top-level entity in a catalog? If not, we need to think - // about what it would mean to subsequently call buildChildrenEntitySpecs on the list of top-level entities! + // In case we want to allow multiple top-level entities in a catalog we need to think + // about what it would mean to subsequently call buildChildrenEntitySpecs on the list of top-level entities! try { AssemblyTemplateInstantiator ati = at.getInstantiator().newInstance(); if (ati instanceof BrooklynAssemblyTemplateInstantiator) { From f51551768f0f13cc84212f4ec85d3d958589d924 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 10:57:55 +0300 Subject: [PATCH 35/48] Add a test for a catalog referencing itself. --- .../resources/CatalogBundleResourceTest.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index ad5948df16..2fc3102d01 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -21,6 +21,8 @@ public class CatalogBundleResourceTest extends BrooklynRestResourceTest { + private static final String SIMPLE_ENTITY_TYPE = "brooklyn.osgi.tests.SimpleEntity"; + @Test public void testListApplicationYaml() throws Exception { String registeredTypeName = "my.catalog.app.id.load"; @@ -35,7 +37,17 @@ public void testListApplicationYaml() throws Exception { @Test public void testLaunchApplicationYaml() throws Exception { String registeredTypeName = "my.catalog.app.id.launch"; - addCatalogOSGiEntity(registeredTypeName); + registerAndLaunch(registeredTypeName, SIMPLE_ENTITY_TYPE); + } + + @Test + public void testLaunchApplicationLoopYaml() throws Exception { + String registeredTypeName = "my.catalog.app.id.launch"; + registerAndLaunch(registeredTypeName, registeredTypeName); + } + + private void registerAndLaunch(String registeredTypeName, String catalogServiceType) { + addCatalogOSGiEntity(registeredTypeName, catalogServiceType); String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; @@ -53,7 +65,7 @@ public void testLaunchApplicationYaml() throws Exception { Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); - assertEquals(simpleEntity.getEntityType().getName(), "brooklyn.osgi.tests.SimpleEntity"); + assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE); } @Test @@ -79,10 +91,15 @@ public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); - assertEquals(simpleEntity.getEntityType().getName(), "brooklyn.osgi.tests.SimpleEntity"); + assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE); } + private void addCatalogOSGiEntity(String registeredTypeName) { + addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE); + } + + private void addCatalogOSGiEntity(String registeredTypeName, String catalogServiceType) { String catalogYaml = "name: "+registeredTypeName+"\n"+ // FIXME name above should be unnecessary when brooklyn.catalog below is working @@ -96,9 +113,9 @@ private void addCatalogOSGiEntity(String registeredTypeName) { " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL + "\n"+ "\n"+ "services:\n"+ - "- type: brooklyn.osgi.tests.SimpleEntity\n"; + "- type: " + catalogServiceType; - addCatalogEntity(registeredTypeName, catalogYaml); + addCatalogEntity(catalogYaml); } private void addCatalogEntityReferencingCatalogEntry(String ownRegisteredTypeName, String otherRegisteredTypeName) { @@ -115,10 +132,10 @@ private void addCatalogEntityReferencingCatalogEntry(String ownRegisteredTypeNam "services:\n"+ "- type: "+otherRegisteredTypeName+"\n"; - addCatalogEntity(ownRegisteredTypeName, catalogYaml); + addCatalogEntity(catalogYaml); } - private void addCatalogEntity(String registeredTypeName, String catalogYaml) { + private void addCatalogEntity(String catalogYaml) { ClientResponse catalogResponse = client().resource("/v1/catalog") .post(ClientResponse.class, catalogYaml); From ff2a8a062c73ab0c89187d377b6efdac9983724a Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 11:23:19 +0300 Subject: [PATCH 36/48] The build fails when an explicit version is set, with the following error: The following artifacts could not be resolved: io.brooklyn:brooklyn-test-support:jar:0.1.0-SNAPSHOT, io.brooklyn:brooklyn-logback-includes:jar:0.1.0-SNAPSHOT Caused by the usage of ${project.version} in the parent dependencies instead of ${brooklyn.version} Instead I fixed the bundle version to 0.1.0 with the maven-bundle-plugin. This project is tied to the brooklyn version in a way because its build depends on the parent pom. --- core/src/test/dependencies/osgi/entities/pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/test/dependencies/osgi/entities/pom.xml b/core/src/test/dependencies/osgi/entities/pom.xml index a8dfe199f0..8fa76f09fb 100644 --- a/core/src/test/dependencies/osgi/entities/pom.xml +++ b/core/src/test/dependencies/osgi/entities/pom.xml @@ -4,8 +4,6 @@ jar brooklyn-test-osgi-entities - - 0.1.0-SNAPSHOT OSGi bundled test entities From 02137ef3724eb90b4b8d1608c06d4b83578b14c7 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Tue, 8 Jul 2014 10:02:04 +0100 Subject: [PATCH 37/48] CAMP catalog handling: delete unused code --- .../BrooklynAssemblyTemplateInstantiator.java | 142 +----------------- 1 file changed, 2 insertions(+), 140 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index fed65184c5..47e84122c6 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -9,8 +9,6 @@ import java.io.Reader; import java.io.StringReader; -import java.lang.reflect.Constructor; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -23,20 +21,14 @@ import brooklyn.catalog.BrooklynCatalog; import brooklyn.catalog.CatalogItem; import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker; -import brooklyn.config.ConfigKey; import brooklyn.entity.Application; import brooklyn.entity.Entity; -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.BasicApplication; import brooklyn.entity.basic.BasicApplicationImpl; import brooklyn.entity.basic.Entities; -import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.EntityLocal; -import brooklyn.entity.basic.EntityTypes; import brooklyn.entity.basic.StartableApplication; import brooklyn.entity.proxying.EntitySpec; import brooklyn.entity.trait.Startable; -import brooklyn.location.Location; import brooklyn.management.ManagementContext; import brooklyn.management.Task; import brooklyn.management.classloading.BrooklynClassLoadingContext; @@ -45,11 +37,9 @@ import brooklyn.util.collections.MutableMap; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.flags.TypeCoercions; -import brooklyn.util.text.Strings; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpecInstantiator { @@ -100,18 +90,10 @@ protected AppOrSpec createAppOrSpec(AssemblyTemplate template, CampPlatform plat BrooklynClassLoadingContext loader; if (item!=null) { loader = item.newClassLoadingContext(mgmt); - // TODO legacy path - currently item is always null because template.id is a random String, - // and pretty sure that is the desired behaviour; we now (Jul 2014) automatically promote - // so fine for users always to put the catalog registeredType in the services block; - // if we did want to support users supplying an `id' that would be available not via the above - // but via template.getCustomAttributes().get("id"); - - // FIXME No longer called; delete: - // return createApplicationFromCatalog(platform, item, template, requireSpec); } else { loader = JavaBrooklynClassLoadingContext.newDefault(mgmt); } - return new AppOrSpec(createApplicationFromNonCatalogCampTemplate(template, platform, loader)); + return new AppOrSpec(createApplicationFromCampTemplate(template, platform, loader)); } private static class AppOrSpec { @@ -142,86 +124,6 @@ public EntitySpec getSpec() { } } - protected AppOrSpec createApplicationFromCatalog(CampPlatform platform, CatalogItem item, AssemblyTemplate template, boolean requireSpec) { - ManagementContext mgmt = getBrooklynManagementContext(platform); - - if (!template.getApplicationComponentTemplates().isEmpty() || - !template.getPlatformComponentTemplates().isEmpty()) - log.warn("CAMP AssemblyTemplate was not empty when creating from catalog spec; ignoring templates declared within it " + - "("+template+")"); - - // name (and description) -- not prescribed by camp spec (cf discussion with gil) - String name = template.getName(); - - String type = item.getJavaType(); - - // Load the class; first try to use the appropriate catalog item; but then allow anything that is on the classpath - final Class clazz; - if (Strings.isEmpty(type)) { - clazz = BasicApplication.class; - } else { - clazz = item.newClassLoadingContext(mgmt).loadClass(type, Entity.class); - } - - try { - if (ApplicationBuilder.class.isAssignableFrom(clazz)) { - if (requireSpec) { - // TODO we could enable this, by returning the spec from the ApplicationBuilder; - // note that we would have to set it up so that ApplicationBuilder.doBuild is called from an - // entity initializer set on the spec, and remove the doBuild call from ApplicationBuilder.manage. - // (this could also allow ApplicationBuilder instances to be used from catalog) - throw new IllegalStateException("ApplicationBuilder items cannot be used when specs have to be created"); - } - Constructor constructor = clazz.getConstructor(); - ApplicationBuilder appBuilder = (ApplicationBuilder) constructor.newInstance(); - // for builder, we (1) can't get a spec (so discourage use of Builder?), - // and (2) we have to manually extract key bits of the template - - if (!Strings.isEmpty(name)) appBuilder.appDisplayName(name); - - // TODO use resolver's configureEntitySpec instead - final Map configO = (Map) template.getCustomAttributes().get("brooklyn.config"); - - log.info("CAMP placing '{}' under management", appBuilder); - appBuilder.configure( convertFlagsToKeys(appBuilder.getType(), configO) ); - Application instance = appBuilder.manage(mgmt); - - applyLocations(mgmt, template, instance); - - return new AppOrSpec(instance); - - } else if (Application.class.isAssignableFrom(clazz)) { - // TODO use resolver's configureEntitySpec instead - final Map configO = (Map) template.getCustomAttributes().get("brooklyn.config"); - - @SuppressWarnings("unchecked") - brooklyn.entity.proxying.EntitySpec coreSpec = toCoreEntitySpec((Class)clazz, name, configO); - applyLocations(mgmt, template, coreSpec); - - return new AppOrSpec(coreSpec); - - } else { - throw new IllegalArgumentException("Class "+clazz+" must extend one of ApplicationBuilder or Application"); - } - - } catch (Exception e) { - log.error("CAMP failed to create application: "+e, e); - throw Exceptions.propagate(e); - } - } - - private void applyLocations(ManagementContext mgmt, AssemblyTemplate template, final Application instance) { - List locations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(template.getCustomAttributes(), false); - if (locations!=null) - ((EntityInternal)instance).addLocations(locations); - } - - private void applyLocations(ManagementContext mgmt, AssemblyTemplate template, final EntitySpec spec) { - List locations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(template.getCustomAttributes(), false); - if (locations!=null) - spec.locations(locations); - } - private ManagementContext getBrooklynManagementContext(CampPlatform platform) { return ((HasBrooklynManagementContext)platform).getBrooklynManagementContext(); } @@ -233,47 +135,8 @@ public Task start(Application app, CampPlatform platform) { MutableMap.of("locations", MutableList.of())); } - // TODO this is an exact copy of BrooklynRestResoureUtils; make available to both somehow? (or even better, avoid somehow) - private static Map convertFlagsToKeys(Class javaType, Map config) { - if (config==null || config.isEmpty() || javaType==null) return config; - - Map> configKeys = EntityTypes.getDefinedConfigKeys(javaType); - Map result = new LinkedHashMap(); - for (Map.Entry entry: config.entrySet()) { - log.debug("Setting key {} to {} for CAMP creation of {}", new Object[] { entry.getKey(), entry.getValue(), javaType}); - Object key = configKeys.get(entry.getKey()); - if (key==null) { - log.warn("Unrecognised config key {} passed to {}; will be treated as flag (and likely ignored)", entry.getKey(), javaType); - key = entry.getKey(); - } - result.put(key, entry.getValue()); - } - return result; - } - - // TODO exact copy of BRRU, as above - @SuppressWarnings({ "rawtypes", "unchecked" }) - private EntitySpec toCoreEntitySpec(Class clazz, String name, Map configO) { - Map config = (configO == null) ? Maps.newLinkedHashMap() : Maps.newLinkedHashMap(configO); - - EntitySpec result; - if (clazz.isInterface()) { - result = EntitySpec.create(clazz); - } else { - // If this is a concrete class, particularly for an Application class, we want the proxy - // to expose all interfaces it implements. - Class interfaceclazz = (Application.class.isAssignableFrom(clazz)) ? Application.class : Entity.class; - Class[] additionalInterfaceClazzes = clazz.getInterfaces(); - result = EntitySpec.create(interfaceclazz).impl(clazz).additionalInterfaces(additionalInterfaceClazzes); - } - - if (!Strings.isEmpty(name)) result.displayName(name); - result.configure( convertFlagsToKeys(result.getImplementation(), config) ); - return result; - } - @SuppressWarnings("unchecked") - protected EntitySpec createApplicationFromNonCatalogCampTemplate(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader) { + protected EntitySpec createApplicationFromCampTemplate(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader) { // AssemblyTemplates created via PDP, _specifying_ then entities to put in BrooklynComponentTemplateResolver resolver = BrooklynComponentTemplateResolver.Factory.newInstance( @@ -403,5 +266,4 @@ protected void buildChildrenEntitySpecs(BrooklynClassLoadingContext loader, Enti } } } - } From 42baa6de609feebfc874b14de840645c5c66bb0b Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Tue, 8 Jul 2014 10:42:31 +0100 Subject: [PATCH 38/48] Fix recursive call (catalog type == java type) --- .../BrooklynAssemblyTemplateInstantiator.java | 29 +++- .../BrooklynComponentTemplateResolver.java | 8 +- .../resources/CatalogBundleResourceTest.java | 128 ++++++++++-------- 3 files changed, 106 insertions(+), 59 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index 47e84122c6..34244c271b 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -40,6 +40,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpecInstantiator { @@ -195,20 +196,36 @@ protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec> buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform) { + return buildTemplateServicesAsSpecsImpl(loader, template, platform, Sets.newLinkedHashSet()); + } + + private List> buildTemplateServicesAsSpecsImpl(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform, Set encounteredCatalogTypes) { List> result = Lists.newArrayList(); for (ResolvableLink ctl: template.getPlatformComponentTemplates().links()) { PlatformComponentTemplate appChildComponentTemplate = ctl.resolve(); BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(loader, appChildComponentTemplate); ManagementContext mgmt = loader.getManagementContext(); - + + String catalogIdOrJavaType = entityResolver.getCatalogIdOrJavaType(); + EntitySpec spec; CatalogItem> item = entityResolver.getCatalogItem(); + + // FIXME + log.warn("buildTemplateServicesAsSpecsImpl: catalogIdOrJavaType="+catalogIdOrJavaType+"; item="+item+"; loader="+loader+"; template="+template+"; encounteredCatalogTypes="+encounteredCatalogTypes); + + if (item == null || item.getJavaType() != null) { spec = entityResolver.resolveSpec(); } else { - spec = resolveCatalogYamlReferenceSpec(platform, mgmt, item); + boolean firstOccurrence = encounteredCatalogTypes.add(catalogIdOrJavaType); + if (firstOccurrence) { + spec = resolveCatalogYamlReferenceSpec(platform, mgmt, item, encounteredCatalogTypes); + } else { + throw new IllegalStateException("Recursive reference to " + catalogIdOrJavaType); + } } BrooklynClassLoadingContext newLoader = entityResolver.loader; @@ -221,7 +238,9 @@ private List> buildTemplateServicesAsSpecs(BrooklynClassLoadingCon private EntitySpec resolveCatalogYamlReferenceSpec(CampPlatform platform, ManagementContext mgmt, - CatalogItem> item) { + CatalogItem> item, + Set encounteredCatalogTypes) { + String yaml = item.getPlanYaml(); Reader input = new StringReader(yaml); @@ -239,9 +258,9 @@ private EntitySpec resolveCatalogYamlReferenceSpec(CampPlatform platform, try { AssemblyTemplateInstantiator ati = at.getInstantiator().newInstance(); if (ati instanceof BrooklynAssemblyTemplateInstantiator) { - List> specs = ((BrooklynAssemblyTemplateInstantiator)ati).buildTemplateServicesAsSpecs(itemLoader, at, platform); + List> specs = ((BrooklynAssemblyTemplateInstantiator)ati).buildTemplateServicesAsSpecsImpl(itemLoader, at, platform, encounteredCatalogTypes); if (specs.size() > 1) { - throw new UnsupportedOperationException("Only supporting single service in catalog item currently"); + throw new UnsupportedOperationException("Only supporting single service in catalog item currently: got "+specs); } return specs.get(0); } else { diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index 4180a1a313..07a127bd72 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -173,8 +173,14 @@ public boolean canResolve() { /** returns the entity class, if needed in contexts which scan its statics for example */ public Class loadEntityClass() { + return tryLoadEntityClass().get(); + } + + /** tries to load the Java entity class */ + public Maybe> tryLoadEntityClass() { CatalogItem> item = getCatalogItem(); String typeName = getCatalogIdOrJavaType(); + if (item!=null) { // add additional bundles loader = new BrooklynClassLoadingContextSequential(mgmt, item.newClassLoadingContext(mgmt), loader); @@ -196,7 +202,7 @@ public Class loadEntityClass() { } } - return loader.loadClass(typeName, Entity.class); + return loader.tryLoadClass(typeName, Entity.class); } /** resolves the spec, updating the loader if a catalog item is loaded */ diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index 2fc3102d01..9780d306f3 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -1,8 +1,10 @@ package brooklyn.rest.resources; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import java.io.InputStream; import java.net.URI; import javax.ws.rs.core.Response; @@ -15,6 +17,7 @@ import brooklyn.rest.domain.ApplicationSummary; import brooklyn.rest.domain.CatalogEntitySummary; import brooklyn.rest.testing.BrooklynRestResourceTest; +import brooklyn.util.stream.Streams; import com.google.common.collect.Iterables; import com.sun.jersey.api.client.ClientResponse; @@ -37,29 +40,19 @@ public void testListApplicationYaml() throws Exception { @Test public void testLaunchApplicationYaml() throws Exception { String registeredTypeName = "my.catalog.app.id.launch"; - registerAndLaunch(registeredTypeName, SIMPLE_ENTITY_TYPE); + registerAndLaunchAndAssertSimpleEntity(registeredTypeName, SIMPLE_ENTITY_TYPE); } @Test - public void testLaunchApplicationLoopYaml() throws Exception { - String registeredTypeName = "my.catalog.app.id.launch"; - registerAndLaunch(registeredTypeName, registeredTypeName); - } + public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws Exception { + String referencedRegisteredTypeName = "my.catalog.app.id.referenced"; + String referrerRegisteredTypeName = "my.catalog.app.id.referring"; + addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE); + addCatalogOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName); - private void registerAndLaunch(String registeredTypeName, String catalogServiceType) { - addCatalogOSGiEntity(registeredTypeName, catalogServiceType); + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+referrerRegisteredTypeName+" } ] }"; + ApplicationSummary appSummary = createAndWaitForApp(yaml); - String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; - - ClientResponse response = client().resource("/v1/applications") - .entity(yaml, "application/x-yaml") - .post(ClientResponse.class); - assertTrue(response.getStatus()/100 == 2, "response is "+response); - - // Expect app to be running - URI appUri = response.getLocation(); - waitForApplicationToBeRunning(response.getLocation()); - ApplicationSummary appSummary = client().resource(appUri).get(ApplicationSummary.class); String appId = appSummary.getId(); assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); @@ -69,23 +62,23 @@ private void registerAndLaunch(String registeredTypeName, String catalogServiceT } @Test - public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws Exception { - String referencedRegisteredTypeName = "my.catalog.app.id.referenced"; - String referrerRegisteredTypeName = "my.catalog.app.id.referring"; - addCatalogOSGiEntity(referencedRegisteredTypeName); - addCatalogEntityReferencingCatalogEntry(referrerRegisteredTypeName, referencedRegisteredTypeName); + public void testLaunchApplicationLoopWithJavaTypeNameInYamlFails() throws Exception { + String registeredTypeName = SIMPLE_ENTITY_TYPE; + registerAndLaunchFailsWithRecursionError(registeredTypeName, registeredTypeName); + } + + @Test + public void testLaunchApplicationLoopCatalogIdInYamlFails() throws Exception { + String registeredTypeName = "self.referencing.type"; + registerAndLaunchFailsWithRecursionError(registeredTypeName, registeredTypeName); + } + + private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) { + addCatalogOSGiEntity(registeredTypeName, serviceType); + + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; + ApplicationSummary appSummary = createAndWaitForApp(yaml); - String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+referrerRegisteredTypeName+" } ] }"; - - ClientResponse response = client().resource("/v1/applications") - .entity(yaml, "application/x-yaml") - .post(ClientResponse.class); - assertTrue(response.getStatus()/100 == 2, "response is "+response); - - // Expect app to be running - URI appUri = response.getLocation(); - waitForApplicationToBeRunning(response.getLocation()); - ApplicationSummary appSummary = client().resource(appUri).get(ApplicationSummary.class); String appId = appSummary.getId(); assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); @@ -94,12 +87,41 @@ public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE); } + private void registerAndLaunchFailsWithRecursionError(String registeredTypeName, String serviceType) { + addCatalogOSGiEntity(registeredTypeName, serviceType); + try { + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; + ClientResponse response = client().resource("/v1/applications") + .entity(yaml, "application/x-yaml") + .post(ClientResponse.class); + + int responseStatus = response.getStatus(); + String responseContent = getResponseContentAsString(response); + + assertFalse(responseStatus/100 == 2, "response="+response+"; content="+responseContent); + assertTrue(responseContent.contains("Recursive reference to "+registeredTypeName), "content="+responseContent); + } finally { + deleteCatalogEntity(registeredTypeName); + } + } + + private ApplicationSummary createAndWaitForApp(String yaml) { + ClientResponse response = client().resource("/v1/applications") + .entity(yaml, "application/x-yaml") + .post(ClientResponse.class); + assertTrue(response.getStatus()/100 == 2, "response is "+response); + + URI appUri = response.getLocation(); + waitForApplicationToBeRunning(response.getLocation()); + + return client().resource(appUri).get(ApplicationSummary.class); + } private void addCatalogOSGiEntity(String registeredTypeName) { addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE); } - private void addCatalogOSGiEntity(String registeredTypeName, String catalogServiceType) { + private void addCatalogOSGiEntity(String registeredTypeName, String serviceType) { String catalogYaml = "name: "+registeredTypeName+"\n"+ // FIXME name above should be unnecessary when brooklyn.catalog below is working @@ -113,24 +135,7 @@ private void addCatalogOSGiEntity(String registeredTypeName, String catalogServi " - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL + "\n"+ "\n"+ "services:\n"+ - "- type: " + catalogServiceType; - - addCatalogEntity(catalogYaml); - } - - private void addCatalogEntityReferencingCatalogEntry(String ownRegisteredTypeName, String otherRegisteredTypeName) { - String catalogYaml = - "name: "+ownRegisteredTypeName+"\n"+ - // FIXME name above should be unnecessary when brooklyn.catalog below is working - "brooklyn.catalog:\n"+ - " id: " + ownRegisteredTypeName + "\n"+ - " name: My Referrer Catalog App\n"+ - " description: My referrer description\n"+ - " icon_url: classpath://path/to/myicon.jpg\n"+ - " version: 0.2.1\n"+ - "\n"+ - "services:\n"+ - "- type: "+otherRegisteredTypeName+"\n"; + "- type: " + serviceType; addCatalogEntity(catalogYaml); } @@ -141,4 +146,21 @@ private void addCatalogEntity(String catalogYaml) { assertEquals(catalogResponse.getStatus(), Response.Status.CREATED.getStatusCode()); } + + private void deleteCatalogEntity(String catalogItem) { + ClientResponse catalogResponse = client().resource("/v1/catalog/entities/"+catalogItem) + .delete(ClientResponse.class); + + assertEquals(catalogResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + } + + private String getResponseContentAsString(ClientResponse response) { + InputStream in = null; + try { + in = response.getEntityInputStream(); + return new String(Streams.readFully(in)); + } finally { + Streams.closeQuietly(in); + } + } } From ab5386b91a528fe4487d3e405359d1586ec20420 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Tue, 8 Jul 2014 12:14:18 +0100 Subject: [PATCH 39/48] Adds catalog.deleteCatalogItem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - needed for CatalogBundleResourceTest, so that tests don’t interfere with each other. --- .../brooklyn/catalog/BrooklynCatalog.java | 5 ++++ .../internal/BasicBrooklynCatalog.java | 16 +++++++++++ .../brooklyn/catalog/internal/CatalogDo.java | 16 ++++++++++- .../java/brooklyn/rest/api/CatalogApi.java | 10 +++++++ .../rest/resources/CatalogResource.java | 9 +++++++ .../rest/resources/CatalogResourceTest.java | 27 +++++++++++++++++++ 6 files changed, 82 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java index 38583cb8f6..e970b2b6ba 100644 --- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java +++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java @@ -1,5 +1,7 @@ package brooklyn.catalog; +import java.util.NoSuchElementException; + import com.google.common.base.Predicate; public interface BrooklynCatalog { @@ -7,6 +9,9 @@ public interface BrooklynCatalog { /** @return The item with the given ID, or null if not found */ CatalogItem getCatalogItem(String id); + /** @return Deletes the item with the given ID + * @throws NoSuchElementException if not found */ + void deleteCatalogItem(String id); /** variant of {@link #getCatalogItem(String)} which checks (and casts) type for convenience * (returns null if type does not match) */ diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index b77ee6a414..d82794f78c 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -99,6 +99,22 @@ public CatalogItem getCatalogItem(String id) { return itemDo.getDto(); } + @Override + public void deleteCatalogItem(String id) { + log.debug("Deleting manual catalog item from "+mgmt+": "+id); + Preconditions.checkNotNull(id, "id"); + CatalogItem item = getCatalogItem(id); + CatalogItemDtoAbstract itemDto = getAbstractCatalogItem(item); + if (itemDto == null) { + throw new NoSuchElementException("No catalog item found with id "+id); + } + if (manualAdditionsCatalog==null) loadManualAdditionsCatalog(); + manualAdditionsCatalog.deleteEntry(itemDto); + + // Ensure the cache is de-populated + getCatalog().removeEntry(itemDto); + } + @SuppressWarnings("unchecked") @Override public CatalogItem getCatalogItem(Class type, String id) { diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java index 9ae2f623a9..e9b708cc04 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java @@ -190,6 +190,21 @@ public synchronized void addEntry(CatalogItemDtoAbstract entry) { cache.put(entry.getId(), new CatalogItemDo(this, entry)); } + public synchronized void deleteEntry(CatalogItemDtoAbstract entry) { + if (dto.entries != null) + dto.entries.remove(entry); + if (cache!=null) + cache.remove(entry.getId()); + } + + /** removes the given entry from the catalog; + */ + public synchronized void removeEntry(CatalogItemDtoAbstract entry) { + dto.entries.remove(entry); + if (cache!=null) + cache.remove(entry.getId()); + } + /** returns loaded catalog, if this has been loaded */ CatalogDo addCatalog(CatalogDto child) { if (dto.catalogs==null) @@ -283,5 +298,4 @@ public ClassLoader getRootClassLoader() { public BrooklynClassLoadingContext newClassLoadingContext() { return new JavaBrooklynClassLoadingContext(mgmt, getRootClassLoader()); } - } diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java index 8e0d2136a1..05e75bc815 100644 --- a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java +++ b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java @@ -40,6 +40,16 @@ public Response create( @Valid String yaml ) ; + @DELETE + @Path("/entities/{entityId}") + @ApiOperation(value = "Deletes an entity's definition from the catalog") + @ApiErrors(value = { + @ApiError(code = 404, reason = "Entity not found") + }) + public void deleteEntity( + @ApiParam(name = "entityId", value = "The ID of the entity or template to delete", required = true) + @PathParam("entityId") String entityId) throws Exception ; + @GET @Path("/entities") @ApiOperation(value = "List available entity types optionally matching a query", responseClass = "CatalogItemSummary", multiValueResponse = true) diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java index e0df7e3387..43693c1005 100644 --- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java +++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java @@ -80,6 +80,15 @@ public Response create(String yaml) { } } + @Override + public void deleteEntity(String entityId) throws Exception { + CatalogItem result = brooklyn().getCatalog().getCatalogItem(entityId); + if (result==null) { + throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId); + } + brooklyn().getCatalog().deleteCatalogItem(entityId); + } + @Override public List listEntities( final String regex, diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index 708915c1c7..c02c0efc8b 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -170,4 +170,31 @@ public void testListPolicies() { Assert.assertNotNull(asp, "didn't find AutoScalerPolicy"); } + @Test + public void testDeleteCustomEntityFromCatalog() { + String registeredTypeName = "my.catalog.app.id.to.subsequently.delete"; + String yaml = + "name: "+registeredTypeName+"\n"+ + // FIXME name above should be unnecessary when brooklyn.catalog below is working + "brooklyn.catalog:\n"+ + " id: " + registeredTypeName + "\n"+ + " name: My Catalog App To Be Deleted\n"+ + " description: My description\n"+ + " version: 0.1.2\n"+ + "\n"+ + "services:\n"+ + "- type: brooklyn.test.entity.TestEntity\n"; + + client().resource("/v1/catalog") + .post(ClientResponse.class, yaml); + + ClientResponse deleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName) + .delete(ClientResponse.class); + + assertEquals(deleteResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode()); + + ClientResponse getPostDeleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName) + .get(ClientResponse.class); + assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode()); + } } From 5c01f001dbde8f9ebf5e2037ab1f93560268f017 Mon Sep 17 00:00:00 2001 From: Aled Sage Date: Tue, 8 Jul 2014 12:29:21 +0100 Subject: [PATCH 40/48] =?UTF-8?q?CAMP:=20handle=20serviceType=20=E2=80=9Cj?= =?UTF-8?q?ava:=E2=80=9D=20prefix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BrooklynAssemblyTemplateInstantiator.java | 10 ++----- .../BrooklynComponentTemplateResolver.java | 6 ++++ .../resources/CatalogBundleResourceTest.java | 30 ++++++++++++------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index 34244c271b..d898ecc1f9 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -208,16 +208,12 @@ private List> buildTemplateServicesAsSpecsImpl(BrooklynClassLoadin ManagementContext mgmt = loader.getManagementContext(); String catalogIdOrJavaType = entityResolver.getCatalogIdOrJavaType(); - - EntitySpec spec; - CatalogItem> item = entityResolver.getCatalogItem(); - // FIXME - log.warn("buildTemplateServicesAsSpecsImpl: catalogIdOrJavaType="+catalogIdOrJavaType+"; item="+item+"; loader="+loader+"; template="+template+"; encounteredCatalogTypes="+encounteredCatalogTypes); + if (log.isTraceEnabled()) log.trace("Building CAMP template services: type="+catalogIdOrJavaType+"; item="+item+"; loader="+loader+"; template="+template+"; encounteredCatalogTypes="+encounteredCatalogTypes); - - if (item == null || item.getJavaType() != null) { + EntitySpec spec; + if (item == null || item.getJavaType() != null || entityResolver.isJavaTypePrefix()) { spec = entityResolver.resolveSpec(); } else { boolean firstOccurrence = encounteredCatalogTypes.add(catalogIdOrJavaType); diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index 07a127bd72..078fd04abf 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -134,6 +134,12 @@ protected String getDeclaredType() { return Factory.getDeclaredType(null, template.orNull(), attrs); } + // TODO Generalise to have other prefixes (e.g. explicit "catalog:" etc)? + protected boolean isJavaTypePrefix() { + String type = getDeclaredType(); + return type != null && (type.toLowerCase().startsWith("java:") || type.toLowerCase().startsWith("brooklyn:java:")); + } + protected String getCatalogIdOrJavaType() { String type = getDeclaredType(); type = Strings.removeFromStart(type, "brooklyn:", "java:"); diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index 9780d306f3..f3a847708d 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -61,6 +61,13 @@ public void testLaunchApplicationWithCatalogReferencingOtherCatalogYaml() throws assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE); } + @Test + public void testLaunchApplicationWithTypeUsingJavaColonPrefixInYaml() throws Exception { + String registeredTypeName = SIMPLE_ENTITY_TYPE; + String serviceName = "java:"+SIMPLE_ENTITY_TYPE; + registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName); + } + @Test public void testLaunchApplicationLoopWithJavaTypeNameInYamlFails() throws Exception { String registeredTypeName = SIMPLE_ENTITY_TYPE; @@ -75,16 +82,19 @@ public void testLaunchApplicationLoopCatalogIdInYamlFails() throws Exception { private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) { addCatalogOSGiEntity(registeredTypeName, serviceType); - - String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; - ApplicationSummary appSummary = createAndWaitForApp(yaml); - - String appId = appSummary.getId(); - assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); - - Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); - Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); - assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE); + try { + String yaml = "{ name: simple-app-yaml, location: localhost, services: [ { serviceType: "+registeredTypeName+" } ] }"; + ApplicationSummary appSummary = createAndWaitForApp(yaml); + + String appId = appSummary.getId(); + assertEquals(appSummary.getSpec().getName(), "simple-app-yaml"); + + Application app = (Application) getManagementContext().getEntityManager().getEntity(appId); + Entity simpleEntity = Iterables.getOnlyElement(app.getChildren()); + assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE); + } finally { + deleteCatalogEntity(registeredTypeName); + } } private void registerAndLaunchFailsWithRecursionError(String registeredTypeName, String serviceType) { From 37c91c3dad9fdbd66a170fcbf1657e34d4c77bd0 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 15:06:12 +0300 Subject: [PATCH 41/48] Add the classloader as per Aled's suggestion. --- .../java/brooklyn/entity/proxying/InternalEntityFactory.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java index 1e953b6ed5..01a10e6c06 100644 --- a/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java +++ b/core/src/main/java/brooklyn/entity/proxying/InternalEntityFactory.java @@ -111,6 +111,7 @@ public InternalEntityFactory(ManagementContextInternal managementContext, Entity public T createEntityProxy(EntitySpec spec, T entity) { // TODO Don't want the proxy to have to implement EntityLocal, but required by how // AbstractEntity.parent is used (e.g. parent.getAllConfig) + ClassLoader classloader = (spec.getImplementation() != null ? spec.getImplementation() : spec.getType()).getClassLoader(); MutableSet.Builder> builder = MutableSet.>builder() .add(EntityProxy.class, Entity.class, EntityLocal.class, EntityInternal.class); if (spec.getType().isInterface()) { @@ -128,6 +129,7 @@ public T createEntityProxy(EntitySpec spec, T entity) { // Building our own aggregating class loader gets around this. // But we really should not have to do this! What are the consequences? AggregateClassLoader aggregateClassLoader = AggregateClassLoader.newInstanceWithNoLoaders(); + aggregateClassLoader.addFirst(classloader); aggregateClassLoader.addFirst(entity.getClass().getClassLoader()); for(Class iface : interfaces) { aggregateClassLoader.addLast(iface.getClassLoader()); From 09ae42aadf4fc7f723985d56fd31eb4b170cb381 Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Tue, 8 Jul 2014 15:38:42 +0300 Subject: [PATCH 42/48] Remove TODOs, already implemented in CatalogBundleResourceTest --- .../test/java/brooklyn/rest/resources/CatalogResourceTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index c02c0efc8b..9da64e59d1 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -108,9 +108,6 @@ public void testListAllEntities() { assertTrue(entities.size() > 0); } - // TODO test registering entity from the bundle using new registered name - // TODO test registering entity from the bundle using type of item and the registered name (check endless loop) - @Test public void testFilterListOfEntitiesByName() { List entities = client().resource("/v1/catalog/entities") From 7b42d9446285d246a0f1695febb294b38d1bc9ea Mon Sep 17 00:00:00 2001 From: Svetoslav Neykov Date: Wed, 9 Jul 2014 15:44:58 +0300 Subject: [PATCH 43/48] Don't tell BCTR explicitly what classes to use for the spec. The behaviour of the resolver should depend entirely on the passed AbstractResource and its attributes. --- .../BrooklynAssemblyTemplateInstantiator.java | 21 +++++++++++++++++-- .../BrooklynComponentTemplateResolver.java | 17 ++++++--------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java index d898ecc1f9..2e6d580b2c 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java @@ -1,16 +1,20 @@ package io.brooklyn.camp.brooklyn.spi.creation; import io.brooklyn.camp.CampPlatform; +import io.brooklyn.camp.spi.AbstractResource; import io.brooklyn.camp.spi.Assembly; import io.brooklyn.camp.spi.AssemblyTemplate; +import io.brooklyn.camp.spi.AssemblyTemplate.Builder; import io.brooklyn.camp.spi.PlatformComponentTemplate; import io.brooklyn.camp.spi.collection.ResolvableLink; import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator; +import io.brooklyn.camp.spi.pdp.AssemblyTemplateConstructor; import java.io.Reader; import java.io.StringReader; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import org.slf4j.Logger; @@ -141,8 +145,8 @@ protected EntitySpec createApplicationFromCampTemplate(As // AssemblyTemplates created via PDP, _specifying_ then entities to put in BrooklynComponentTemplateResolver resolver = BrooklynComponentTemplateResolver.Factory.newInstance( - loader, template); - EntitySpec app = resolver.resolveSpec(StartableApplication.class, BasicApplicationImpl.class); + loader, buildWrapperAppTemplate(template)); + EntitySpec app = resolver.resolveSpec(); // first build the children into an empty shell app List> childSpecs = buildTemplateServicesAsSpecs(loader, template, platform); @@ -162,6 +166,19 @@ protected EntitySpec createApplicationFromCampTemplate(As return app; } + private AssemblyTemplate buildWrapperAppTemplate(AssemblyTemplate template) { + Builder builder = AssemblyTemplate.builder(); + builder.type("brooklyn:" + BasicApplicationImpl.class.getName()); + builder.id(template.getId()); + builder.name(template.getName()); + for (Entry entry : template.getCustomAttributes().entrySet()) { + builder.customAttribute(entry.getKey(), entry.getValue()); + } + builder.instantiator(template.getInstantiator()); + AssemblyTemplate wrapTemplate = builder.build(); + return wrapTemplate; + } + protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec app) { Object leaveWrapped = template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY); if (leaveWrapped!=null) { diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java index 078fd04abf..5d2f5a79c4 100644 --- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java +++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java @@ -214,21 +214,16 @@ public Maybe> tryLoadEntityClass() { /** resolves the spec, updating the loader if a catalog item is loaded */ @SuppressWarnings("unchecked") public EntitySpec resolveSpec() { - // ensure loader is updated - getCatalogItem(); - - return (EntitySpec)resolveSpec(loadEntityClass(), null); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public EntitySpec resolveSpec(Class type, @Nullable Class optionalImpl) { if (alreadyBuilt.getAndSet(true)) throw new IllegalStateException("Spec can only be used once: "+this); + // ensure loader is updated + getCatalogItem(); + + Class type = (Class) loadEntityClass(); + EntitySpec spec; - if (optionalImpl != null) { - spec = EntitySpec.create(type).impl(optionalImpl); - } else if (type.isInterface()) { + if (type.isInterface()) { spec = EntitySpec.create(type); } else { // If this is a concrete class, particularly for an Application class, we want the proxy From d94bcb17d26db75edab3036361135f7467752f7b Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Wed, 9 Jul 2014 11:43:52 -0400 Subject: [PATCH 44/48] comments about the need for name on a template being added to catalog --- .../brooklyn/rest/resources/CatalogBundleResourceTest.java | 3 ++- .../test/java/brooklyn/rest/resources/CatalogResourceTest.java | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java index f3a847708d..ba6e3ea9de 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogBundleResourceTest.java @@ -134,7 +134,8 @@ private void addCatalogOSGiEntity(String registeredTypeName) { private void addCatalogOSGiEntity(String registeredTypeName, String serviceType) { String catalogYaml = "name: "+registeredTypeName+"\n"+ - // FIXME name above should be unnecessary when brooklyn.catalog below is working + // FIXME name above should be unnecessary -- slight problem somewhere currently + // as testListApplicationYaml fails without the line above "brooklyn.catalog:\n"+ " id: " + registeredTypeName + "\n"+ " name: My Catalog App\n"+ diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java index 9da64e59d1..f9297a42d2 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java @@ -53,8 +53,6 @@ public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBu String registeredTypeName = "my.catalog.app.id"; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String yaml = - "name: "+registeredTypeName+"\n"+ - // FIXME name above should be unnecessary when brooklyn.catalog below is working "brooklyn.catalog:\n"+ " id: " + registeredTypeName + "\n"+ " name: My Catalog App\n"+ From 167056156f0af08eec77fdb14728c198aa283c00 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Wed, 9 Jul 2014 11:44:16 -0400 Subject: [PATCH 45/48] clean up how catalogs are created, being clearer about URL vs contents, and support resetting a catalog from a given XML definition, with REST API endpoint. needed for a master brooklyn to push catalog changes to a client brooklyn. --- .../internal/BasicBrooklynCatalog.java | 14 ++-- .../brooklyn/catalog/internal/CatalogDo.java | 2 +- .../brooklyn/catalog/internal/CatalogDto.java | 62 +++++++++----- .../internal/AbstractManagementContext.java | 2 +- .../brooklyn/camp/lite/CampYamlLiteTest.java | 81 +++++++++++++------ .../java/brooklyn/rest/api/CatalogApi.java | 9 +++ .../rest/resources/CatalogResource.java | 7 ++ 7 files changed, 123 insertions(+), 54 deletions(-) diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index d82794f78c..a4c71e55e7 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -62,14 +62,10 @@ public static BrooklynClassLoadingContext getLoader() { } private final ManagementContext mgmt; - private final CatalogDo catalog; + private CatalogDo catalog; private volatile CatalogDo manualAdditionsCatalog; private volatile LoadedClassLoader manualAdditionsClasses; - public BasicBrooklynCatalog(ManagementContext mgmt, String catalogUrl) { - this(mgmt, CatalogDto.newDtoFromUrl(catalogUrl)); - } - public BasicBrooklynCatalog(final ManagementContext mgmt, final CatalogDto dto) { this.mgmt = Preconditions.checkNotNull(mgmt, "managementContext"); this.catalog = new CatalogDo(mgmt, dto); @@ -83,6 +79,14 @@ public boolean blockIfNotLoaded(Duration timeout) { } } + public void reset(CatalogDto dto) { + CatalogDo catalog = new CatalogDo(mgmt, dto); + log.debug("Resetting "+this+" catalog to "+dto); + catalog.load(mgmt, null); + log.debug("Reloaded catalog for "+this+", now switching"); + this.catalog = catalog; + } + public CatalogDo getCatalog() { return catalog; } diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java index e9b708cc04..101dd612d1 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java @@ -72,7 +72,7 @@ protected synchronized void loadThisCatalog(ManagementContext mgmt, CatalogDo pa log.warn("Catalog "+this+" being initialised with different mgmt "+mgmt+" when already managed by "+this.mgmt, new Throwable("source of reparented "+this)); this.parent = parent; this.mgmt = mgmt; - dto.populateFromUrl(); + dto.populate(); loadCatalogClasspath(); loadCatalogItems(); isLoaded = true; diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java index 655ed29d3a..5ecef5c969 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java @@ -1,7 +1,7 @@ package brooklyn.catalog.internal; import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.StringReader; import java.util.List; import org.slf4j.Logger; @@ -10,6 +10,7 @@ import brooklyn.util.ResourceUtils; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.exceptions.PropagatedRuntimeException; +import brooklyn.util.stream.Streams; import com.google.common.base.Objects; @@ -18,7 +19,11 @@ public class CatalogDto { private static final Logger LOG = LoggerFactory.getLogger(CatalogDto.class); String id; + /** e.g. url */ String url; + + String contents; + String contentsDescription; String name; String description; CatalogClasspathDto classpath; @@ -39,15 +44,22 @@ public static CatalogDto newDtoFromUrl(String url) { if (LOG.isDebugEnabled()) LOG.debug("Retrieving catalog from: {}", url); try { InputStream source = ResourceUtils.create().getResourceFromUrl(url); - CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new InputStreamReader(source)); - if (LOG.isDebugEnabled()) LOG.debug("Retrieved catalog from: {}", url); - return result; + String contents = Streams.readFullyString(source); + return newDtoFromXmlContents(contents, url); } catch (Throwable t) { Exceptions.propagateIfFatal(t); throw new PropagatedRuntimeException("Unable to retrieve catalog from " + url + ": " + t, t); } } + public static CatalogDto newDtoFromXmlContents(String xmlContents, String originDescription) { + CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new StringReader(xmlContents)); + result.contentsDescription = originDescription; + + if (LOG.isDebugEnabled()) LOG.debug("Retrieved catalog from: {}", originDescription); + return result; + } + public static CatalogDto newNamedInstance(String name, String description) { CatalogDto result = new CatalogDto(); result.name = name; @@ -57,9 +69,29 @@ public static CatalogDto newNamedInstance(String name, String description) { public static CatalogDto newLinkedInstance(String url) { CatalogDto result = new CatalogDto(); - result.url = url; + result.contentsDescription = url; + result.contents = ResourceUtils.create().getResourceAsString(url); return result; } + + void populate() { + if (contents==null) { + if (url != null) { + contents = ResourceUtils.create().getResourceAsString(url); + contentsDescription = url; + } else { + LOG.warn("Catalog DTO has no contents); ignoring call to populate it."); + return; + } + } + + CatalogDto remoteDto = newDtoFromXmlContents(contents, contentsDescription); + try { + copyFrom(remoteDto, true); + } catch (Exception e) { + Exceptions.propagate(e); + } + } /** * @throws NullPointerException If source is null (and !skipNulls) @@ -71,35 +103,21 @@ void copyFrom(CatalogDto source, boolean skipNulls) throws IllegalAccessExceptio } if (!skipNulls || source.id != null) id = source.id; - if (!skipNulls || source.url != null) url = source.url; + if (!skipNulls || source.contentsDescription != null) contentsDescription = source.contentsDescription; + if (!skipNulls || source.contents != null) contents = source.contents; if (!skipNulls || source.name != null) name = source.name; if (!skipNulls || source.description != null) description = source.description; if (!skipNulls || source.classpath != null) classpath = source.classpath; if (!skipNulls || source.entries != null) entries = source.entries; } - /** - * Populates this Dto by loading the catalog at its {@link #url}. Takes no action if url is null. - * Throws if there are any problems in retrieving or copying from url. - */ - void populateFromUrl() { - if (url != null) { - CatalogDto remoteDto = newDtoFromUrl(url); - try { - copyFrom(remoteDto, true); - } catch (Exception e) { - Exceptions.propagate(e); - } - } - } - @Override public String toString() { return Objects.toStringHelper(this) .omitNullValues() .add("name", name) .add("id", id) - .add("url", url) + .add("contentsDescription", contentsDescription) .toString(); } diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java index 8c1e579a18..c54c6d987a 100644 --- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java +++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java @@ -316,7 +316,7 @@ protected synchronized void loadCatalog() { try { if (!Strings.isEmpty(catalogUrl)) { - catalog = new BasicBrooklynCatalog(this, catalogUrl); + catalog = new BasicBrooklynCatalog(this, CatalogDto.newDtoFromUrl(catalogUrl)); if (log.isDebugEnabled()) log.debug("Loaded catalog from "+catalogUrl+": "+catalog); } diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java index 120ee9d576..61a7dd5ee3 100644 --- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java +++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java @@ -22,6 +22,8 @@ import brooklyn.catalog.CatalogItem; import brooklyn.catalog.CatalogPredicates; +import brooklyn.catalog.internal.BasicBrooklynCatalog; +import brooklyn.catalog.internal.CatalogDto; import brooklyn.entity.Entity; import brooklyn.entity.proxying.EntitySpec; import brooklyn.management.internal.LocalManagementContext; @@ -114,8 +116,15 @@ public void testYamlServiceForCatalog() { public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBundle() throws IOException { String registeredTypeName = "my.catalog.app.id"; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; - String yaml = - "brooklyn.catalog:\n"+ + String yaml = getSampleMyCatalogAppYaml(registeredTypeName, bundleUrl); + + mgmt.getCatalog().addItem(yaml); + + assertMgmtHasSampleMyCatalogApp(registeredTypeName, bundleUrl); + } + + private String getSampleMyCatalogAppYaml(String registeredTypeName, String bundleUrl) { + return "brooklyn.catalog:\n"+ " id: " + registeredTypeName + "\n"+ " name: My Catalog App\n"+ " description: My description\n"+ @@ -126,31 +135,53 @@ public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBu "\n"+ "services:\n"+ "- type: brooklyn.test.entity.TestEntity\n"; + } - mgmt.getCatalog().addItem(yaml); - - CatalogItem item = mgmt.getCatalog().getCatalogItem(registeredTypeName); - assertEquals(item.getRegisteredTypeName(), registeredTypeName); - - // stored as yaml, not java + private void assertMgmtHasSampleMyCatalogApp(String registeredTypeName, String bundleUrl) { + CatalogItem item = mgmt.getCatalog().getCatalogItem(registeredTypeName); + assertEquals(item.getRegisteredTypeName(), registeredTypeName); + + // stored as yaml, not java // assertEquals(entityItem.getJavaType(), "brooklyn.test.entity.TestEntity"); - Assert.assertNotNull(item.getPlanYaml()); - Assert.assertTrue(item.getPlanYaml().contains("brooklyn.test.entity.TestEntity")); - - assertEquals(item.getId(), registeredTypeName); - - // and let's check we have libraries - List libs = item.getLibraries().getBundles(); - assertEquals(libs, MutableList.of(bundleUrl)); - - // now let's check other things on the item - assertEquals(item.getName(), "My Catalog App"); - assertEquals(item.getDescription(), "My description"); - assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif"); - - // and confirm we can resolve ICON - byte[] iconData = Streams.readFully( ResourceUtils.create(item.newClassLoadingContext(mgmt)).getResourceFromUrl(item.getIconUrl()) ); - assertEquals(iconData.length, 43); + Assert.assertNotNull(item.getPlanYaml()); + Assert.assertTrue(item.getPlanYaml().contains("brooklyn.test.entity.TestEntity")); + + assertEquals(item.getId(), registeredTypeName); + + // and let's check we have libraries + List libs = item.getLibraries().getBundles(); + assertEquals(libs, MutableList.of(bundleUrl)); + + // now let's check other things on the item + assertEquals(item.getName(), "My Catalog App"); + assertEquals(item.getDescription(), "My description"); + assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif"); + + // and confirm we can resolve ICON + byte[] iconData = Streams.readFully( ResourceUtils.create(item.newClassLoadingContext(mgmt)).getResourceFromUrl(item.getIconUrl()) ); + assertEquals(iconData.length, 43); } + + @Test + public void testResetXmlWithCustomEntity() throws IOException { + String registeredTypeName = "my.catalog.app.id"; + String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; + String yaml = getSampleMyCatalogAppYaml(registeredTypeName, bundleUrl); + + LocalManagementContextForTests mgmt2 = new LocalManagementContextForTests(); + try { + CampPlatformWithJustBrooklynMgmt platform2 = new CampPlatformWithJustBrooklynMgmt(mgmt2); + MockWebPlatform.populate(platform2, TestAppAssemblyInstantiator.class); + + mgmt2.getCatalog().addItem(yaml); + String xml = ((BasicBrooklynCatalog)mgmt2.getCatalog()).toXmlString(); + ((BasicBrooklynCatalog)mgmt.getCatalog()).reset(CatalogDto.newDtoFromXmlContents(xml, "copy of temporary catalog")); + } finally { + mgmt2.terminate(); + } + + assertMgmtHasSampleMyCatalogApp(registeredTypeName, bundleUrl); + } + } diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java index 05e75bc815..f20ccdb776 100644 --- a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java +++ b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java @@ -40,6 +40,15 @@ public Response create( @Valid String yaml ) ; + @POST + @Consumes(MediaType.APPLICATION_XML) + @Path("/reset") + @ApiOperation(value = "Resets the catalog to the given (XML) format") + public Response resetXml( + @ApiParam(name = "xml", value = "XML descriptor of the entire catalog to install", required = true) + @Valid String xml + ) ; + @DELETE @Path("/entities/{entityId}") @ApiOperation(value = "Deletes an entity's definition from the catalog") diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java index 43693c1005..f3780a8df9 100644 --- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java +++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java @@ -17,6 +17,8 @@ import brooklyn.catalog.CatalogItem; import brooklyn.catalog.CatalogPredicates; +import brooklyn.catalog.internal.BasicBrooklynCatalog; +import brooklyn.catalog.internal.CatalogDto; import brooklyn.entity.Entity; import brooklyn.entity.proxying.EntitySpec; import brooklyn.rest.api.CatalogApi; @@ -80,6 +82,11 @@ public Response create(String yaml) { } } + public Response resetXml(String xml) { + ((BasicBrooklynCatalog)mgmt().getCatalog()).reset(CatalogDto.newDtoFromXmlContents(xml, "REST reset")); + return Response.ok().build(); + } + @Override public void deleteEntity(String entityId) throws Exception { CatalogItem result = brooklyn().getCatalog().getCatalogItem(entityId); From f46ab334cb481a6e1783cdc1694e11fbc52af0e6 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Wed, 9 Jul 2014 12:01:23 -0400 Subject: [PATCH 46/48] address @aledsage review comments from today, including fixing ResourceUtils constructor --- core/src/main/java/brooklyn/management/ha/OsgiManager.java | 4 ++-- core/src/main/java/brooklyn/util/ResourceUtils.java | 1 + core/src/test/resources/brooklyn/osgi/README.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java index d0447c8379..c67296947e 100644 --- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java +++ b/core/src/main/java/brooklyn/management/ha/OsgiManager.java @@ -117,7 +117,7 @@ public Maybe> tryResolveClass(String type, Iterable bundleU } else { bundleProblems.put(bundleUrlOrNameVersionString, new IllegalStateException("Unable to find bundle "+bundleUrlOrNameVersionString)); } - } catch (Throwable e) { + } catch (Exception e) { Exceptions.propagateIfFatal(e); bundleProblems.put(bundleUrlOrNameVersionString, e); } @@ -138,7 +138,7 @@ public URL getResource(String name, Iterable bundleUrlsOrNameVersionStri URL result = bundle.get().getResource(name); if (result!=null) return result; } - } catch (Throwable e) { + } catch (Exception e) { Exceptions.propagateIfFatal(e); } } diff --git a/core/src/main/java/brooklyn/util/ResourceUtils.java b/core/src/main/java/brooklyn/util/ResourceUtils.java index bdda6dfb80..347ecbbbbb 100644 --- a/core/src/main/java/brooklyn/util/ResourceUtils.java +++ b/core/src/main/java/brooklyn/util/ResourceUtils.java @@ -131,6 +131,7 @@ public static final ResourceUtils create() { } public ResourceUtils(ClassLoader loader, Object contextObject, String contextMessage) { + this(new JavaBrooklynClassLoadingContext(null, loader), contextObject, contextMessage); } public ResourceUtils(BrooklynClassLoadingContext loader, Object contextObject, String contextMessage) { diff --git a/core/src/test/resources/brooklyn/osgi/README.md b/core/src/test/resources/brooklyn/osgi/README.md index 5db7db2ff4..ad6e4d3861 100644 --- a/core/src/test/resources/brooklyn/osgi/README.md +++ b/core/src/test/resources/brooklyn/osgi/README.md @@ -1 +1 @@ -Sampe OSGi bundle files included here, either are built from /src/dependencies in this project, or contain their sources, or both. +Sample OSGi bundle files included here, either are built from /src/dependencies in this project, or contain their sources, or both. From ff3d299b287d35f216787eb1385479da7d1e469c Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Wed, 9 Jul 2014 12:10:31 -0400 Subject: [PATCH 47/48] delete the app added in new test, because other method assumes just one app --- .../rest/resources/ApplicationResourceTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java index a3c9a8fc50..1a030a9d6e 100644 --- a/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java +++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/ApplicationResourceTest.java @@ -42,6 +42,7 @@ import brooklyn.entity.Application; import brooklyn.entity.basic.BasicApplication; import brooklyn.entity.basic.BasicEntity; +import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityFunctions; import brooklyn.entity.basic.Lifecycle; import brooklyn.location.Location; @@ -219,6 +220,7 @@ public void testDeployApplicationYaml() throws Exception { assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); } + @SuppressWarnings("deprecation") @Test public void testReferenceCatalogEntity() throws Exception { getManagementContext().getCatalog().addItem(BasicEntity.class); @@ -234,6 +236,10 @@ public void testReferenceCatalogEntity() throws Exception { URI appUri = response.getLocation(); waitForApplicationToBeRunning(response.getLocation()); assertEquals(client().resource(appUri).get(ApplicationSummary.class).getSpec().getName(), "simple-app-yaml"); + + ClientResponse response2 = client().resource(appUri.getPath()) + .delete(ClientResponse.class); + assertEquals(response2.getStatus(), Response.Status.ACCEPTED.getStatusCode()); } @Test @@ -350,6 +356,12 @@ public void testFetchApplicationsAndEntity() { Collection groupMembers = (Collection) groupDetails.get("members"); Assert.assertNotNull(groupMembers); + + for (Application appi: getManagementContext().getApplications()) { + Entities.dumpInfo(appi); + } + log.info("MEMBERS: "+groupMembers); + Assert.assertEquals(groupMembers.size(), 3); // includes the app too?! Map entityMemberDetails = (Map) Iterables.find(groupMembers, withValueForKey("name", "simple-ent"), null); Map groupMemberDetails = (Map) Iterables.find(groupMembers, withValueForKey("name", "simple-group"), null); From 2a633052d78aeb4bf83959c8bcc5f0e98eb2a00d Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Wed, 9 Jul 2014 12:11:59 -0400 Subject: [PATCH 48/48] add @Override as per review comment --- .../src/main/java/brooklyn/rest/resources/CatalogResource.java | 1 + 1 file changed, 1 insertion(+) diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java index b6323a52de..91a49fe8c3 100644 --- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java +++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java @@ -100,6 +100,7 @@ public Response create(String yaml) { } } + @Override public Response resetXml(String xml) { ((BasicBrooklynCatalog)mgmt().getCatalog()).reset(CatalogDto.newDtoFromXmlContents(xml, "REST reset")); return Response.ok().build();