You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/21 03:54:34 UTC
incubator-brooklyn git commit: Illustration and test for
PlanToSpecTransformer which uses XML, and tidies encountered in the process
Repository: incubator-brooklyn
Updated Branches:
refs/heads/master ee9b59a74 -> 2a3603ba1
Illustration and test for PlanToSpecTransformer which uses XML, and tidies encountered in the process
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/2a3603ba
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/2a3603ba
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/2a3603ba
Branch: refs/heads/master
Commit: 2a3603ba19cc5551bfead2f04158e5f666431de7
Parents: ee9b59a
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Aug 20 21:31:35 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Aug 20 21:33:02 2015 +0100
----------------------------------------------------------------------
.../core/mgmt/EntityManagementUtils.java | 116 ++++++++++------
.../brooklyn/core/plan/PlanToSpecFactory.java | 93 +++++++++++--
.../core/plan/PlanToSpecTransformer.java | 32 +++--
.../core/plan/XmlPlanToSpecTransformer.java | 132 +++++++++++++++++++
.../core/plan/XmlPlanToSpecTransformerTest.java | 65 +++++++++
.../util/core/internal/TypeCoercionsTest.java | 7 +
.../api/AssemblyTemplateSpecInstantiator.java | 4 +-
.../BrooklynAssemblyTemplateInstantiator.java | 28 +---
.../spi/creation/CampToSpecTransformer.java | 10 +-
.../test/lite/TestAppAssemblyInstantiator.java | 3 +-
.../rest/resources/ApplicationResource.java | 4 +-
.../brooklyn/util/exceptions/Exceptions.java | 4 +-
12 files changed, 403 insertions(+), 95 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
index 1391dc4..b86a0a8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
@@ -18,8 +18,6 @@
*/
package org.apache.brooklyn.core.mgmt;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -40,9 +38,9 @@ import org.apache.brooklyn.core.effector.Effectors;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityFunctions;
import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.core.plan.PlanNotRecognizedException;
import org.apache.brooklyn.core.plan.PlanToSpecFactory;
import org.apache.brooklyn.core.plan.PlanToSpecTransformer;
+import org.apache.brooklyn.entity.stock.BasicApplication;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.task.TaskBuilder;
@@ -53,6 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -65,8 +64,13 @@ public class EntityManagementUtils {
private static final Logger log = LoggerFactory.getLogger(EntityManagementUtils.class);
/**
- * A marker config value which indicates that an application was created automatically
- * to allow the management of a non-app entity.
+ * A marker config value which indicates that an {@link Application} entity was created automatically,
+ * needed because a plan might give multiple top-level entities or a non-Application top-level entity,
+ * but brooklyn requires an {@link Application} at the root.
+ * <p>
+ * Typically when such a wrapper app wraps another {@link Application}
+ * (or when we are adding to an existing entity and it wraps multiple {@link Entity} instances)
+ * it will be unwrapped. See {@link #newWrapperApp()} and {@link #unwrapApplication(EntitySpec)}.
*/
public static final ConfigKey<Boolean> WRAPPER_APP_MARKER = ConfigKeys.newBooleanConfigKey("brooklyn.wrapper_app");
@@ -77,35 +81,29 @@ public class EntityManagementUtils {
return app;
}
- /** as {@link #createUnstarted(ManagementContext, EntitySpec)} but for a YAML spec */
- public static <T extends Application> T createUnstarted(ManagementContext mgmt, String yaml) {
- EntitySpec<T> spec = createEntitySpec(mgmt, yaml);
+ /** as {@link #createUnstarted(ManagementContext, EntitySpec)} but for a string plan (e.g. camp yaml) */
+ public static Application createUnstarted(ManagementContext mgmt, String plan) {
+ EntitySpec<? extends Application> spec = createEntitySpecForApplication(mgmt, plan);
return createUnstarted(mgmt, spec);
}
- public static <T extends Application> EntitySpec<T> createEntitySpec(ManagementContext mgmt, String yaml) {
- Collection<String> types = new ArrayList<String>();
- for (PlanToSpecTransformer c : PlanToSpecFactory.all(mgmt)) {
- try {
- return c.createApplicationSpec(yaml);
- } catch (PlanNotRecognizedException e) {
- types.add(c.getName());
+ public static EntitySpec<? extends Application> createEntitySpecForApplication(ManagementContext mgmt, final String plan) {
+ return PlanToSpecFactory.attemptWithLoaders(mgmt, new Function<PlanToSpecTransformer, EntitySpec<? extends Application>>() {
+ @Override
+ public EntitySpec<? extends Application> apply(PlanToSpecTransformer input) {
+ return input.createApplicationSpec(plan);
}
- }
- throw new PlanNotRecognizedException("Invalid plan, tried parsing with " + types);
+ }).get();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
- public static AbstractBrooklynObjectSpec<?, ?> createCatalogSpec(ManagementContext mgmt, CatalogItem<?, ?> item) {
- Collection<String> types = new ArrayList<String>();
- for (PlanToSpecTransformer c : PlanToSpecFactory.all(mgmt)) {
- try {
- return c.createCatalogSpec((CatalogItem)item);
- } catch (PlanNotRecognizedException e) {
- types.add(c.getName());
+ public static AbstractBrooklynObjectSpec<?, ?> createCatalogSpec(ManagementContext mgmt, final CatalogItem<?, ?> item) {
+ return PlanToSpecFactory.attemptWithLoaders(mgmt, new Function<PlanToSpecTransformer, AbstractBrooklynObjectSpec<?, ?>>() {
+ @Override
+ public AbstractBrooklynObjectSpec<?, ?> apply(PlanToSpecTransformer input) {
+ return input.createCatalogSpec((CatalogItem)item);
}
- }
- throw new PlanNotRecognizedException("Invalid plan, tried parsing with " + types);
+ }).get();
}
/** container for operation which creates something and which wants to return both
@@ -159,14 +157,14 @@ public class EntityManagementUtils {
ManagementContext mgmt = parent.getApplication().getManagementContext();
- EntitySpec<?> specA = createEntitySpec(mgmt, yaml);
+ EntitySpec<? extends Application> specA = createEntitySpecForApplication(mgmt, yaml);
// see whether we can promote children
List<EntitySpec<?>> specs = MutableList.of();
- if (canPromote(specA)) {
+ if (canPromoteChildrenInWrappedApplication(specA)) {
// we can promote
for (EntitySpec<?> specC: specA.getChildren()) {
- collapseSpec(specA, specC);
+ mergeWrapperParentSpecToChildEntity(specA, specC);
specs.add(specC);
}
} else {
@@ -225,26 +223,64 @@ public class EntityManagementUtils {
return CreationResult.of(children, task);
}
- /** worker method to combine specs */
+ /** if an application should be unwrapped, it does so, returning the child; otherwise returns the argument passed in.
+ * use {@link #canPromoteWrappedApplication(EntitySpec)} to test whether it will unwrap. */
+ public static EntitySpec<? extends Application> unwrapApplication(EntitySpec<? extends Application> wrapperApplication) {
+ if (canPromoteWrappedApplication(wrapperApplication)) {
+ @SuppressWarnings("unchecked")
+ EntitySpec<? extends Application> wrappedApplication = (EntitySpec<? extends Application>) Iterables.getOnlyElement( wrapperApplication.getChildren() );
+
+ // if promoted, apply the transformations done to the app
+ // (transformations will be done by the resolveSpec call above, but we are collapsing oldApp so transfer to app=newApp)
+ EntityManagementUtils.mergeWrapperParentSpecToChildEntity(wrapperApplication, wrappedApplication);
+ return wrappedApplication;
+ }
+ return wrapperApplication;
+ }
+
+ /** Modifies the child so it includes the inessential setup of its parent,
+ * for use when unwrapping specific children, but a name or other item may have been set on the parent.
+ * See {@link #WRAPPER_APP_MARKER}. */
@Beta //where should this live long-term?
- public static void collapseSpec(EntitySpec<?> sourceToBeCollapsed, EntitySpec<?> targetToBeExpanded) {
- if (Strings.isEmpty(targetToBeExpanded.getDisplayName()))
- targetToBeExpanded.displayName(sourceToBeCollapsed.getDisplayName());
- if (!sourceToBeCollapsed.getLocations().isEmpty())
- targetToBeExpanded.locations(sourceToBeCollapsed.getLocations());
+ public static void mergeWrapperParentSpecToChildEntity(EntitySpec<? extends Application> wrapperParent, EntitySpec<?> wrappedChild) {
+ if (Strings.isEmpty(wrappedChild.getDisplayName()))
+ wrappedChild.displayName(wrapperParent.getDisplayName());
+ if (!wrapperParent.getLocations().isEmpty())
+ wrappedChild.locations(wrapperParent.getLocations());
// NB: this clobbers child config; might prefer to deeply merge maps etc
// (but this should not be surprising, as unwrapping is often parameterising the nested blueprint, so outer config should dominate)
- Map<ConfigKey<?>, Object> configWithoutWrapperMarker = Maps.filterKeys(sourceToBeCollapsed.getConfig(), Predicates.not(Predicates.<ConfigKey<?>>equalTo(EntityManagementUtils.WRAPPER_APP_MARKER)));
- targetToBeExpanded.configure(configWithoutWrapperMarker);
- targetToBeExpanded.configure(sourceToBeCollapsed.getFlags());
+ Map<ConfigKey<?>, Object> configWithoutWrapperMarker = Maps.filterKeys(wrapperParent.getConfig(), Predicates.not(Predicates.<ConfigKey<?>>equalTo(EntityManagementUtils.WRAPPER_APP_MARKER)));
+ wrappedChild.configure(configWithoutWrapperMarker);
+ wrappedChild.configure(wrapperParent.getFlags());
// TODO copying tags to all entities is not ideal;
// in particular the BrooklynTags.YAML_SPEC tag will show all entities if the root has multiple
- targetToBeExpanded.tags(sourceToBeCollapsed.getTags());
+ wrappedChild.tags(wrapperParent.getTags());
+ }
+
+ public static EntitySpec<? extends Application> newWrapperApp() {
+ return EntitySpec.create(BasicApplication.class).configure(WRAPPER_APP_MARKER, true);
}
+
+ /** returns true if the spec is for an empty-ish wrapper app contianing an application,
+ * for use when adding from a plan specifying an application which was wrapped because it had to be.
+ * @see #WRAPPER_APP_MARKER */
+ public static boolean canPromoteWrappedApplication(EntitySpec<? extends Application> app) {
+ if (app.getChildren().size()!=1)
+ return false;
- public static boolean canPromote(EntitySpec<?> spec) {
+ EntitySpec<?> childSpec = Iterables.getOnlyElement(app.getChildren());
+ if (childSpec.getType()==null || !Application.class.isAssignableFrom(childSpec.getType()))
+ return false;
+
+ return canPromoteChildrenInWrappedApplication(app);
+ }
+
+ /** returns true if the spec is for an empty-ish wrapper app,
+ * for use when adding from a plan specifying multiple entities but nothing significant at the application level.
+ * @see #WRAPPER_APP_MARKER */
+ public static boolean canPromoteChildrenInWrappedApplication(EntitySpec<?> spec) {
return canPromoteBasedOnName(spec) &&
isWrapperApp(spec) &&
//equivalent to no keys starting with "brooklyn."
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java b/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
index 5d21420..6f74855 100644
--- a/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
@@ -18,29 +18,102 @@
*/
package org.apache.brooklyn.core.plan;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.ServiceLoader;
import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import com.google.common.collect.Lists;
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
public class PlanToSpecFactory {
- public static PlanToSpecTransformer forMime(ManagementContext mgmt, String mime) {
+
+ private static final Logger log = LoggerFactory.getLogger(PlanToSpecFactory.class);
+
+ private static Collection<PlanToSpecTransformer> getAll() {
+ return ImmutableList.copyOf(ServiceLoader.load(PlanToSpecTransformer.class));
+ }
+
+ private static Collection<Class<? extends PlanToSpecTransformer>> OVERRIDE;
+ @SafeVarargs
+ @VisibleForTesting
+ public synchronized static void forceAvailable(Class<? extends PlanToSpecTransformer> ...classes) {
+ OVERRIDE = Arrays.asList(classes);
+ }
+ public synchronized static void clearForced() {
+ OVERRIDE = null;
+ }
+
+ public static Collection<PlanToSpecTransformer> all(ManagementContext mgmt) {
+ Collection<Class<? extends PlanToSpecTransformer>> override = OVERRIDE;
+ Collection<PlanToSpecTransformer> result = new ArrayList<PlanToSpecTransformer>();
+ if (override!=null) {
+ for (Class<? extends PlanToSpecTransformer> o1: override) {
+ try {
+ result.add(o1.newInstance());
+ } catch (Exception e) {
+ Exceptions.propagate(e);
+ }
+ }
+ } else {
+ result.addAll(getAll());
+ }
+ for(PlanToSpecTransformer t : result) {
+ t.injectManagementContext(mgmt);
+ }
+ return result;
+ }
+
+ @Beta
+ public static PlanToSpecTransformer forPlanType(ManagementContext mgmt, String planType) {
Collection<PlanToSpecTransformer> transformers = all(mgmt);
for (PlanToSpecTransformer transformer : transformers) {
- if (transformer.accepts(mime)) {
+ if (transformer.accepts(planType)) {
return transformer;
}
}
- throw new IllegalStateException("PlanToSpecTransformer for type " + mime + " not found. Registered transformers are: " + transformers);
+ throw new IllegalStateException("PlanToSpecTransformer for plan type " + planType + " not found. Registered transformers are: " + transformers);
}
-
- public static Collection<PlanToSpecTransformer> all(ManagementContext mgmt) {
- Collection<PlanToSpecTransformer> transformers = Lists.newArrayList(ServiceLoader.load(PlanToSpecTransformer.class));
- for(PlanToSpecTransformer t : transformers) {
- t.injectManagementContext(mgmt);
+
+ // TODO primitive loading mechanism, just tries all in order; we'll want something better as we get more plan transformers
+ @Beta
+ public static <T> Maybe<T> attemptWithLoaders(ManagementContext mgmt, Function<PlanToSpecTransformer,T> f) {
+ return attemptWithLoaders(all(mgmt), f);
+ }
+
+ public static <T> Maybe<T> attemptWithLoaders(Iterable<PlanToSpecTransformer> transformers, Function<PlanToSpecTransformer,T> f) {
+ Collection<String> transformersWhoDontSupport = new ArrayList<String>();
+ Collection<Exception> otherProblemsFromTransformers = new ArrayList<Exception>();
+ for (PlanToSpecTransformer t: transformers) {
+ try {
+ return Maybe.of(f.apply(t));
+ } catch (PlanNotRecognizedException e) {
+ transformersWhoDontSupport.add(t.getShortDescription() +
+ (Strings.isNonBlank(e.getMessage()) ? " ("+e.getMessage()+")" : ""));
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ otherProblemsFromTransformers.add(new IllegalArgumentException("Transformer for "+t.getShortDescription()+" gave an error creating this plan", e));
+ }
+ }
+ // failed
+ Exception result;
+ if (!otherProblemsFromTransformers.isEmpty()) {
+ // at least one thought he could do it
+ log.debug("Plan could not be transformed; failure will be propagated (other transformers tried = "+transformersWhoDontSupport+"): "+otherProblemsFromTransformers);
+ result = Exceptions.create(null, otherProblemsFromTransformers);
+ } else {
+ result = new PlanNotRecognizedException("Invalid plan; format could not be recognized, trying with: "+transformersWhoDontSupport);
}
- return transformers;
+ return Maybe.absent(result);
}
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecTransformer.java b/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecTransformer.java
index 66f1b62..63d5c1f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecTransformer.java
@@ -28,22 +28,34 @@ import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
import com.google.common.annotations.Beta;
-/** Pluggable {@link ServiceLoader} interface for defferent plan-interpreters.
- * Implementations should take a plan and return an {@link EntitySpec}.
+/** Pluggable {@link ServiceLoader} interface for different plan-interpreters,
+ * that is, different ways of taking an application plan and returning an {@link EntitySpec},
+ * and a {@link CatalogItem} and returning an {@link AbstractBrooklynObjectSpec}.
*/
@Beta
public interface PlanToSpecTransformer extends ManagementContextInjectable {
- /** Human-readable name for this transformer */
- String getName();
+ /** A short, human-readable name for this transformer */
+ String getShortDescription();
- /** whether this accepts the given MIME format */
- boolean accepts(String mime);
+ /** whether this accepts the given plan type */
+ // TODO determine semantics of plan type; for now, we try all using PlanToSpecFactory methods,
+ // that's okay when there's just a very few, but we'll want something better if that grows
+ @Beta
+ boolean accepts(String planType);
- /** creates an {@link EntitySpec} given a plan, according to the transformation rules this understands */
- <T extends Application> EntitySpec<T> createApplicationSpec(String plan);
+ /** creates an {@link EntitySpec} given a complete plan textual description for a top-level application,
+ * according to the transformation rules this understands.
+ * <p>
+ * should throw {@link PlanNotRecognizedException} if not supported. */
+ EntitySpec<? extends Application> createApplicationSpec(String plan) throws PlanNotRecognizedException;
- /** creates an object spec given a catalog item, according to the transformation rules this understands */
- <T,SpecT extends AbstractBrooklynObjectSpec<T, SpecT>> AbstractBrooklynObjectSpec<T, SpecT> createCatalogSpec(CatalogItem<T, SpecT> item);
+ /** creates an object spec given a catalog item.
+ * <p>
+ * the catalog item might be known by type, or its source plan fragment text might be inspected and transformed.
+ * implementations will typically look at the {@link CatalogItem#getCatalogItemType()} first.
+ * <p>
+ * should throw {@link PlanNotRecognizedException} if this transformer does not know what to do with the plan. */
+ <T,SpecT extends AbstractBrooklynObjectSpec<T, SpecT>> AbstractBrooklynObjectSpec<T, SpecT> createCatalogSpec(CatalogItem<T, SpecT> item) throws PlanNotRecognizedException;
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformer.java b/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformer.java
new file mode 100644
index 0000000..5fc103d
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformer.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.plan;
+
+import java.io.StringReader;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.entity.stock.BasicEntity;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.stream.ReaderInputStream;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/** Example implementation of {@link PlanToSpecTransformer} showing
+ * how implementations are meant to be written. */
+public class XmlPlanToSpecTransformer implements PlanToSpecTransformer {
+
+ @SuppressWarnings("unused")
+ private ManagementContext mgmt;
+
+ @Override
+ public void injectManagementContext(ManagementContext managementContext) {
+ mgmt = managementContext;
+ }
+
+ @Override
+ public String getShortDescription() {
+ return "Dummy app structure created from the XML tree";
+ }
+
+ @Override
+ public boolean accepts(String mime) {
+ if ("test-xml".equals(mime)) return true;
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public EntitySpec<? extends Application> createApplicationSpec(String plan) {
+ Document dom = parseXml(plan);
+ EntitySpec<?> result = toEntitySpec(dom, 0);
+ if (Application.class.isAssignableFrom(result.getType())) {
+ return (EntitySpec<Application>) result;
+ } else {
+ return EntityManagementUtils.newWrapperApp().child(result);
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public <T, SpecT extends AbstractBrooklynObjectSpec<T, SpecT>> AbstractBrooklynObjectSpec<T, SpecT> createCatalogSpec(CatalogItem<T, SpecT> item) {
+ if (item.getPlanYaml()==null) throw new PlanNotRecognizedException("Plan is null");
+ if (item.getCatalogItemType()==CatalogItemType.ENTITY) {
+ return (EntitySpec)toEntitySpec(parseXml(item.getPlanYaml()), 1);
+ }
+ if (item.getCatalogItemType()==CatalogItemType.TEMPLATE) {
+ return (EntitySpec)toEntitySpec(parseXml(item.getPlanYaml()), 0);
+ }
+ throw new PlanNotRecognizedException("Type "+item.getCatalogItemType()+" not supported");
+ }
+
+ private Document parseXml(String plan) {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ Document dom;
+
+ try {
+ //Using factory get an instance of document builder
+ DocumentBuilder db = dbf.newDocumentBuilder();
+
+ //parse using builder to get DOM representation of the XML file
+ dom = db.parse(new ReaderInputStream(new StringReader(plan)));
+
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ throw new PlanNotRecognizedException(e);
+ }
+ return dom;
+ }
+
+ private EntitySpec<?> toEntitySpec(Node dom, int depth) {
+ if (dom.getNodeType()==Node.DOCUMENT_NODE) {
+ if (dom.getChildNodes().getLength()!=1) {
+ // NB: <?...?> entity preamble might break this
+ throw new IllegalStateException("Document for "+dom+" has "+dom.getChildNodes().getLength()+" nodes; 1 expected.");
+ }
+ return toEntitySpec(dom.getChildNodes().item(0), depth);
+ }
+
+ EntitySpec<?> result = depth == 0 ? EntitySpec.create(BasicApplication.class) : EntitySpec.create(BasicEntity.class);
+ result.displayName(dom.getNodeName());
+ if (dom.getAttributes()!=null) {
+ for (int i=0; i<dom.getAttributes().getLength(); i++)
+ result.configure(dom.getAttributes().item(i).getNodeName(), dom.getAttributes().item(i).getTextContent());
+ }
+ if (dom.getChildNodes()!=null) {
+ for (int i=0; i<dom.getChildNodes().getLength(); i++) {
+ Node item = dom.getChildNodes().item(i);
+ if (item.getNodeType()==Node.ELEMENT_NODE) {
+ result.child(toEntitySpec(item, depth+1));
+ }
+ }
+ }
+ return result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformerTest.java b/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformerTest.java
new file mode 100644
index 0000000..d6528df
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/plan/XmlPlanToSpecTransformerTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.plan;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.python.google.common.collect.Iterables;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/** Tests the sample {@link XmlPlanToSpecTransformer}
+ * which illustrates how the {@link PlanToSpecTransformer} can be used. */
+public class XmlPlanToSpecTransformerTest {
+
+ private ManagementContext mgmt;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ PlanToSpecFactory.forceAvailable(XmlPlanToSpecTransformer.class);
+ mgmt = LocalManagementContextForTests.newInstance();
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDown() {
+ PlanToSpecFactory.clearForced();
+ if (mgmt!=null) Entities.destroyAll(mgmt);
+ }
+
+ @Test
+ public void testSimpleXmlPlanParse() {
+ EntitySpec<? extends Application> appSpec = EntityManagementUtils.createEntitySpecForApplication(mgmt,
+ "<root><a_kid foo=\"bar\"/></root>");
+ Application app = EntityManagementUtils.createStarting(mgmt, appSpec).get();
+ Entities.dumpInfo(app);
+ Assert.assertEquals(app.getDisplayName(), "root");
+ Entity child = Iterables.getOnlyElement(app.getChildren());
+ Assert.assertEquals(child.getDisplayName(), "a_kid");
+ Assert.assertEquals(child.config().get(ConfigKeys.newStringConfigKey("foo")), "bar");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java b/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
index 8ce718b..ec89ebf 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java
@@ -19,6 +19,7 @@
package org.apache.brooklyn.util.core.internal;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -67,6 +68,7 @@ public class TypeCoercionsTest {
assertEquals(TypeCoercions.coerce("true", Boolean.class), (Boolean)true);
assertEquals(TypeCoercions.coerce("False", Boolean.class), (Boolean)false);
assertEquals(TypeCoercions.coerce("true ", Boolean.class), (Boolean)true);
+ assertNull(TypeCoercions.coerce(null, Boolean.class), null);
assertEquals(TypeCoercions.coerce("1", char.class), (Character)'1');
assertEquals(TypeCoercions.coerce("1", short.class), (Short)((short)1));
@@ -209,30 +211,35 @@ public class TypeCoercionsTest {
@Test
public void testListEntryCoercion() {
+ @SuppressWarnings("serial")
List<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<List<Class<?>>>() { });
Assert.assertEquals(s, ImmutableList.of(Integer.class, Double.class));
}
@Test
public void testListEntryToSetCoercion() {
+ @SuppressWarnings("serial")
Set<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<Set<Class<?>>>() { });
Assert.assertEquals(s, ImmutableSet.of(Integer.class, Double.class));
}
@Test
public void testListEntryToCollectionCoercion() {
+ @SuppressWarnings("serial")
Collection<?> s = TypeCoercions.coerce(ImmutableList.of("java.lang.Integer", "java.lang.Double"), new TypeToken<Collection<Class<?>>>() { });
Assert.assertEquals(s, ImmutableList.of(Integer.class, Double.class));
}
@Test
public void testMapValueCoercion() {
+ @SuppressWarnings("serial")
Map<?,?> s = TypeCoercions.coerce(ImmutableMap.of("int", "java.lang.Integer", "double", "java.lang.Double"), new TypeToken<Map<String, Class<?>>>() { });
Assert.assertEquals(s, ImmutableMap.of("int", Integer.class, "double", Double.class));
}
@Test
public void testMapKeyCoercion() {
+ @SuppressWarnings("serial")
Map<?,?> s = TypeCoercions.coerce(ImmutableMap.of("java.lang.Integer", "int", "java.lang.Double", "double"), new TypeToken<Map<Class<?>, String>>() { });
Assert.assertEquals(s, ImmutableMap.of(Integer.class, "int", Double.class, "double"));
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/api/AssemblyTemplateSpecInstantiator.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/api/AssemblyTemplateSpecInstantiator.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/api/AssemblyTemplateSpecInstantiator.java
index 88417e3..df7227b 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/api/AssemblyTemplateSpecInstantiator.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/api/AssemblyTemplateSpecInstantiator.java
@@ -20,6 +20,7 @@ package org.apache.brooklyn.camp.brooklyn.api;
import java.util.Set;
+import org.apache.brooklyn.api.entity.Application;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.camp.CampPlatform;
import org.apache.brooklyn.camp.spi.AssemblyTemplate;
@@ -28,8 +29,7 @@ import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
public interface AssemblyTemplateSpecInstantiator extends AssemblyTemplateInstantiator {
- EntitySpec<?> createSpec(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader, boolean autoUnwrapIfAppropriate);
+ EntitySpec<? extends Application> createSpec(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader, boolean autoUnwrapIfAppropriate);
EntitySpec<?> createNestedSpec(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext itemLoader, Set<String> encounteredCatalogTypes);
-
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
index 63fa664..d582c45 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
@@ -41,11 +41,11 @@ import org.apache.brooklyn.camp.spi.PlatformComponentTemplate;
import org.apache.brooklyn.camp.spi.collection.ResolvableLink;
import org.apache.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
-import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
-import org.apache.brooklyn.core.mgmt.HasBrooklynManagementContext;
import org.apache.brooklyn.core.mgmt.EntityManagementUtils.CreationResult;
+import org.apache.brooklyn.core.mgmt.HasBrooklynManagementContext;
import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
import org.apache.brooklyn.entity.stock.BasicApplicationImpl;
@@ -56,7 +56,6 @@ import org.apache.brooklyn.util.net.Urls;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -88,7 +87,6 @@ public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpe
return ((HasBrooklynManagementContext)platform).getBrooklynManagementContext();
}
- @SuppressWarnings("unchecked")
public EntitySpec<? extends Application> createSpec(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader, boolean autoUnwrapIfPossible) {
log.debug("CAMP creating application instance for {} ({})", template.getId(), template);
@@ -106,12 +104,7 @@ public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpe
}
if (autoUnwrapIfPossible && shouldUnwrap(template, app)) {
- EntitySpec<? extends Application> oldApp = app;
- app = (EntitySpec<? extends Application>) Iterables.getOnlyElement( app.getChildren() );
-
- // if promoted, apply the transformations done to the app
- // (transformations will be done by the resolveSpec call above, but we are collapsing oldApp so transfer to app=newApp)
- EntityManagementUtils.collapseSpec(oldApp, app);
+ app = EntityManagementUtils.unwrapApplication(app);
}
return app;
@@ -132,20 +125,9 @@ public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpe
}
protected boolean shouldUnwrap(AssemblyTemplate template, EntitySpec<? extends Application> app) {
- Object leaveWrapped = template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY);
- if (leaveWrapped!=null) {
- if (TypeCoercions.coerce(leaveWrapped, Boolean.class))
- return false;
- }
-
- if (app.getChildren().size()!=1)
+ if (Boolean.TRUE.equals(TypeCoercions.coerce(template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY), Boolean.class)))
return false;
-
- EntitySpec<?> childSpec = Iterables.getOnlyElement(app.getChildren());
- if (childSpec.getType()==null || !Application.class.isAssignableFrom(childSpec.getType()))
- return false;
-
- return EntityManagementUtils.canPromote(app);
+ return EntityManagementUtils.canPromoteWrappedApplication(app);
}
private List<EntitySpec<?>> buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampToSpecTransformer.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampToSpecTransformer.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampToSpecTransformer.java
index ebafa76..7ab200d 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampToSpecTransformer.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampToSpecTransformer.java
@@ -41,8 +41,8 @@ public class CampToSpecTransformer implements PlanToSpecTransformer {
private ManagementContext mgmt;
@Override
- public String getName() {
- return YAML_CAMP_PLAN_TYPE;
+ public String getShortDescription() {
+ return "Brooklyn OASIS CAMP interpreter";
}
@Override
@@ -51,7 +51,7 @@ public class CampToSpecTransformer implements PlanToSpecTransformer {
}
@Override
- public <T extends Application> EntitySpec<T> createApplicationSpec(String plan) {
+ public EntitySpec<? extends Application> createApplicationSpec(String plan) {
CampPlatform camp = CampCatalogUtils.getCampPlatform(mgmt);
AssemblyTemplate at = camp.pdp().registerDeploymentPlan( new StringReader(plan) );
AssemblyTemplateInstantiator instantiator;
@@ -62,9 +62,7 @@ public class CampToSpecTransformer implements PlanToSpecTransformer {
}
if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.create(mgmt);
- @SuppressWarnings("unchecked")
- EntitySpec<T> createSpec = (EntitySpec<T>) ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, true);
- return createSpec;
+ return ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, true);
} else {
// The unknown instantiator can create the app (Assembly), but not a spec.
// Currently, all brooklyn plans should produce the above.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/TestAppAssemblyInstantiator.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/TestAppAssemblyInstantiator.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/TestAppAssemblyInstantiator.java
index dc0c33d..831805f 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/TestAppAssemblyInstantiator.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/TestAppAssemblyInstantiator.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.camp.brooklyn.test.lite;
import java.util.Map;
import java.util.Set;
+import org.apache.brooklyn.api.entity.Application;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.camp.CampPlatform;
@@ -58,7 +59,7 @@ public class TestAppAssemblyInstantiator extends BasicAssemblyTemplateInstantiat
}
@Override
- public EntitySpec<?> createSpec(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader, boolean autoUnwrap) {
+ public EntitySpec<? extends Application> createSpec(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader, boolean autoUnwrap) {
EntitySpec<TestApplication> app = EntitySpec.create(TestApplication.class)
.configure(TestEntity.CONF_NAME, template.getName())
.configure(TestEntity.CONF_MAP_THING, MutableMap.of("type", template.getType(), "desc", template.getDescription()));
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
index 5bc8df9..25e80e0 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
@@ -272,7 +272,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
}
log.debug("Creating app from yaml:\n{}", yaml);
- EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpec(mgmt(), yaml);
+ EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(), yaml);
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, spec)) {
throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s",
@@ -330,7 +330,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
//TODO infer encoding from request
String potentialYaml = new String(inputToAutodetectType);
- EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpec(mgmt(), potentialYaml);
+ EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(), potentialYaml);
// TODO not json - try ZIP, etc
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2a3603ba/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java b/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
index 347a212..583104b 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
@@ -29,6 +29,8 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import javax.annotation.Nullable;
+
import org.apache.brooklyn.util.text.Strings;
import com.google.common.base.Predicate;
@@ -267,7 +269,7 @@ public class Exceptions {
return create(null, exceptions);
}
/** creates the given exception, but without propagating it, for use when caller will be wrapping */
- public static RuntimeException create(String prefix, Collection<? extends Throwable> exceptions) {
+ public static RuntimeException create(@Nullable String prefix, Collection<? extends Throwable> exceptions) {
if (exceptions.size()==1) {
Throwable e = exceptions.iterator().next();
if (Strings.isBlank(prefix)) return new PropagatedRuntimeException(e);