Repository: incubator-brooklyn Updated Branches: refs/heads/master 7c649d73d -> 3e40b2bfd
Start to flesh out how types get created. also move inner classes to top level Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/817e9bd2 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/817e9bd2 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/817e9bd2 Branch: refs/heads/master Commit: 817e9bd278a2bf9e13341c67e9bc130dc897f889 Parents: 2aa01c0 Author: Alex Heneveld <[email protected]> Authored: Mon Nov 2 16:06:44 2015 +0000 Committer: Alex Heneveld <[email protected]> Committed: Tue Nov 10 17:13:01 2015 +0000 ---------------------------------------------------------------------- .../api/typereg/BrooklynTypeRegistry.java | 8 +- .../brooklyn/api/typereg/RegisteredType.java | 31 ++- .../api/typereg/RegisteredTypeConstraint.java | 2 +- .../core/plan/PlanNotRecognizedException.java | 6 + .../brooklyn/core/plan/PlanToSpecFactory.java | 4 + .../core/plan/PlanToSpecTransformer.java | 3 +- .../AbstractCustomImplementationPlan.java | 52 +++++ .../typereg/AbstractTypePlanTransformer.java | 130 +++++++++++++ .../core/typereg/BasicBrooklynTypeRegistry.java | 31 ++- .../core/typereg/BasicRegisteredType.java | 122 ++++++++++++ .../typereg/BasicTypeImplementationPlan.java | 41 ++++ .../typereg/BrooklynTypePlanTransformer.java | 68 +++++++ .../core/typereg/JavaTypePlanTransformer.java | 112 +++++++++++ .../core/typereg/RegisteredTypeConstraints.java | 9 +- .../core/typereg/RegisteredTypeKindVisitor.java | 42 ++++ .../brooklyn/core/typereg/RegisteredTypes.java | 192 ++----------------- .../core/typereg/TypePlanTransformers.java | 160 ++++++++++++++++ .../typereg/UnsupportedTypePlanException.java | 37 ++++ .../core/test/BrooklynAppLiveTestSupport.java | 26 +-- .../core/test/BrooklynAppUnitTestSupport.java | 38 +--- .../core/test/BrooklynMgmtUnitTestSupport.java | 58 ++++++ .../typereg/JavaTypePlanTransformerTest.java | 90 +++++++++ .../apache/brooklyn/util/text/Identifiers.java | 3 + .../brooklyn/util/text/IdentifiersTest.java | 6 + 24 files changed, 1024 insertions(+), 247 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java b/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java index 3ade1db..f98f12f 100644 --- a/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java +++ b/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java @@ -33,10 +33,10 @@ public interface BrooklynTypeRegistry { /** a registered type which will create an {@link AbstractBrooklynObjectSpec} (e.g. {@link EntitySpec}) * for the type registered (e.g. the {@link Entity} instance) */ SPEC, - // TODO -// BEAN - - // NB: additional kinds should have the Visitor in RegisteredTypes updated + /** a registered type which will create the java type described */ + BEAN + // note: additional kinds should have the visitor in core/RegisteredTypeKindVisitor updated + // to flush out all places which want to implement support for all kinds } Iterable<RegisteredType> getAll(); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java index 6ffa09c..a1fc5cf 100644 --- a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java +++ b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java @@ -20,14 +20,20 @@ package org.apache.brooklyn.api.typereg; import java.util.Collection; +import javax.annotation.Nullable; + import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.objs.Identifiable; +import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind; + +import com.google.common.annotations.Beta; public interface RegisteredType extends Identifiable { - @Override - String getId(); + @Override String getId(); + + RegisteredTypeKind getKind(); String getSymbolicName(); String getVersion(); @@ -48,9 +54,12 @@ public interface RegisteredType extends Identifiable { * such as if the concrete type is private and callers should know only about a particular public interface, * or if precise type details are unavailable and all that is known at creation is some higher level interface/supertype * (e.g. this may return {@link Entity} even though the spec points at a specific subclass, - * for instance because the YAML has not yet been parsed or OSGi bundles downloaded). + * for instance because the YAML has not yet been parsed or OSGi bundles downloaded). + * <p> + * If nothing is known, this will return null, and the item will not participate in type filtering. */ - Class<?> getJavaType(); + @Beta + @Nullable Class<?> getJavaType(); /** * @return True if the item has been deprecated (i.e. its use is discouraged) @@ -61,5 +70,19 @@ public interface RegisteredType extends Identifiable { * @return True if the item has been disabled (i.e. its use is forbidden, except for pre-existing apps) */ boolean isDisabled(); + + /** @return implementation details, so that the framework can find a suitable {@link BrooklynTypePlanTransformer} + * which can then use this object to instantiate this type */ + TypeImplementationPlan getPlan(); + + public interface TypeImplementationPlan { + /** hint which {@link BrooklynTypePlanTransformer} instance(s) can be used, if known; + * this may be null if the relevant transformer was not declared when created, + * but in general we should look to determine the kind as early as possible + * and use that to retrieve the appropriate such transformer */ + String getPlanFormat(); + /** data for the implementation; may be more specific */ + Object getPlanData(); + } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeConstraint.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeConstraint.java b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeConstraint.java index b55d546..851d88a 100644 --- a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeConstraint.java +++ b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeConstraint.java @@ -41,5 +41,5 @@ public interface RegisteredTypeConstraint { /** encountered types, so that during resolution, * if we have already attempted to resolve a given type, * the instantiator can avoid recursive cycles */ - public Set<String> getEncounteredTypes(); + @Nonnull public Set<String> getEncounteredTypes(); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/plan/PlanNotRecognizedException.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/plan/PlanNotRecognizedException.java b/core/src/main/java/org/apache/brooklyn/core/plan/PlanNotRecognizedException.java index 4d62d4a..dd5c93d 100644 --- a/core/src/main/java/org/apache/brooklyn/core/plan/PlanNotRecognizedException.java +++ b/core/src/main/java/org/apache/brooklyn/core/plan/PlanNotRecognizedException.java @@ -18,8 +18,14 @@ */ package org.apache.brooklyn.core.plan; +import org.apache.brooklyn.core.typereg.BrooklynTypePlanTransformer; +import org.apache.brooklyn.core.typereg.UnsupportedTypePlanException; + +/** @deprecated since 0.9.0 use {@link UnsupportedTypePlanException} as part of switch to {@link BrooklynTypePlanTransformer} */ +@Deprecated public class PlanNotRecognizedException extends RuntimeException { + /** {@link UnsupportedTypePlanException} */ private static final long serialVersionUID = -5590108442839125317L; public PlanNotRecognizedException(String message, Throwable cause) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/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 1b49170..5614b97 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 @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.ServiceLoader; import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.core.typereg.BrooklynTypePlanTransformer; +import org.apache.brooklyn.core.typereg.TypePlanTransformers; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException; import org.apache.brooklyn.util.guava.Maybe; @@ -36,6 +38,8 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; +/** @deprecated since 0.9.0 use {@link TypePlanTransformers} as part of switch to {@link BrooklynTypePlanTransformer} */ +@Deprecated public class PlanToSpecFactory { private static final Logger log = LoggerFactory.getLogger(PlanToSpecFactory.class); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/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 24753aa..e88406c 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 @@ -26,13 +26,14 @@ 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.core.mgmt.ManagementContextInjectable; +import org.apache.brooklyn.core.typereg.BrooklynTypePlanTransformer; import com.google.common.annotations.Beta; /** 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}. - */ + * @deprecated since 0.9.0 use {@link BrooklynTypePlanTransformer} */ @Beta public interface PlanToSpecTransformer extends ManagementContextInjectable { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractCustomImplementationPlan.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractCustomImplementationPlan.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractCustomImplementationPlan.java new file mode 100644 index 0000000..d9dde39 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractCustomImplementationPlan.java @@ -0,0 +1,52 @@ +/* + * 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.typereg; + +import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan; + +/** Abstract superclass for plans to create {@link TypeImplementationPlan} with strong types on + * {@link #getPlanData()} and ensuring the correct format (or null for no format) */ +public abstract class AbstractCustomImplementationPlan<T> extends BasicTypeImplementationPlan { + + public AbstractCustomImplementationPlan(String format, T data) { + super(format, data); + } + public AbstractCustomImplementationPlan(String expectedFormat, Class<T> expectedDataType, TypeImplementationPlan otherPlan) { + super(expectedFormat!=null ? expectedFormat : otherPlan.getPlanFormat(), otherPlan.getPlanData()); + if (!expectedDataType.isInstance(otherPlan.getPlanData())) { + throw new IllegalArgumentException("Plan "+otherPlan+" does not have "+expectedDataType+" data so cannot cast to "+this); + } + if (expectedFormat!=null && otherPlan.getPlanFormat()!=null) { + if (!otherPlan.getPlanFormat().equals(expectedFormat)) { + throw new IllegalArgumentException("Plan "+otherPlan+" in wrong format "+otherPlan.getPlanFormat()+", when expecting "+expectedFormat); + } + } + } + + @Override + public String getPlanFormat() { + return format; + } + + @SuppressWarnings("unchecked") + @Override + public T getPlanData() { + return (T)super.getPlanData(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java new file mode 100644 index 0000000..ae2c610 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java @@ -0,0 +1,130 @@ +/* + * 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.typereg; + +import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec; +import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.api.typereg.RegisteredTypeConstraint; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.javalang.JavaClassNames; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Convenience supertype for {@link BrooklynTypePlanTransformer} instances. + */ +public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTransformer { + + private static final Logger log = LoggerFactory.getLogger(AbstractTypePlanTransformer.class); + + protected ManagementContext mgmt; + + @Override + public void injectManagementContext(ManagementContext mgmt) { + this.mgmt = mgmt; + } + + private final String format; + private final String formatName; + private final String formatDescription; + + protected AbstractTypePlanTransformer(String format, String formatName, String formatDescription) { + this.format = format; + this.formatName = formatName; + this.formatDescription = formatDescription; + } + + @Override + public String getFormatCode() { + return format; + } + + @Override + public String getFormatName() { + return formatName; + } + + @Override + public String getFormatDescription() { + return formatDescription; + } + + @Override + public String toString() { + return getFormatCode()+":"+JavaClassNames.simpleClassName(this); + } + + @Override + public double scoreForType(RegisteredType type, RegisteredTypeConstraint context) { + if (getFormatCode().equals(type.getPlan().getPlanFormat())) return 1; + if (type.getPlan().getPlanFormat()==null) + return scoreForNullFormat(type.getPlan().getPlanData(), type, context); + else + return scoreForNonmatchingNonnullFormat(type.getPlan().getPlanFormat(), type.getPlan().getPlanData(), type, context); + } + + protected abstract double scoreForNullFormat(Object planData, RegisteredType type, RegisteredTypeConstraint context); + protected abstract double scoreForNonmatchingNonnullFormat(String planFormat, Object planData, RegisteredType type, RegisteredTypeConstraint context); + + /** delegates to more specific abstract create methods, + * and performs common validation and customisation of the items created. + * <p> + * this includes: + * <li> setting the {@link AbstractBrooklynObjectSpec#catalogItemId(String)} + */ + @Override + public Object create(final RegisteredType type, final RegisteredTypeConstraint context) { + try { + return validate(new RegisteredTypeKindVisitor<Object>() { + @Override protected Object visitSpec(RegisteredType type) { + try { + AbstractBrooklynObjectSpec<?, ?> result = createSpec(type, context); + result.catalogItemId(type.getId()); + return result; + } catch (Exception e) { throw Exceptions.propagate(e); } + } + @Override protected Object visitBean(RegisteredType type) { + try { + return createBean(type, context); + } catch (Exception e) { throw Exceptions.propagate(e); } + } + + }.visit(type), type, context); + } catch (UnsupportedTypePlanException e) { + // no logging + throw Exceptions.propagate(e); + } catch (Exception e) { + Exceptions.propagateIfFatal(e); + log.debug("Could not instantiate "+type+" (rethrowing): "+Exceptions.collapseText(e)); + throw Exceptions.propagate(e); + } + } + + protected <T> T validate(T createdObject, RegisteredType type, RegisteredTypeConstraint context) { + if (createdObject==null) return null; + // TODO validation based on the constraint, throw UnsupportedTypePlanException with details if not matched + return createdObject; + } + + protected abstract AbstractBrooklynObjectSpec<?,?> createSpec(RegisteredType type, RegisteredTypeConstraint context) throws Exception; + + protected abstract Object createBean(RegisteredType type, RegisteredTypeConstraint context) throws Exception; + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java index 0a9a229..08b6103 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java @@ -29,10 +29,13 @@ import org.apache.brooklyn.api.typereg.RegisteredType; import org.apache.brooklyn.api.typereg.RegisteredTypeConstraint; import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; -import org.apache.brooklyn.core.typereg.RegisteredTypes.RegisteredSpecType; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.guava.Maybe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.api.client.util.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; @@ -96,8 +99,9 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { @SuppressWarnings({ "deprecation", "unchecked", "rawtypes" }) @Override public <SpecT extends AbstractBrooklynObjectSpec<?,?>> SpecT createSpec(RegisteredType type, @Nullable RegisteredTypeConstraint constraint, Class<SpecT> specSuperType) { - if (!(type instanceof RegisteredSpecType)) { - throw new IllegalStateException("Cannot create spec from type "+type); + Preconditions.checkNotNull(type, "type"); + if (type.getKind()!=RegisteredTypeKind.SPEC) { + throw new IllegalStateException("Cannot create spec from type "+type+" (kind "+type.getKind()+")"); } if (constraint!=null) { if (constraint.getKind()!=null && constraint.getKind()!=RegisteredTypeKind.SPEC) { @@ -110,11 +114,28 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { } constraint = RegisteredTypeConstraints.extendedWithSpecSuperType(constraint, specSuperType); - // TODO look up in the actual registry + Maybe<Object> result = TypePlanTransformers.transform(mgmt, type, constraint); + if (result.isPresent()) return (SpecT) result.get(); // fallback: look up in (legacy) catalog + // TODO remove once all transformers are available in the new style CatalogItem item = (CatalogItem) mgmt.getCatalog().getCatalogItem(type.getSymbolicName(), type.getVersion()); - return (SpecT) BasicBrooklynCatalog.internalCreateSpecWithTransformers(mgmt, item, constraint.getEncounteredTypes()); + try { + return (SpecT) BasicBrooklynCatalog.internalCreateSpecWithTransformers(mgmt, item, constraint.getEncounteredTypes()); + } catch (Exception e) { + Exceptions.propagateIfFatal(e); + // for now, combine this failure with the original + try { + result.get(); + // won't come here + throw new IllegalStateException("should have failed getting type resolution for "+type); + } catch (Exception e0) { + // prefer older exception, until the new transformer is the primary pathway + throw Exceptions.create("Unable to instantiate "+type, MutableList.of(e, e0)); + } + // ultimately swallow the legacy failure, return the original failure (the call below will throw because result is absent) +// return (SpecT) result.get(); + } } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java new file mode 100644 index 0000000..ac0b266 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java @@ -0,0 +1,122 @@ +/* + * 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.typereg; + +import java.util.Collection; +import java.util.List; + +import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind; +import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl; +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.util.javalang.JavaClassNames; + +public class BasicRegisteredType implements RegisteredType { + + final String symbolicName; + final String version; + final RegisteredTypeKind kind; + + List<OsgiBundleWithUrl> bundles; + String displayName; + String description; + String iconUrl; + boolean deprecated; + boolean disabled; + + TypeImplementationPlan implementationPlan; + + // TODO ensure this is re-populated on rebind? or remove? + transient Class<?> javaType; + + public BasicRegisteredType(RegisteredTypeKind kind, String symbolicName, String version, Class<?> javaType, TypeImplementationPlan implementationPlan) { + this.kind = kind; + this.symbolicName = symbolicName; + this.version = version; + this.javaType = javaType; + this.implementationPlan = implementationPlan; + } + + @Override + public String getId() { + return symbolicName + (version!=null ? ":"+version : ""); + } + + @Override + public String getSymbolicName() { + return symbolicName; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public RegisteredTypeKind getKind() { + return kind; + } + + @Override + public Collection<OsgiBundleWithUrl> getLibraries() { + return bundles; + } + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getIconUrl() { + return iconUrl; + } + + @Override + public boolean isDisabled() { + return disabled; + } + + @Override + public boolean isDeprecated() { + return deprecated; + } + + @Override + public Class<?> getJavaType() { + return javaType; + } + + @Override + public TypeImplementationPlan getPlan() { + return implementationPlan; + } + + @Override + public String toString() { + return JavaClassNames.simpleClassName(this)+"["+getId()+ + (isDisabled() ? ";DISABLED" : "")+ + (isDeprecated() ? ";deprecated" : "")+ + "]"; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/BasicTypeImplementationPlan.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicTypeImplementationPlan.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicTypeImplementationPlan.java new file mode 100644 index 0000000..7647323 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicTypeImplementationPlan.java @@ -0,0 +1,41 @@ +/* + * 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.typereg; + +import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan; + +public class BasicTypeImplementationPlan implements TypeImplementationPlan { + final String format; + final Object data; + + public BasicTypeImplementationPlan(String format, Object data) { + this.format = format; + this.data = data; + } + + @Override + public String getPlanFormat() { + return format; + } + + @Override + public Object getPlanData() { + return data; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/BrooklynTypePlanTransformer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BrooklynTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BrooklynTypePlanTransformer.java new file mode 100644 index 0000000..96c9cb7 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BrooklynTypePlanTransformer.java @@ -0,0 +1,68 @@ +/* + * 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.typereg; + +import java.util.List; +import java.util.ServiceLoader; + +import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry; +import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind; +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.api.typereg.RegisteredTypeConstraint; +import org.apache.brooklyn.core.mgmt.ManagementContextInjectable; + +/** + * Interface for use by schemes which with to be able to transform plans. + * <p> + * To add a new plan transformation scheme, simply create an implementation and declare it + * as a java service (cf {@link ServiceLoader}). + * <p> + * Implementations may wish to extend {@link AbstractTypePlanTransformer} which simplifies the process. + */ +public interface BrooklynTypePlanTransformer extends ManagementContextInjectable { + + /** @return a code to identify type implementations created specifying the use of this plan transformer. */ + String getFormatCode(); + /** @return a display name for this transformer. */ + String getFormatName(); + /** @return a description for this transformer */ + String getFormatDescription(); + + /** @return how appropriate is this transformer for the {@link RegisteredType#getPlan()} of the type; + * 0 (or less) if not, 1 for absolutely, and in some autodetect cases a value between 0 and 1 indicate a ranking. + * <p> + * The framework guarantees arguments are nonnull, and that the {@link RegisteredType#getPlan()} is also not-null. + * However the format in that plan may be null. */ + double scoreForType(RegisteredType type, RegisteredTypeConstraint context); + /** Creates a new instance of the indicated type, or throws if not supported; + * this method is used by the {@link BrooklynTypeRegistry} when it creates instances, + * so implementations must respect the {@link RegisteredTypeKind} semantics and the {@link RegisteredTypeConstraint} + * if they return an instance. + * <p> + * The framework guarantees this will only be invoked when {@link #scoreForType(RegisteredType, RegisteredTypeConstraint)} + * has returned a positive value. + * <p> + * Implementations should either return null or throw {@link UnsupportedTypePlanException} + * if the {@link RegisteredType#getPlan()} is not supported. */ + Object create(RegisteredType type, RegisteredTypeConstraint context); + + double scoreForTypeDefinition(String formatCode, Object catalogData); + List<RegisteredType> createFromTypeDefinition(String formatCode, Object catalogData); + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformer.java new file mode 100644 index 0000000..febf52a --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformer.java @@ -0,0 +1,112 @@ +/* + * 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.typereg; + +import java.util.List; + +import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec; +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.api.typereg.RegisteredTypeConstraint; +import org.apache.brooklyn.core.catalog.internal.CatalogUtils; +import org.apache.brooklyn.util.text.Identifiers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Instantiates classes from a registered type which simply + * defines the java class name and OSGi bundles to use. + * <p> + * This is used where a {@link RegisteredType} is defined simply with the name of a java class + * e.g. with a no-arg constructor -- no YAML etc just the name of the class. + */ +public class JavaTypePlanTransformer extends AbstractTypePlanTransformer { + + private static final Logger log = LoggerFactory.getLogger(JavaTypePlanTransformer.class); + public static final String FORMAT = "java-type-name"; + + public static class JavaTypeNameImplementation extends AbstractCustomImplementationPlan<String> { + private transient Class<?> cachedType; + public JavaTypeNameImplementation(String javaType) { + super(FORMAT, javaType); + } + public Class<?> getCachedType() { + return cachedType; + } + } + + public JavaTypePlanTransformer() { + super(FORMAT, "Java type name", "Expects a java type name in a format suitable for use with ClassLoader.loadClass"); + } + + @Override + protected double scoreForNullFormat(Object planData, RegisteredType type, RegisteredTypeConstraint context) { + if (type.getPlan().getPlanData() instanceof String && + ((String)type.getPlan().getPlanData()).matches(Identifiers.JAVA_BINARY_REGEX)) { + return 0.1; + } + return 0; + } + + @Override + protected double scoreForNonmatchingNonnullFormat(String planFormat, Object planData, RegisteredType type, RegisteredTypeConstraint context) { + return 0; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + protected AbstractBrooklynObjectSpec<?,?> createSpec(RegisteredType type, RegisteredTypeConstraint context) throws Exception { + Class targetType = getType(type, context); + Class specType = RegisteredTypeConstraints.spec((Class)targetType).getJavaSuperType(); + AbstractBrooklynObjectSpec result = (AbstractBrooklynObjectSpec) specType.getConstructor(Class.class).newInstance(targetType); + return result; + } + + @Override + protected Object createBean(RegisteredType type, RegisteredTypeConstraint context) throws Exception { + return getType(type, context).newInstance(); + } + + private Class<?> getType(RegisteredType type, RegisteredTypeConstraint context) { + if (type.getPlan() instanceof JavaTypeNameImplementation) { + Class<?> cachedType = ((JavaTypeNameImplementation)type.getPlan()).getCachedType(); + if (cachedType==null) { + log.debug("Storing cached type "+cachedType+" for "+type); + cachedType = loadType(type, context); + } + return cachedType; + } + return loadType(type, context); + } + private Class<?> loadType(RegisteredType type, RegisteredTypeConstraint context) { + return CatalogUtils.newClassLoadingContext(mgmt, type).loadClass( ((String)type.getPlan().getPlanData()) ); + } + + + // TODO not supported as a catalog format (yet) + @Override + public double scoreForTypeDefinition(String formatCode, Object catalogData) { + return 0; + } + + @Override + public List<RegisteredType> createFromTypeDefinition(String formatCode, Object catalogData) { + throw new UnsupportedTypePlanException("this transformer does not support YAML catalog additions"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeConstraints.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeConstraints.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeConstraints.java index 9d59343..c880b7e 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeConstraints.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeConstraints.java @@ -22,6 +22,7 @@ import groovy.xml.Entity; import java.util.Set; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.brooklyn.api.entity.EntitySpec; @@ -42,9 +43,9 @@ public class RegisteredTypeConstraints { /** Immutable (from caller's perspective) record of a constraint */ public final static class BasicRegisteredTypeConstraint implements RegisteredTypeConstraint { - private RegisteredTypeKind kind; - private Class<?> javaSuperType; - private Set<String> encounteredTypes; + @Nullable private RegisteredTypeKind kind; + @Nullable private Class<?> javaSuperType; + @Nonnull private Set<String> encounteredTypes = ImmutableSet.of(); private BasicRegisteredTypeConstraint() {} @@ -108,7 +109,7 @@ public class RegisteredTypeConstraints { return of(RegisteredTypeKind.SPEC, javaSuperType); } - public static <T extends AbstractBrooklynObjectSpec<?,?>> RegisteredTypeConstraint extendedWithSpecSuperType(RegisteredTypeConstraint source, Class<T> specSuperType) { + public static <T extends AbstractBrooklynObjectSpec<?,?>> RegisteredTypeConstraint extendedWithSpecSuperType(@Nullable RegisteredTypeConstraint source, @Nullable Class<T> specSuperType) { Class<?> superType = lookupTargetTypeForSpec(specSuperType); BasicRegisteredTypeConstraint constraint = new BasicRegisteredTypeConstraint(source); if (source==null) source = constraint; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeKindVisitor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeKindVisitor.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeKindVisitor.java new file mode 100644 index 0000000..530828e --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypeKindVisitor.java @@ -0,0 +1,42 @@ +/* + * 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.typereg; + +import org.apache.brooklyn.api.typereg.RegisteredType; + +/** Visitor adapter which can be used to ensure all kinds are supported + * <p> + * By design this class may have abstract methods added without notification, + * and subclasses will be responsible for providing the implementation in order to ensure compatibility. */ +public abstract class RegisteredTypeKindVisitor<T> { + + public T visit(RegisteredType type) { + if (type==null) throw new NullPointerException("Registered type must not be null"); + switch (type.getKind()) { + case SPEC: return visitSpec(type); + case BEAN: return visitBean(type); + // others go here + default: + throw new IllegalStateException("Unexpected registered type: "+type.getClass()); + } + } + + protected abstract T visitSpec(RegisteredType type); + protected abstract T visitBean(RegisteredType type); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java index 8cddde2..7e35084 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java @@ -18,15 +18,12 @@ */ package org.apache.brooklyn.core.typereg; -import java.util.Collection; -import java.util.List; - import org.apache.brooklyn.api.catalog.CatalogItem; import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind; import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl; import org.apache.brooklyn.api.typereg.RegisteredType; -import org.apache.brooklyn.core.plan.PlanToSpecTransformer; -import org.apache.brooklyn.util.javalang.JavaClassNames; +import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan; +import org.apache.brooklyn.core.typereg.JavaTypePlanTransformer.JavaTypeNameImplementation; import com.google.common.annotations.Beta; import com.google.common.base.Function; @@ -47,17 +44,16 @@ public class RegisteredTypes { @Deprecated public static RegisteredType of(CatalogItem<?, ?> item) { if (item==null) return null; - TypeImplementation impl = null; + TypeImplementationPlan impl = null; if (item.getPlanYaml()!=null) { - impl = new TypeImplementation(null, item.getPlanYaml()); + impl = new BasicTypeImplementationPlan(null, item.getPlanYaml()); } else if (item.getJavaType()!=null) { - impl = new JavaTypeImplementation(item.getJavaType()); + impl = new JavaTypeNameImplementation(item.getJavaType()); } else { throw new IllegalStateException("Unsupported catalog item "+item+" when trying to create RegisteredType"); } - RegisteredSpecType type = new RegisteredSpecType(item.getSymbolicName(), item.getVersion(), - item.getCatalogItemJavaType(), impl); + BasicRegisteredType type = (BasicRegisteredType) spec(item.getSymbolicName(), item.getVersion(), item.getCatalogItemJavaType(), impl); type.bundles = item.getLibraries()==null ? ImmutableList.<OsgiBundleWithUrl>of() : ImmutableList.<OsgiBundleWithUrl>copyOf(item.getLibraries()); type.displayName = item.getDisplayName(); type.description = item.getDescription(); @@ -66,180 +62,26 @@ public class RegisteredTypes { type.deprecated = item.isDeprecated(); // TODO - // javaType, specType, registeredTypeName ... - // tags ? + // probably not: javaType, specType, registeredTypeName ... + // maybe: tags ? return type; } - - /** Visitor adapter which can be used to ensure all kinds are supported */ - public static abstract class RegisteredTypeKindVisitor<T> { - public T visit(RegisteredType type) { - if (type==null) throw new NullPointerException("Registered type must not be null"); - if (type instanceof RegisteredSpecType) { - return visitSpec((RegisteredSpecType)type); - } - // others go here - throw new IllegalStateException("Unexpected registered type: "+type.getClass()); - } - - protected abstract T visitSpec(RegisteredSpecType type); - - // TODO beans, others - } - - public static RegisteredTypeKind getKindOf(RegisteredType type) { - return new RegisteredTypeKindVisitor<RegisteredTypeKind>() { - @Override protected RegisteredTypeKind visitSpec(RegisteredSpecType type) { return RegisteredTypeKind.SPEC; } - }.visit(type); - } - - public abstract static class AbstractRegisteredType implements RegisteredType { - - final String symbolicName; - final String version; - - List<OsgiBundleWithUrl> bundles; - String displayName; - String description; - String iconUrl; - boolean deprecated; - boolean disabled; - - // TODO ensure this is re-populated on rebind - transient Class<?> javaType; - - public AbstractRegisteredType(String symbolicName, String version, Class<?> javaType) { - this.symbolicName = symbolicName; - this.version = version; - this.javaType = javaType; - } - - @Override - public String getId() { - return symbolicName + (version!=null ? ":"+version : ""); - } - - @Override - public String getSymbolicName() { - return symbolicName; - } - - @Override - public String getVersion() { - return version; - } - - @Override - public Collection<OsgiBundleWithUrl> getLibraries() { - return bundles; - } - - @Override - public String getDisplayName() { - return displayName; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public String getIconUrl() { - return iconUrl; - } - - @Override - public boolean isDisabled() { - return disabled; - } - - @Override - public boolean isDeprecated() { - return deprecated; - } - - @Override - public Class<?> getJavaType() { - return javaType; - } - - @Override - public String toString() { - return JavaClassNames.simpleClassName(this)+"["+getId()+ - (isDisabled() ? ";DISABLED" : "")+ - (isDeprecated() ? ";deprecated" : "")+ - "]"; - } - } - - // TODO -// public static class RegisteredBeanType extends AbstractRegisteredType { -// -// } - public static class RegisteredSpecType extends AbstractRegisteredType { - - private TypeImplementation impl; - - public RegisteredSpecType(String symbolicName, String version, Class<?> javaType, TypeImplementation impl) { - super(symbolicName, version, javaType); - this.impl = impl; - } - - public TypeImplementation getImplementation() { - return impl; - } - } - - public static class TypeImplementation { - final String format; - final Object data; - - public TypeImplementation(String kind, Object data) { - super(); - this.format = kind; - this.data = data; - } - - /** details of the implementation, if known; - * this may be null if the relevant {@link PlanToSpecTransformer} was not declared when created, - * but in general we should look to determine the kind as early as possible and use that - * to retrieve the appropriate such transformer. - */ - public String getFormat() { - return format; - } - - public Object getData() { - return data; - } + public static RegisteredType bean(String symbolicName, String version, Class<?> javaType, TypeImplementationPlan plan) { + return new BasicRegisteredType(RegisteredTypeKind.BEAN, symbolicName, version, javaType, plan); } - public static class JavaTypeImplementation extends TypeImplementation { - public static final String FORMAT = "java"; - public JavaTypeImplementation(String javaType) { - super(FORMAT, javaType); - } - public String getJavaType() { return (String)getData(); } + public static RegisteredType spec(String symbolicName, String version, Class<?> javaType, TypeImplementationPlan plan) { + return new BasicRegisteredType(RegisteredTypeKind.SPEC, symbolicName, version, javaType, plan); } - -// // TODO remove, unless we want it -// public static class CampYamlTypeImplementation extends TypeImplementation { -// public static final String FORMAT = "camp"; -// public CampYamlTypeImplementation(String javaType) { -// super(FORMAT, javaType); -// } -// public String getCampYaml() { return (String)getData(); } -// } /** returns the implementation data for a spec if it is a string (e.g. plan yaml or java class name); else false */ @Beta public static String getImplementationDataStringForSpec(RegisteredType item) { - if (!(item instanceof RegisteredSpecType)) return null; - Object data = ((RegisteredSpecType)item).getImplementation().getData(); - if (data instanceof String) return (String) data; - return null; + if (item==null || item.getPlan()==null) return null; + Object data = item.getPlan().getPlanData(); + if (!(data instanceof String)) return null; + return (String)data; } - + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java b/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java new file mode 100644 index 0000000..b2b496e --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/TypePlanTransformers.java @@ -0,0 +1,160 @@ +/* + * 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.typereg; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.TreeMap; + +import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry; +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.api.typereg.RegisteredTypeConstraint; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException; +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.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +public class TypePlanTransformers { + + private static final Logger log = LoggerFactory.getLogger(TypePlanTransformers.class); + + private static Collection<BrooklynTypePlanTransformer> getAll() { + return ImmutableList.copyOf(ServiceLoader.load(BrooklynTypePlanTransformer.class)); + } + + private static Collection<Class<? extends BrooklynTypePlanTransformer>> OVERRIDE; + @SafeVarargs + @VisibleForTesting + public synchronized static void forceAvailable(Class<? extends BrooklynTypePlanTransformer> ...classes) { + OVERRIDE = Arrays.asList(classes); + } + public synchronized static void clearForced() { + OVERRIDE = null; + } + + public static Collection<BrooklynTypePlanTransformer> all(ManagementContext mgmt) { + // TODO cache these in the TypeRegistry, looking for new ones periodically or supplying a way to register them + Collection<Class<? extends BrooklynTypePlanTransformer>> override = OVERRIDE; + Collection<BrooklynTypePlanTransformer> result = new ArrayList<BrooklynTypePlanTransformer>(); + if (override!=null) { + for (Class<? extends BrooklynTypePlanTransformer> o1: override) { + try { + result.add(o1.newInstance()); + } catch (Exception e) { + Exceptions.propagate(e); + } + } + } else { + result.addAll(getAll()); + } + for(BrooklynTypePlanTransformer t : result) { + t.injectManagementContext(mgmt); + } + return result; + } + + /** returns a list of {@link BrooklynTypePlanTransformer} instances for this {@link ManagementContext} + * which may be able to handle the given plan; the list is sorted with highest-score transformer first */ + @Beta + public static List<BrooklynTypePlanTransformer> forType(ManagementContext mgmt, RegisteredType type, RegisteredTypeConstraint constraint) { + Multimap<Double,BrooklynTypePlanTransformer> byScoreMulti = ArrayListMultimap.create(); + Collection<BrooklynTypePlanTransformer> transformers = all(mgmt); + for (BrooklynTypePlanTransformer transformer : transformers) { + double score = transformer.scoreForType(type, constraint); + if (score>0) byScoreMulti.put(score, transformer); + } + Map<Double, Collection<BrooklynTypePlanTransformer>> tree = new TreeMap<Double, Collection<BrooklynTypePlanTransformer>>(byScoreMulti.asMap()); + List<Collection<BrooklynTypePlanTransformer>> highestFirst = new ArrayList<Collection<BrooklynTypePlanTransformer>>(tree.values()); + Collections.reverse(highestFirst); + return MutableList.copyOf(Iterables.concat(highestFirst)).asUnmodifiable(); + } + + /** transforms the given type to an instance, if possible + * <p> + * callers should generally use one of the create methods on {@link BrooklynTypeRegistry} rather than using this method directly. */ + @Beta + public static Maybe<Object> transform(ManagementContext mgmt, RegisteredType type, RegisteredTypeConstraint constraint) { + if (type==null) return Maybe.absent("type cannot be null"); + if (type.getPlan()==null) return Maybe.absent("type plan cannot be null, when instantiating "+type); + + List<BrooklynTypePlanTransformer> transformers = forType(mgmt, type, constraint); + Collection<String> transformersWhoDontSupport = new ArrayList<String>(); + Collection<Exception> failuresFromTransformers = new ArrayList<Exception>(); + for (BrooklynTypePlanTransformer t: transformers) { + try { + Object result = t.create(type, constraint); + if (result==null) { + transformersWhoDontSupport.add(t.getFormatCode() + " (returned null)"); + continue; + } + return Maybe.of(result); + } catch (UnsupportedTypePlanException e) { + transformersWhoDontSupport.add(t.getFormatCode() + + (Strings.isNonBlank(e.getMessage()) ? " ("+e.getMessage()+")" : "")); + } catch (@SuppressWarnings("deprecation") org.apache.brooklyn.core.plan.PlanNotRecognizedException e) { + // just in case (shouldn't happen) + transformersWhoDontSupport.add(t.getFormatCode() + + (Strings.isNonBlank(e.getMessage()) ? " ("+e.getMessage()+")" : "")); + } catch (Throwable e) { + Exceptions.propagateIfFatal(e); + failuresFromTransformers.add(new PropagatedRuntimeException("Transformer for "+t.getFormatCode()+" gave an error creating this plan: "+ + Exceptions.collapseText(e), e)); + } + } + + // failed + Exception result; + if (!failuresFromTransformers.isEmpty()) { + // at least one thought he could do it + if (log.isDebugEnabled()) { + log.debug("Failure transforming plan; returning summary failure, but for reference " + + "potentially application transformers were "+transformers+", " + + "others available are "+MutableList.builder().addAll(all(mgmt)).removeAll(transformers).build()+"; " + + "failures: "+failuresFromTransformers); + } + result = failuresFromTransformers.size()==1 ? Exceptions.create(null, failuresFromTransformers) : + Exceptions.create("All plan transformers failed", failuresFromTransformers); + } else { + if (transformers.isEmpty()) { + result = new UnsupportedTypePlanException("Invalid plan; format could not be recognized, none of the available transformers "+all(mgmt)+" support "+type); + } else { + result = new UnsupportedTypePlanException("Invalid plan; potentially applicable transformers "+transformers+" do not support it, and other available transformers "+ + MutableList.builder().addAll(all(mgmt)).removeAll(transformers).build()+" do not accept it"); + } + } + return Maybe.absent(result); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/main/java/org/apache/brooklyn/core/typereg/UnsupportedTypePlanException.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/UnsupportedTypePlanException.java b/core/src/main/java/org/apache/brooklyn/core/typereg/UnsupportedTypePlanException.java new file mode 100644 index 0000000..98cbd7a --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/UnsupportedTypePlanException.java @@ -0,0 +1,37 @@ +/* + * 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.typereg; + +public class UnsupportedTypePlanException extends RuntimeException { + + private static final long serialVersionUID = -5590108442839125317L; + + public UnsupportedTypePlanException(String message, Throwable cause) { + super(message, cause); + } + + public UnsupportedTypePlanException(String message) { + super(message); + } + + public UnsupportedTypePlanException(Throwable cause) { + super(cause); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppLiveTestSupport.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppLiveTestSupport.java b/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppLiveTestSupport.java index b986fed..977b7a5 100644 --- a/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppLiveTestSupport.java +++ b/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppLiveTestSupport.java @@ -19,15 +19,9 @@ package org.apache.brooklyn.core.test; import org.apache.brooklyn.api.entity.EntitySpec; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.factory.ApplicationBuilder; import org.apache.brooklyn.core.internal.BrooklynProperties; -import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; import org.apache.brooklyn.core.test.entity.TestApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; /** @@ -36,31 +30,17 @@ import org.testng.annotations.BeforeMethod; * Uses a management context that will not load {@code ~/.brooklyn/catalog.xml} but will * read from the default {@code ~/.brooklyn/brooklyn.properties}. */ -public class BrooklynAppLiveTestSupport { - - private static final Logger LOG = LoggerFactory.getLogger(BrooklynAppLiveTestSupport.class); +public class BrooklynAppLiveTestSupport extends BrooklynMgmtUnitTestSupport { protected TestApplication app; - protected ManagementContextInternal mgmt; @BeforeMethod(alwaysRun=true) public void setUp() throws Exception { if (mgmt!=null) { - app = ApplicationBuilder.newManagedApp(newAppSpec(), mgmt); + app = mgmt.getEntityManager().createEntity(newAppSpec()); } else { mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault()); - app = ApplicationBuilder.newManagedApp(newAppSpec(), mgmt); - } - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - try { - if (mgmt != null) Entities.destroyAll(mgmt); - } catch (Throwable t) { - LOG.error("Caught exception in tearDown method", t); - } finally { - mgmt = null; + app = mgmt.getEntityManager().createEntity(newAppSpec()); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppUnitTestSupport.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppUnitTestSupport.java b/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppUnitTestSupport.java index fa96e0b..cbe39d0 100644 --- a/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppUnitTestSupport.java +++ b/core/src/test/java/org/apache/brooklyn/core/test/BrooklynAppUnitTestSupport.java @@ -20,14 +20,7 @@ package org.apache.brooklyn.core.test; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.core.entity.BrooklynConfigKeys; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.factory.ApplicationBuilder; -import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; -import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; import org.apache.brooklyn.core.test.entity.TestApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; /** @@ -35,40 +28,25 @@ import org.testng.annotations.BeforeMethod; * <p> * Uses a light-weight management context that will not read {@code ~/.brooklyn/brooklyn.properties}. */ -public class BrooklynAppUnitTestSupport { - - private static final Logger LOG = LoggerFactory.getLogger(BrooklynAppUnitTestSupport.class); +public class BrooklynAppUnitTestSupport extends BrooklynMgmtUnitTestSupport { protected TestApplication app; - protected ManagementContextInternal mgmt; - - protected boolean shouldSkipOnBoxBaseDirResolution() { - return true; - } @BeforeMethod(alwaysRun=true) public void setUp() throws Exception { - if (mgmt == null) { - mgmt = LocalManagementContextForTests.newInstance(); - } + super.setUp(); setUpApp(); } + protected boolean shouldSkipOnBoxBaseDirResolution() { + return true; + } + protected void setUpApp() { EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class) .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution()); - app = ApplicationBuilder.newManagedApp(appSpec, mgmt); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - try { - if (mgmt != null) Entities.destroyAll(mgmt); - } catch (Throwable t) { - LOG.error("Caught exception in tearDown method", t); - } finally { - mgmt = null; - } + + app = mgmt.getEntityManager().createEntity(appSpec); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/test/java/org/apache/brooklyn/core/test/BrooklynMgmtUnitTestSupport.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/test/BrooklynMgmtUnitTestSupport.java b/core/src/test/java/org/apache/brooklyn/core/test/BrooklynMgmtUnitTestSupport.java new file mode 100644 index 0000000..956ad63 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/test/BrooklynMgmtUnitTestSupport.java @@ -0,0 +1,58 @@ +/* + * 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.test; + +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; + +/** + * To be extended by unit/integration tests. + * <p> + * Uses a light-weight management context that will not read {@code ~/.brooklyn/brooklyn.properties}. + */ +public class BrooklynMgmtUnitTestSupport { + + private static final Logger LOG = LoggerFactory.getLogger(BrooklynMgmtUnitTestSupport.class); + + protected ManagementContextInternal mgmt; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + if (mgmt == null) { + mgmt = LocalManagementContextForTests.newInstance(); + } + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + try { + if (mgmt != null) Entities.destroyAll(mgmt); + } catch (Throwable t) { + LOG.error("Caught exception in tearDown method", t); + } finally { + mgmt = null; + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/core/src/test/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformerTest.java b/core/src/test/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformerTest.java new file mode 100644 index 0000000..c4d8038 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/typereg/JavaTypePlanTransformerTest.java @@ -0,0 +1,90 @@ +/* + * 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.typereg; + +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class JavaTypePlanTransformerTest extends BrooklynMgmtUnitTestSupport { + + public static class NoArg { + public String name() { return "no-arg"; } + } + + protected RegisteredType type; + protected BrooklynTypePlanTransformer transformer; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + type = newNoArgRegisteredType(JavaTypePlanTransformer.FORMAT); + transformer = newTransformer(); + } + + protected RegisteredType newNoArgRegisteredType(String format) { + return RegisteredTypes.bean("no-arg", "1.0", null, new BasicTypeImplementationPlan(format, NoArg.class.getName())); + } + + protected BrooklynTypePlanTransformer newTransformer() { + BrooklynTypePlanTransformer xf = new JavaTypePlanTransformer(); + xf.injectManagementContext(mgmt); + return xf; + } + + @Test + public void testScoreJavaType() { + double score = transformer.scoreForType(type, null); + Assert.assertEquals(score, 1, 0.00001); + } + + @Test + public void testCreateJavaType() { + Object obj = transformer.create(type, null); + Assert.assertTrue(obj instanceof NoArg, "obj is "+obj); + Assert.assertEquals(((NoArg)obj).name(), "no-arg"); + } + + @Test + public void testScoreJavaTypeWithNullFormat() { + type = newNoArgRegisteredType(null); + double score = transformer.scoreForType(type, null); + Assert.assertEquals(score, 0.1, 0.00001); + } + + @Test + public void testCreateJavaTypeWithNullFormat() { + type = newNoArgRegisteredType(null); + Object obj = transformer.create(type, null); + Assert.assertTrue(obj instanceof NoArg, "obj is "+obj); + Assert.assertEquals(((NoArg)obj).name(), "no-arg"); + } + + @Test + public void testScoreJavaTypeWithOtherFormat() { + type = newNoArgRegisteredType("crazy-format"); + double score = transformer.scoreForType(type, null); + Assert.assertEquals(score, 0, 0.00001); + // we don't test creation; it may or may not succeed, but with score 0 it shouldn't get invoked + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java b/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java index 8d3e035..b389f9c 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java @@ -26,6 +26,9 @@ public class Identifiers { public static final String JAVA_GOOD_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; public static final String JAVA_GOOD_NONSTART_CHARS = JAVA_GOOD_START_CHARS+"1234567890"; + public static final String JAVA_SEGMENT_REGEX = "["+JAVA_GOOD_START_CHARS+"]"+"["+JAVA_GOOD_NONSTART_CHARS+"]*"; + public static final String JAVA_PACKAGE_OR_CLASS_REGEX = "("+JAVA_SEGMENT_REGEX+"\\."+")*"+JAVA_SEGMENT_REGEX; + public static final String JAVA_BINARY_REGEX = JAVA_PACKAGE_OR_CLASS_REGEX+"(\\$["+JAVA_GOOD_NONSTART_CHARS+"]+)*"; public static final String JAVA_GENERATED_IDENTIFIER_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; public static final String JAVA_GENERATED_IDENTIFIERNONSTART_CHARS = JAVA_GENERATED_IDENTIFIER_START_CHARS+"1234567890"; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/817e9bd2/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java ---------------------------------------------------------------------- diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java index 1b2068f..8f4463f 100644 --- a/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java +++ b/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java @@ -86,4 +86,10 @@ public class IdentifiersTest { log.info("ID's made from hash, of -1 is "+id1+" and of Long.MIN_VALUE is "+Identifiers.makeIdFromHash(Long.MIN_VALUE)); } + @Test + public void testJavaClassRegex() { + Assert.assertTrue("foo".matches(Identifiers.JAVA_BINARY_REGEX)); + Assert.assertTrue("foo.bar.Baz$1".matches(Identifiers.JAVA_BINARY_REGEX)); + } + }
