/* * 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.camp.brooklyn.spi.dsl.methods; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.Callable; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.api.sensor.Sensor; import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.entity.EntityPredicates; import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; import org.apache.brooklyn.core.mgmt.internal.EntityManagerInternal; import org.apache.brooklyn.core.sensor.DependentConfiguration; import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.util.core.task.TaskBuilder; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; public class DslComponent extends BrooklynDslDeferredSupplier<Entity> { private static final long serialVersionUID = -7715984495268724954L; private final String componentId; private final DslComponent scopeComponent; private final Scope scope; public DslComponent(String componentId) { this(Scope.GLOBAL, componentId); } public DslComponent(Scope scope, String componentId) { this(null, scope, componentId); } public DslComponent(DslComponent scopeComponent, Scope scope, String componentId) { Preconditions.checkNotNull(scope, "scope"); this.scopeComponent = scopeComponent; this.componentId = componentId; this.scope = scope; } // --------------------------- @Override public Task<Entity> newTask() { return TaskBuilder.<Entity>builder().displayName(toString()).tag(BrooklynTaskTags.TRANSIENT_TASK_TAG) .body(new EntityInScopeFinder(scopeComponent, scope, componentId)).build(); } protected static class EntityInScopeFinder implements Callable<Entity> { protected final DslComponent scopeComponent; protected final Scope scope; protected final String componentId; public EntityInScopeFinder(DslComponent scopeComponent, Scope scope, String componentId) { this.scopeComponent = scopeComponent; this.scope = scope; this.componentId = componentId; } protected EntityInternal getEntity() { if (scopeComponent!=null) { return (EntityInternal)scopeComponent.get(); } else { return entity(); } } @Override public Entity call() throws Exception { Iterable<Entity> entitiesToSearch = null; switch (scope) { case THIS: return getEntity(); case PARENT: return getEntity().getParent(); case GLOBAL: entitiesToSearch = ((EntityManagerInternal)getEntity().getManagementContext().getEntityManager()) .getAllEntitiesInApplication( entity().getApplication() ); break; case ROOT: return getEntity().getApplication(); case SCOPE_ROOT: return Entities.catalogItemScopeRoot(getEntity()); case DESCENDANT: entitiesToSearch = Entities.descendants(getEntity()); break; case ANCESTOR: entitiesToSearch = Entities.ancestors(getEntity()); break; case SIBLING: entitiesToSearch = getEntity().getParent().getChildren(); break; case CHILD: entitiesToSearch = getEntity().getChildren(); break; default: throw new IllegalStateException("Unexpected scope "+scope); } Optional<Entity> result = Iterables.tryFind(entitiesToSearch, EntityPredicates.configEqualTo(BrooklynCampConstants.PLAN_ID, componentId)); if (result.isPresent()) return result.get(); // TODO may want to block and repeat on new entities joining? throw new NoSuchElementException("No entity matching id " + componentId+ (scope==Scope.GLOBAL ? "" : ", in scope "+scope+" wrt "+getEntity()+ (scopeComponent!=null ? " ("+scopeComponent+" from "+entity()+")" : ""))); } } // ------------------------------- // DSL words which move to a new component public DslComponent entity(String scopeOrId) { return new DslComponent(this, Scope.GLOBAL, scopeOrId); } public DslComponent child(String scopeOrId) { return new DslComponent(this, Scope.CHILD, scopeOrId); } public DslComponent sibling(String scopeOrId) { return new DslComponent(this, Scope.SIBLING, scopeOrId); } public DslComponent descendant(String scopeOrId) { return new DslComponent(this, Scope.DESCENDANT, scopeOrId); } public DslComponent ancestor(String scopeOrId) { return new DslComponent(this, Scope.ANCESTOR, scopeOrId); } public DslComponent root() { return new DslComponent(this, Scope.ROOT, ""); } public DslComponent scopeRoot() { return new DslComponent(this, Scope.SCOPE_ROOT, ""); } @Deprecated /** @deprecated since 0.7.0 */ public DslComponent component(String scopeOrId) { return new DslComponent(this, Scope.GLOBAL, scopeOrId); } public DslComponent parent() { return new DslComponent(this, Scope.PARENT, ""); } public DslComponent component(String scope, String id) { if (!DslComponent.Scope.isValid(scope)) { throw new IllegalArgumentException(scope + " is not a vlaid scope"); } return new DslComponent(this, DslComponent.Scope.fromString(scope), id); } // DSL words which return things public BrooklynDslDeferredSupplier<?> attributeWhenReady(final String sensorName) { return new AttributeWhenReady(this, sensorName); } // class simply makes the memento XML files nicer protected static class AttributeWhenReady extends BrooklynDslDeferredSupplier<Object> { private static final long serialVersionUID = 1740899524088902383L; private final DslComponent component; private final String sensorName; public AttributeWhenReady(DslComponent component, String sensorName) { this.component = Preconditions.checkNotNull(component); this.sensorName = sensorName; } @SuppressWarnings("unchecked") @Override public Task<Object> newTask() { Entity targetEntity = component.get(); Sensor<?> targetSensor = targetEntity.getEntityType().getSensor(sensorName); if (!(targetSensor instanceof AttributeSensor<?>)) { targetSensor = Sensors.newSensor(Object.class, sensorName); } return (Task<Object>) DependentConfiguration.attributeWhenReady(targetEntity, (AttributeSensor<?>)targetSensor); } @Override public String toString() { return (component.scope==Scope.THIS ? "" : component.toString()+".") + "attributeWhenReady("+JavaStringEscapes.wrapJavaString(sensorName)+")"; } } public BrooklynDslDeferredSupplier<?> config(final String keyName) { return new DslConfigSupplier(this, keyName); } protected final static class DslConfigSupplier extends BrooklynDslDeferredSupplier<Object> { private final DslComponent component; private final String keyName; private static final long serialVersionUID = -4735177561947722511L; public DslConfigSupplier(DslComponent component, String keyName) { this.component = Preconditions.checkNotNull(component); this.keyName = keyName; } @Override public Task<Object> newTask() { return Tasks.builder().displayName("retrieving config for "+keyName).tag(BrooklynTaskTags.TRANSIENT_TASK_TAG).dynamic(false).body(new Callable<Object>() { @Override public Object call() throws Exception { Entity targetEntity = component.get(); return targetEntity.getConfig(ConfigKeys.newConfigKey(Object.class, keyName)); } }).build(); } @Override public String toString() { return (component.scope==Scope.THIS ? "" : component.toString()+".") + "config("+JavaStringEscapes.wrapJavaString(keyName)+")"; } } public BrooklynDslDeferredSupplier<Sensor<?>> sensor(final String sensorName) { return new DslSensorSupplier(this, sensorName); } protected final static class DslSensorSupplier extends BrooklynDslDeferredSupplier<Sensor<?>> { private final DslComponent component; private final String sensorName; private static final long serialVersionUID = -4735177561947722511L; public DslSensorSupplier(DslComponent component, String sensorName) { this.component = Preconditions.checkNotNull(component); this.sensorName = sensorName; } @Override public Task<Sensor<?>> newTask() { return Tasks.<Sensor<?>>builder().displayName("looking up sensor for "+sensorName).dynamic(false).body(new Callable<Sensor<?>>() { @Override public Sensor<?> call() throws Exception { Entity targetEntity = component.get(); Sensor<?> result = null; if (targetEntity!=null) { result = targetEntity.getEntityType().getSensor(sensorName); } if (result!=null) return result; return Sensors.newSensor(Object.class, sensorName); } }).build(); } @Override public String toString() { return (component.scope==Scope.THIS ? "" : component.toString()+".") + "sensor("+JavaStringEscapes.wrapJavaString(sensorName)+")"; } } public static enum Scope { GLOBAL ("global"), CHILD ("child"), PARENT ("parent"), SIBLING ("sibling"), DESCENDANT ("descendant"), ANCESTOR("ancestor"), ROOT("root"), SCOPE_ROOT("scopeRoot"), THIS ("this"); public static final Set<Scope> VALUES = ImmutableSet.of(GLOBAL, CHILD, PARENT, SIBLING, DESCENDANT, ANCESTOR, ROOT, SCOPE_ROOT, THIS); private final String name; private Scope(String name) { this.name = name; } public static Scope fromString(String name) { return tryFromString(name).get(); } public static Maybe<Scope> tryFromString(String name) { for (Scope scope : VALUES) if (scope.name.toLowerCase().equals(name.toLowerCase())) return Maybe.of(scope); return Maybe.absent(new IllegalArgumentException(name + " is not a valid scope")); } public static boolean isValid(String name) { for (Scope scope : VALUES) if (scope.name.toLowerCase().equals(name.toLowerCase())) return true; return false; } } @Override public String toString() { return "$brooklyn:entity("+ (scopeComponent==null ? "" : JavaStringEscapes.wrapJavaString(scopeComponent.toString())+", ")+ (scope==Scope.GLOBAL ? "" : JavaStringEscapes.wrapJavaString(scope.toString())+", ")+ JavaStringEscapes.wrapJavaString(componentId)+ ")"; } }