You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/12/23 12:06:39 UTC
[16/71] [abbrv] incubator-brooklyn git commit: Merge commit 'e430723'
into reorg2
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityAsserts.java
----------------------------------------------------------------------
diff --cc brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityAsserts.java
index 0000000,4641447..e78a1af
mode 000000,100644..100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityAsserts.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityAsserts.java
@@@ -1,0 -1,228 +1,226 @@@
+ /*
+ * 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.entity;
+
+ import com.google.common.annotations.Beta;
+ import com.google.common.base.Objects;
+ import com.google.common.base.Predicate;
+ import com.google.common.base.Predicates;
+ import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.Maps;
+ import com.google.common.collect.Sets;
+ import org.apache.brooklyn.api.entity.Entity;
+ import org.apache.brooklyn.api.entity.Group;
+ import org.apache.brooklyn.api.mgmt.SubscriptionHandle;
+ import org.apache.brooklyn.api.sensor.AttributeSensor;
+ import org.apache.brooklyn.api.sensor.SensorEvent;
+ import org.apache.brooklyn.api.sensor.SensorEventListener;
+ import org.apache.brooklyn.config.ConfigKey;
+ import org.apache.brooklyn.test.Asserts;
+ import org.apache.brooklyn.util.time.Duration;
+
+ import java.util.Collection;
+ import java.util.Map;
+ import java.util.Set;
+ import java.util.concurrent.Callable;
+ import java.util.concurrent.atomic.AtomicBoolean;
+ import java.util.concurrent.atomic.AtomicReference;
+
+ import static org.apache.brooklyn.test.Asserts.assertEquals;
+
+ /**
+ * Convenience class containing assertions that may be made about entities.
+ */
+ public class EntityAsserts {
+
+
+ public static <T> void assertAttributeEquals(Entity entity, AttributeSensor<T> attribute, T expected) {
+ assertEquals(entity.getAttribute(attribute), expected, "entity=" + entity + "; attribute=" + attribute);
+ }
+
+ public static <T> void assertConfigEquals(Entity entity, ConfigKey<T> configKey, T expected) {
+ assertEquals(entity.getConfig(configKey), expected, "entity=" + entity + "; configKey=" + configKey);
+ }
+
+ public static <T> void assertAttributeEqualsEventually(final Entity entity, final AttributeSensor<T> attribute, final T expected) {
+ assertAttributeEqualsEventually(Maps.newLinkedHashMap(), entity, attribute, expected);
+ }
+
+ public static <T> void assertAttributeEqualsEventually(Map<?,?> flags, final Entity entity, final AttributeSensor<T> attribute, final T expected) {
+ // Not using assertAttributeEventually(predicate) so get nicer error message
+ Asserts.succeedsEventually((Map) flags, new Runnable() {
+ @Override
+ public void run() {
+ assertAttributeEquals(entity, attribute, expected);
+ }
+ });
+ }
+
+ public static <T> T assertAttributeEventuallyNonNull(final Entity entity, final AttributeSensor<T> attribute) {
+ return assertAttributeEventuallyNonNull(Maps.newLinkedHashMap(), entity, attribute);
+ }
+
+ public static <T> T assertAttributeEventuallyNonNull(Map<?,?> flags, final Entity entity, final AttributeSensor<T> attribute) {
+ return assertAttributeEventually(flags, entity, attribute, Predicates.notNull());
+ }
+
+ public static <T> T assertAttributeEventually(final Entity entity, final AttributeSensor<T> attribute, Predicate<? super T> predicate) {
+ return assertAttributeEventually(ImmutableMap.of(), entity, attribute, predicate);
+ }
+
+ public static <T> T assertAttributeEventually(Map<?,?> flags, final Entity entity, final AttributeSensor<T> attribute, final Predicate<? super T> predicate) {
+ final AtomicReference<T> result = new AtomicReference<T>();
+ Asserts.succeedsEventually((Map)flags, new Runnable() {
+ @Override public void run() {
+ T val = entity.getAttribute(attribute);
+ Asserts.assertTrue(predicate.apply(val), "val=" + val);
+ result.set(val);
+ }});
+ return result.get();
+ }
+
+ public static <T> T assertAttribute(final Entity entity, final AttributeSensor<T> attribute, final Predicate<? super T> predicate) {
+ T val = entity.getAttribute(attribute);
+ Asserts.assertTrue(predicate.apply(val), "val=" + val);
+ return val;
+ }
+
+
+ public static <T extends Entity> void assertPredicateEventuallyTrue(final T entity, final Predicate<? super T> predicate) {
+ assertPredicateEventuallyTrue(Maps.newLinkedHashMap(), entity, predicate);
+ }
+
+ public static <T extends Entity> void assertPredicateEventuallyTrue(Map<?,?> flags, final T entity, final Predicate<? super T> predicate) {
+ Asserts.succeedsEventually((Map)flags, new Runnable() {
+ @Override public void run() {
+ Asserts.assertTrue(predicate.apply(entity), "predicate unsatisfied");
+ }});
+ }
+
+ public static <T> void assertAttributeEqualsContinually(final Entity entity, final AttributeSensor<T> attribute, final T expected) {
+ assertAttributeEqualsContinually(Maps.newLinkedHashMap(), entity, attribute, expected);
+ }
+
+ public static <T> void assertAttributeEqualsContinually(Map<?,?> flags, final Entity entity, final AttributeSensor<T> attribute, final T expected) {
+ Asserts.succeedsContinually(flags, new Runnable() {
+ @Override public void run() {
+ assertAttributeEquals(entity, attribute, expected);
+ }});
+ }
+
+ public static void assertGroupSizeEqualsEventually(final Group group, int expected) {
+ assertGroupSizeEqualsEventually(ImmutableMap.of(), group, expected);
+ }
+
+ public static void assertGroupSizeEqualsEventually(Map<?,?> flags, final Group group, final int expected) {
+ Asserts.succeedsEventually((Map)flags, new Runnable() {
+ @Override public void run() {
+ Collection<Entity> members = group.getMembers();
+ assertEquals(members.size(), expected, "members=" + members);
+ }});
+ }
+
+
+ /**
+ * Asserts that the entity's value for this attribute changes, by registering a subscription and checking the value.
+ *
+ * @param entity The entity whose attribute will be checked.
+ * @param attribute The attribute to check on the entity.
+ *
+ * @throws AssertionError if the assertion fails.
+ */
+ public static void assertAttributeChangesEventually(final Entity entity, final AttributeSensor<?> attribute) {
+ final Object origValue = entity.getAttribute(attribute);
+ final AtomicBoolean changed = new AtomicBoolean();
+ SubscriptionHandle handle = entity.subscriptions().subscribe(entity, attribute, new SensorEventListener<Object>() {
+ @Override public void onEvent(SensorEvent<Object> event) {
+ if (!Objects.equal(origValue, event.getValue())) {
+ changed.set(true);
+ }
+ }});
+ try {
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ Asserts.assertTrue(changed.get(), entity + " -> " + attribute + " not changed");
+ }});
+ } finally {
+ entity.subscriptions().unsubscribe(entity, handle);
+ }
+ }
+
+
+ /**
+ * Assert that the given attribute of an entity does not take any of the disallowed values during a given period.
+ *
+ * This method relies on {@link Asserts#succeedsContinually(Runnable)}, therefore it loops comparing the value
+ * of the attribute to the disallowed values, rather than setting up a subscription. It may therefore miss a
+ * situation where the attribute temporarily takes a disallowed value. This method is therefore suited for use
+ * where the attribute will take on a value permanently, which may or may not be disallowed.
+ *
+ * @param entity The entity owning the attribute to check.
+ * @param attribute The attribute on the entity.
+ * @param disallowed The disallowed values for the entity.
+ * @param <T> Type of the sensor.
+ */
+ @Beta @SafeVarargs
+ public static <T> void assertAttributeContinuallyNotEqualTo(final Entity entity, final AttributeSensor<T> attribute, T... disallowed) {
+ final Set<T> reject = Sets.newHashSet(disallowed);
+ Asserts.succeedsContinually(new Runnable() {
+ @Override
+ public void run() {
+ T val = entity.getAttribute(attribute);
+ Asserts.assertFalse(reject.contains(val),
+ "Attribute " + attribute + " on " + entity + " has disallowed value " + val);
+ }
+ });
+ }
+
-
-
+ /**
+ * Assert that the given attribute of an entity does not take any of the disallowed values during a given period.
+ *
+ * This method relies on {@link Asserts#succeedsContinually(Runnable)}, therefore it loops comparing the value
+ * of the attribute to the disallowed values, rather than setting up a subscription. It may therefore miss a
+ * situation where the attribute temporarily takes a disallowed value. This method is therefore suited for use
+ * where the attribute will take on a value permanently, which may or may not be disallowed.
+ *
+ * @param flags Flags controlling the loop, with keys: <ul>
+ * <li>timeout: a {@link Duration} specification String for the duration for which to test the
+ * assertion. Default 1 second.</li>
+ * <li>period: a {@link Duration} specification String for the interval at which to perform polls
+ * on the attribute value. Default 10ms.</li>
+ * </ul>
+ * @param entity The entity owning the attribute to check.
+ * @param attribute The attribute on the entity.
+ * @param disallowed The disallowed values for the entity.
+ * @param <T> Type of the sensor.
+ */
+ @Beta @SafeVarargs
+ public static <T> void assertAttributeContinuallyNotEqualTo(final Map<?, ?> flags, final Entity entity, final AttributeSensor<T> attribute, T... disallowed) {
+ final Set<T> reject = Sets.newHashSet(disallowed);
+ Asserts.succeedsContinually(flags, new Runnable() {
+ @Override
+ public void run() {
+ T val = entity.getAttribute(attribute);
+ Asserts.assertFalse(reject.contains(val),
+ "Attribute " + attribute + " on " + entity + " has disallowed value " + val);
+ }
+ });
+ }
+
+ }
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
----------------------------------------------------------------------
diff --cc brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
index 0000000,a4459ed..c65a176
mode 000000,100644..100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
@@@ -1,0 -1,291 +1,307 @@@
+ /*
+ * 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.entity;
+
+ import static com.google.common.base.Preconditions.checkNotNull;
+
+ import java.util.Collection;
+ import java.util.Map;
+
+ import org.apache.brooklyn.api.entity.Application;
+ import org.apache.brooklyn.api.entity.Entity;
+ import org.apache.brooklyn.api.entity.EntityLocal;
+ import org.apache.brooklyn.api.location.Location;
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ import org.apache.brooklyn.api.objs.Identifiable;
+ import org.apache.brooklyn.api.sensor.AttributeSensor;
+ import org.apache.brooklyn.config.ConfigKey;
+ import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+ import org.apache.brooklyn.util.core.flags.TypeCoercions;
+ import org.apache.brooklyn.util.guava.Functionals;
+
+ import com.google.common.base.Function;
+ import com.google.common.base.Predicate;
+ import com.google.common.base.Supplier;
+ import com.google.common.base.Suppliers;
+ import com.google.common.collect.Iterables;
+
+ public class EntityFunctions {
+
+ /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+ @SuppressWarnings("unused") @Deprecated
+ private static <T> Function<Entity, T> attributeOld(final AttributeSensor<T> attribute) {
+ // TODO PERSISTENCE WORKAROUND
+ class GetEntityAttributeFunction implements Function<Entity, T> {
+ @Override public T apply(Entity input) {
+ return (input == null) ? null : input.getAttribute(attribute);
+ }
- };
++ }
+ return new GetEntityAttributeFunction();
+ }
+
+ /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+ @SuppressWarnings("unused") @Deprecated
+ private static <T> Function<Entity, T> configOld(final ConfigKey<T> key) {
+ // TODO PERSISTENCE WORKAROUND
+ class GetEntityConfigFunction implements Function<Entity, T> {
+ @Override public T apply(Entity input) {
+ return (input == null) ? null : input.getConfig(key);
+ }
- };
++ }
+ return new GetEntityConfigFunction();
+ }
+
+ /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+ @SuppressWarnings("unused") @Deprecated
+ private static Function<Entity, String> displayNameOld() {
+ // TODO PERSISTENCE WORKAROUND
+ class GetEntityDisplayName implements Function<Entity, String> {
+ @Override public String apply(Entity input) {
+ return (input == null) ? null : input.getDisplayName();
+ }
- };
++ }
+ return new GetEntityDisplayName();
+ }
+
+ /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+ @SuppressWarnings("unused") @Deprecated
+ private static Function<Identifiable, String> idOld() {
+ // TODO PERSISTENCE WORKAROUND
+ class GetIdFunction implements Function<Identifiable, String> {
+ @Override public String apply(Identifiable input) {
+ return (input == null) ? null : input.getId();
+ }
- };
++ }
+ return new GetIdFunction();
+ }
+
+ /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+ @SuppressWarnings("unused") @Deprecated
+ private static Function<Entity,Void> settingSensorsConstantOld(final Map<AttributeSensor<?>,Object> values) {
+ // TODO PERSISTENCE WORKAROUND
+ class SettingSensorsConstantFunction implements Function<Entity, Void> {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override public Void apply(Entity input) {
+ for (Map.Entry<AttributeSensor<?>,Object> entry : values.entrySet()) {
+ AttributeSensor sensor = (AttributeSensor)entry.getKey();
+ Object value = entry.getValue();
+ if (value==Entities.UNCHANGED) {
+ // nothing
+ } else if (value==Entities.REMOVE) {
+ ((EntityInternal)input).removeAttribute(sensor);
+ } else {
+ value = TypeCoercions.coerce(value, sensor.getTypeToken());
+ ((EntityInternal)input).sensors().set(sensor, value);
+ }
+ }
+ return null;
+ }
+ }
+ return new SettingSensorsConstantFunction();
+ }
+
+ /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+ @SuppressWarnings("unused") @Deprecated
+ private static <K,V> Function<Entity, Void> updatingSensorMapEntryOld(final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
+ // TODO PERSISTENCE WORKAROUND
+ class UpdatingSensorMapEntryFunction implements Function<Entity, Void> {
+ @Override public Void apply(Entity input) {
+ ServiceStateLogic.updateMapSensorEntry((EntityLocal)input, mapSensor, key, valueSupplier.get());
+ return null;
+ }
+ }
+ return new UpdatingSensorMapEntryFunction();
+ }
+
+ /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+ @SuppressWarnings("unused") @Deprecated
+ private static Supplier<Collection<Application>> applicationsOld(final ManagementContext mgmt) {
+ // TODO PERSISTENCE WORKAROUND
+ class AppsSupplier implements Supplier<Collection<Application>> {
+ @Override
+ public Collection<Application> get() {
+ return mgmt.getApplications();
+ }
+ }
+ return new AppsSupplier();
+ }
+
+ public static <T> Function<Entity, T> attribute(AttributeSensor<T> attribute) {
+ return new GetEntityAttributeFunction<T>(checkNotNull(attribute, "attribute"));
+ }
+
+ protected static class GetEntityAttributeFunction<T> implements Function<Entity, T> {
+ private final AttributeSensor<T> attribute;
+ protected GetEntityAttributeFunction(AttributeSensor<T> attribute) {
+ this.attribute = attribute;
+ }
+ @Override public T apply(Entity input) {
+ return (input == null) ? null : input.getAttribute(attribute);
+ }
- };
++ }
++
++ public static <T> Function<Object, T> attribute(Entity entity, AttributeSensor<T> attribute) {
++ return new GetFixedEntityAttributeFunction<>(entity, attribute);
++ }
++
++ protected static class GetFixedEntityAttributeFunction<T> implements Function<Object, T> {
++ private final Entity entity;
++ private final AttributeSensor<T> attribute;
++ protected GetFixedEntityAttributeFunction(Entity entity, AttributeSensor<T> attribute) {
++ this.entity = entity;
++ this.attribute = attribute;
++ }
++ @Override public T apply(Object input) {
++ return entity.getAttribute(attribute);
++ }
++ }
+
+ public static <T> Function<Entity, T> config(ConfigKey<T> key) {
+ return new GetEntityConfigFunction<T>(checkNotNull(key, "key"));
+ }
+
+ protected static class GetEntityConfigFunction<T> implements Function<Entity, T> {
+ private final ConfigKey<T> key;
+
+ protected GetEntityConfigFunction(ConfigKey<T> key) {
+ this.key = key;
+ }
+
+ @Override public T apply(Entity input) {
+ return (input == null) ? null : input.getConfig(key);
+ }
- };
++ }
+
+ public static Function<Entity, String> displayName() {
+ return GetEntityDisplayName.INSTANCE;
+ }
+
+ protected static class GetEntityDisplayName implements Function<Entity, String> {
+ public static final GetEntityDisplayName INSTANCE = new GetEntityDisplayName();
+ @Override public String apply(Entity input) {
+ return (input == null) ? null : input.getDisplayName();
+ }
- };
++ }
+
+ public static Function<Identifiable, String> id() {
+ return GetIdFunction.INSTANCE;
+ }
+
+ protected static class GetIdFunction implements Function<Identifiable, String> {
+ public static final GetIdFunction INSTANCE = new GetIdFunction();
+ @Override public String apply(Identifiable input) {
+ return (input == null) ? null : input.getId();
+ }
- };
++ }
+
+
+ /** returns a function which sets the given sensors on the entity passed in,
+ * with {@link Entities#UNCHANGED} and {@link Entities#REMOVE} doing those actions. */
+ public static Function<Entity,Void> settingSensorsConstant(final Map<AttributeSensor<?>,Object> values) {
+ return new SettingSensorsConstantFunction(checkNotNull(values, "values"));
+ }
+
+ protected static class SettingSensorsConstantFunction implements Function<Entity, Void> {
+ private final Map<AttributeSensor<?>, Object> values;
+
+ protected SettingSensorsConstantFunction(Map<AttributeSensor<?>, Object> values) {
+ this.values = values;
+ }
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override public Void apply(Entity input) {
+ for (Map.Entry<AttributeSensor<?>,Object> entry : values.entrySet()) {
+ AttributeSensor sensor = (AttributeSensor)entry.getKey();
+ Object value = entry.getValue();
+ if (value==Entities.UNCHANGED) {
+ // nothing
+ } else if (value==Entities.REMOVE) {
+ ((EntityInternal)input).sensors().remove(sensor);
+ } else {
+ value = TypeCoercions.coerce(value, sensor.getTypeToken());
+ ((EntityInternal)input).sensors().set(sensor, value);
+ }
+ }
+ return null;
+ }
+ }
+
+ /** as {@link #settingSensorsConstant(Map)} but as a {@link Runnable} */
+ public static Runnable settingSensorsConstant(final Entity entity, final Map<AttributeSensor<?>,Object> values) {
+ checkNotNull(entity, "entity");
+ checkNotNull(values, "values");
+ return Functionals.runnable(Suppliers.compose(settingSensorsConstant(values), Suppliers.ofInstance(entity)));
+ }
+
+ public static <K,V> Function<Entity, Void> updatingSensorMapEntry(final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
+ return new UpdatingSensorMapEntryFunction<K,V>(mapSensor, key, valueSupplier);
+ }
+
+ protected static class UpdatingSensorMapEntryFunction<K, V> implements Function<Entity, Void> {
+ private final AttributeSensor<Map<K, V>> mapSensor;
+ private final K key;
+ private final Supplier<? extends V> valueSupplier;
+
+ public UpdatingSensorMapEntryFunction(AttributeSensor<Map<K, V>> mapSensor, K key, Supplier<? extends V> valueSupplier) {
+ this.mapSensor = mapSensor;
+ this.key = key;
+ this.valueSupplier = valueSupplier;
+ }
+ @Override public Void apply(Entity input) {
+ ServiceStateLogic.updateMapSensorEntry((EntityLocal)input, mapSensor, key, valueSupplier.get());
+ return null;
+ }
+ }
+
+ public static <K,V> Runnable updatingSensorMapEntry(final Entity entity, final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
+ return Functionals.runnable(Suppliers.compose(updatingSensorMapEntry(mapSensor, key, valueSupplier), Suppliers.ofInstance(entity)));
+ }
+
+ public static Supplier<Collection<Application>> applications(ManagementContext mgmt) {
+ return new AppsSupplier(checkNotNull(mgmt, "mgmt"));
+ }
+
+ protected static class AppsSupplier implements Supplier<Collection<Application>> {
+ private final ManagementContext mgmt;
+
+ public AppsSupplier(ManagementContext mgmt) {
+ this.mgmt = mgmt;
+ }
+ @Override
+ public Collection<Application> get() {
+ return mgmt.getApplications();
+ }
+ }
+
+ public static Function<Entity, Location> locationMatching(Predicate<? super Location> filter) {
+ return new LocationMatching(filter);
+ }
+
+ private static class LocationMatching implements Function<Entity, Location> {
+ private Predicate<? super Location> filter;
+
+ private LocationMatching() { /* for xstream */
+ }
+ public LocationMatching(Predicate<? super Location> filter) {
+ this.filter = filter;
+ }
+ @Override public Location apply(Entity input) {
+ return Iterables.find(input.getLocations(), filter);
+ }
+ }
+ }
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
----------------------------------------------------------------------
diff --cc brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
index 0000000,111eee0..da209e1
mode 000000,100644..100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
@@@ -1,0 -1,306 +1,319 @@@
+ /*
+ * 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.entity.internal;
+
+ import static com.google.common.base.Preconditions.checkNotNull;
+ import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.elvis;
+
++import java.util.Collection;
+ import java.util.Collections;
+ import java.util.LinkedHashMap;
+ import java.util.Map;
+ import java.util.Set;
+
+ import org.apache.brooklyn.api.mgmt.ExecutionContext;
+ import org.apache.brooklyn.api.mgmt.Task;
+ import org.apache.brooklyn.config.ConfigInheritance;
+ import org.apache.brooklyn.config.ConfigKey;
+ import org.apache.brooklyn.core.config.Sanitizer;
+ import org.apache.brooklyn.core.config.StructuredConfigKey;
+ import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
+ import org.apache.brooklyn.core.entity.AbstractEntity;
+ import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.core.config.ConfigBag;
+ import org.apache.brooklyn.util.core.flags.FlagUtils;
+ import org.apache.brooklyn.util.core.flags.SetFromFlag;
+ import org.apache.brooklyn.util.core.flags.TypeCoercions;
+ import org.apache.brooklyn.util.core.internal.ConfigKeySelfExtracting;
+ import org.apache.brooklyn.util.guava.Maybe;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+
+ import com.google.common.base.Predicate;
+ import com.google.common.collect.Maps;
+ import com.google.common.collect.Sets;
+
+ public class EntityConfigMap extends AbstractConfigMapImpl {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EntityConfigMap.class);
+
+ /** entity against which config resolution / task execution will occur */
+ private final AbstractEntity entity;
+
+ /**
+ * Map of configuration information that is defined at start-up time for the entity. These
+ * configuration parameters are shared and made accessible to the "children" of this
+ * entity.
+ */
+ private final Map<ConfigKey<?>,Object> inheritedConfig = Collections.synchronizedMap(new LinkedHashMap<ConfigKey<?>, Object>());
+ // TODO do we really want to have *both* bags and maps for these? danger that they get out of synch.
+ // have added some logic (Oct 2014) so that the same changes are applied to both, in most places at least;
+ // i (alex) think we should prefer ConfigBag (the input keys don't matter, it is more a question of retrieval keys),
+ // but first we need ConfigBag to support StructuredConfigKeys
+ private final ConfigBag localConfigBag;
+ private final ConfigBag inheritedConfigBag;
+
++ public EntityConfigMap(AbstractEntity entity) {
++ // Not using ConcurrentMap, because want to (continue to) allow null values.
++ // Could use ConcurrentMapAcceptingNullVals (with the associated performance hit on entrySet() etc).
++ this(entity, Collections.synchronizedMap(Maps.<ConfigKey<?>, Object>newLinkedHashMap()));
++ }
++
+ public EntityConfigMap(AbstractEntity entity, Map<ConfigKey<?>, Object> storage) {
+ this.entity = checkNotNull(entity, "entity must be specified");
+ this.ownConfig = checkNotNull(storage, "storage map must be specified");
+
+ // TODO store ownUnused in backing-storage
+ this.localConfigBag = ConfigBag.newInstance();
+ this.inheritedConfigBag = ConfigBag.newInstance();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
+ // FIXME What about inherited task in config?!
+ // alex says: think that should work, no?
+ // FIXME What if someone calls getConfig on a task, before setting parent app?
+ // alex says: not supported (throw exception, or return the task)
+
+ // In case this entity class has overridden the given key (e.g. to set default), then retrieve this entity's key
+ // TODO If ask for a config value that's not in our configKeys, should we really continue with rest of method and return key.getDefaultValue?
+ // e.g. SshBasedJavaAppSetup calls setAttribute(JMX_USER), which calls getConfig(JMX_USER)
+ // but that example doesn't have a default...
+ ConfigKey<T> ownKey = entity!=null ? (ConfigKey<T>)elvis(entity.getEntityType().getConfigKey(key.getName()), key) : key;
+
+ ConfigInheritance inheritance = key.getInheritance();
+ if (inheritance==null) inheritance = ownKey.getInheritance();
+ if (inheritance==null) {
+ // TODO we could warn by introducing a temporary "ALWAYS_BUT_WARNING" instance
+ inheritance = getDefaultInheritance();
+ }
+
+ // TODO We're notifying of config-changed because currently persistence needs to know when the
+ // attributeWhenReady is complete (so it can persist the result).
+ // Long term, we'll just persist tasks properly so the call to onConfigChanged will go!
+
+ // Don't use groovy truth: if the set value is e.g. 0, then would ignore set value and return default!
+ if (ownKey instanceof ConfigKeySelfExtracting) {
+ Object rawval = ownConfig.get(key);
+ T result = null;
+ boolean complete = false;
+ if (((ConfigKeySelfExtracting<T>)ownKey).isSet(ownConfig)) {
+ ExecutionContext exec = entity.getExecutionContext();
+ result = ((ConfigKeySelfExtracting<T>)ownKey).extractValue(ownConfig, exec);
+ complete = true;
+ } else if (isInherited(ownKey, inheritance) &&
+ ((ConfigKeySelfExtracting<T>)ownKey).isSet(inheritedConfig)) {
+ ExecutionContext exec = entity.getExecutionContext();
+ result = ((ConfigKeySelfExtracting<T>)ownKey).extractValue(inheritedConfig, exec);
+ complete = true;
+ } else if (localConfigBag.containsKey(ownKey)) {
+ // TODO configBag.get doesn't handle tasks/attributeWhenReady - it only uses TypeCoercions
+ result = localConfigBag.get(ownKey);
+ complete = true;
+ } else if (isInherited(ownKey, inheritance) &&
+ inheritedConfigBag.containsKey(ownKey)) {
+ result = inheritedConfigBag.get(ownKey);
+ complete = true;
+ }
+
+ if (rawval instanceof Task) {
+ entity.getManagementSupport().getEntityChangeListener().onConfigChanged(key);
+ }
+ if (complete) {
+ return result;
+ }
+ } else {
+ LOG.warn("Config key {} of {} is not a ConfigKeySelfExtracting; cannot retrieve value; returning default", ownKey, this);
+ }
+ return TypeCoercions.coerce((defaultValue != null) ? defaultValue : ownKey.getDefaultValue(), key.getTypeToken());
+ }
+
+ private <T> boolean isInherited(ConfigKey<T> key) {
+ return isInherited(key, key.getInheritance());
+ }
+ private <T> boolean isInherited(ConfigKey<T> key, ConfigInheritance inheritance) {
+ if (inheritance==null) inheritance = getDefaultInheritance();
+ return inheritance.isInherited(key, entity.getParent(), entity);
+ }
+ private ConfigInheritance getDefaultInheritance() {
+ return ConfigInheritance.ALWAYS;
+ }
+
+ @Override
+ public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
+ if (ownConfig.containsKey(key)) return Maybe.of(ownConfig.get(key));
+ if (includeInherited && inheritedConfig.containsKey(key)) return Maybe.of(inheritedConfig.get(key));
+ return Maybe.absent();
+ }
+
+ /** an immutable copy of the config visible at this entity, local and inherited (preferring local) */
+ public Map<ConfigKey<?>,Object> getAllConfig() {
+ Map<ConfigKey<?>,Object> result = new LinkedHashMap<ConfigKey<?>,Object>(inheritedConfig.size()+ownConfig.size());
+ result.putAll(inheritedConfig);
+ result.putAll(ownConfig);
+ return Collections.unmodifiableMap(result);
+ }
+
+ /** an immutable copy of the config defined at this entity, ie not inherited */
+ public Map<ConfigKey<?>,Object> getLocalConfig() {
+ Map<ConfigKey<?>,Object> result = new LinkedHashMap<ConfigKey<?>,Object>(ownConfig.size());
+ result.putAll(ownConfig);
+ return Collections.unmodifiableMap(result);
+ }
+
+ /** Creates an immutable copy of the config visible at this entity, local and inherited (preferring local), including those that did not match config keys */
+ public ConfigBag getAllConfigBag() {
+ return ConfigBag.newInstanceCopying(localConfigBag)
+ .putAll(ownConfig)
+ .putIfAbsent(inheritedConfig)
+ .putIfAbsent(inheritedConfigBag)
+ .seal();
+ }
+
+ /** Creates an immutable copy of the config defined at this entity, ie not inherited, including those that did not match config keys */
+ public ConfigBag getLocalConfigBag() {
+ return ConfigBag.newInstanceCopying(localConfigBag)
+ .putAll(ownConfig)
+ .seal();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object setConfig(ConfigKey<?> key, Object v) {
+ Object val = coerceConfigVal(key, v);
+ Object oldVal;
+ if (key instanceof StructuredConfigKey) {
+ oldVal = ((StructuredConfigKey)key).applyValueToMap(val, ownConfig);
+ // TODO ConfigBag does not handle structured config keys; quick fix is to remove (and should also remove any subkeys;
+ // as it stands if someone set string a.b.c in the config bag then removed structured key a.b, then got a.b.c they'd get a vale);
+ // long term fix is to support structured config keys in ConfigBag, at which point i think we could remove ownConfig altogether
+ localConfigBag.remove(key);
+ } else {
+ oldVal = ownConfig.put(key, val);
+ localConfigBag.put((ConfigKey<Object>)key, v);
+ }
+ entity.config().refreshInheritedConfigOfChildren();
+ return oldVal;
+ }
+
+ public void setLocalConfig(Map<ConfigKey<?>, ?> vals) {
+ ownConfig.clear();
+ localConfigBag.clear();
+ ownConfig.putAll(vals);
+ localConfigBag.putAll(vals);
+ }
+
+ public void setInheritedConfig(Map<ConfigKey<?>, ?> valsO, ConfigBag configBagVals) {
+ Map<ConfigKey<?>, ?> vals = filterUninheritable(valsO);
+
+ inheritedConfig.clear();
+ inheritedConfig.putAll(vals);
+
+ // The configBagVals contains all inherited, including strings that did not match a config key on the parent.
+ // They might match a config-key on this entity though, so need to check that:
+ // - if it matches one of our keys, set it in inheritedConfig
+ // - otherwise add it to our inheritedConfigBag
+ Set<String> valKeyNames = Sets.newLinkedHashSet();
+ for (ConfigKey<?> key : vals.keySet()) {
+ valKeyNames.add(key.getName());
+ }
+ Map<String,Object> valsUnmatched = MutableMap.<String,Object>builder()
+ .putAll(configBagVals.getAllConfig())
+ .removeAll(valKeyNames)
+ .build();
+ inheritedConfigBag.clear();
+ Map<ConfigKey<?>, SetFromFlag> annotatedConfigKeys = FlagUtils.getAnnotatedConfigKeys(entity.getClass());
+ Map<String, ConfigKey<?>> renamedConfigKeys = Maps.newLinkedHashMap();
+ for (Map.Entry<ConfigKey<?>, SetFromFlag> entry: annotatedConfigKeys.entrySet()) {
+ String rename = entry.getValue().value();
+ if (rename != null) {
+ renamedConfigKeys.put(rename, entry.getKey());
+ }
+ }
+ for (Map.Entry<String,Object> entry : valsUnmatched.entrySet()) {
+ String name = entry.getKey();
+ Object value = entry.getValue();
+ ConfigKey<?> key = renamedConfigKeys.get(name);
+ if (key == null) key = entity.getEntityType().getConfigKey(name);
+ if (key != null) {
+ if (!isInherited(key)) {
+ // no-op
+ } else if (inheritedConfig.containsKey(key)) {
+ LOG.warn("Entity "+entity+" inherited duplicate config for key "+key+", via explicit config and string name "+name+"; using value of key");
+ } else {
+ inheritedConfig.put(key, value);
+ }
+ } else {
+ // a config bag has discarded the keys, so we must assume default inheritance for things given that way
+ // unless we can infer a key; not a big deal, as we should have the key in inheritedConfig for everything
+ // which originated with a key ... but still, it would be nice to clean up the use of config bag!
+ inheritedConfigBag.putStringKey(name, value);
+ }
+ }
+ }
+
+ private Map<ConfigKey<?>, ?> filterUninheritable(Map<ConfigKey<?>, ?> vals) {
+ Map<ConfigKey<?>, Object> result = Maps.newLinkedHashMap();
+ for (Map.Entry<ConfigKey<?>, ?> entry : vals.entrySet()) {
+ if (isInherited(entry.getKey())) {
+ result.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return result;
+ }
+
+ public void addToLocalBag(Map<String,?> vals) {
+ localConfigBag.putAll(vals);
+ // quick fix for problem that ownConfig can get out of synch
+ ownConfig.putAll(localConfigBag.getAllConfigAsConfigKeyMap());
+ }
+
+ public void removeFromLocalBag(String key) {
+ localConfigBag.remove(key);
+ ownConfig.remove(key);
+ }
+
+ public void clearInheritedConfig() {
+ inheritedConfig.clear();
+ inheritedConfigBag.clear();
+ }
+
+ @Override
+ public EntityConfigMap submap(Predicate<ConfigKey<?>> filter) {
+ EntityConfigMap m = new EntityConfigMap(entity, Maps.<ConfigKey<?>, Object>newLinkedHashMap());
+ for (Map.Entry<ConfigKey<?>,Object> entry: inheritedConfig.entrySet())
+ if (filter.apply(entry.getKey()))
+ m.inheritedConfig.put(entry.getKey(), entry.getValue());
- for (Map.Entry<ConfigKey<?>,Object> entry: ownConfig.entrySet())
- if (filter.apply(entry.getKey()))
- m.ownConfig.put(entry.getKey(), entry.getValue());
++ synchronized (ownConfig) {
++ for (Map.Entry<ConfigKey<?>,Object> entry: ownConfig.entrySet())
++ if (filter.apply(entry.getKey()))
++ m.ownConfig.put(entry.getKey(), entry.getValue());
++ }
+ return m;
+ }
+
+ @Override
+ public String toString() {
- return super.toString()+"[own="+Sanitizer.sanitize(ownConfig)+"; inherited="+Sanitizer.sanitize(inheritedConfig)+"]";
++ Map<ConfigKey<?>, Object> sanitizeConfig;
++ synchronized (ownConfig) {
++ sanitizeConfig = Sanitizer.sanitize(ownConfig);
++ }
++ return super.toString()+"[own="+sanitizeConfig+"; inherited="+Sanitizer.sanitize(inheritedConfig)+"]";
+ }
+
+ }
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/FeedConfig.java
----------------------------------------------------------------------
diff --cc brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/FeedConfig.java
index 0000000,4d06680..b9662ef
mode 000000,100644..100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/FeedConfig.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/feed/FeedConfig.java
@@@ -1,0 -1,297 +1,307 @@@
+ /*
+ * 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.feed;
+
+ import static com.google.common.base.Preconditions.checkNotNull;
+
+ import org.apache.brooklyn.api.sensor.AttributeSensor;
+ import org.apache.brooklyn.core.entity.Entities;
+ import org.apache.brooklyn.core.sensor.Sensors;
+ import org.apache.brooklyn.feed.http.HttpPollConfig;
+ import org.apache.brooklyn.util.collections.MutableList;
+ import org.apache.brooklyn.util.guava.Functionals;
+ import org.apache.brooklyn.util.javalang.JavaClassNames;
+ import org.apache.brooklyn.util.text.Strings;
+
+ import com.google.common.base.Function;
+ import com.google.common.base.Functions;
+ import com.google.common.base.Objects;
+ import com.google.common.base.Predicate;
+
+ /**
+ * Configuration for a poll, or a subscription etc, that is being added to a feed.
+ *
+ * @author aled
+ */
+ public class FeedConfig<V, T, F extends FeedConfig<V, T, F>> {
+
+ /** The onSuccess or onError functions can return this value to indicate that the sensor should not change.
+ * @deprecated since 0.7.0 use UNCHANGED */
+ public static final Object UNSET = Entities.UNCHANGED;
+ /** The onSuccess or onError functions can return this value to indicate that the sensor should not change. */
+ public static final Object UNCHANGED = Entities.UNCHANGED;
+ /** The onSuccess or onError functions can return this value to indicate that the sensor value should be removed
+ * (cf 'null', but useful in dynamic situations) */
+ public static final Object REMOVE = Entities.REMOVE;
+
+ /** Indicates that no sensor is being used here. This sensor is suppressed,
+ * but is useful where you want to use the feeds with custom success/exception/failure functions
+ * which directly set multiple sensors, e.g. dynamically based on the poll response.
+ * <p>
+ * See {@link HttpPollConfig#forMultiple()} and its usages.
+ * (It can work for any poll config, but conveniences have not been supplied for others.) */
+ public static final AttributeSensor<Void> NO_SENSOR = Sensors.newSensor(Void.class, "brooklyn.no.sensor");
+
+ private final AttributeSensor<T> sensor;
+ private Function<? super V, T> onsuccess;
+ private Function<? super V, T> onfailure;
+ private Function<? super Exception, T> onexception;
+ private Predicate<? super V> checkSuccess;
+ private boolean suppressDuplicates;
+ private boolean enabled = true;
+
+ public FeedConfig(AttributeSensor<T> sensor) {
+ this.sensor = checkNotNull(sensor, "sensor");
+ }
+
+ public FeedConfig(FeedConfig<V, T, F> other) {
+ this.sensor = other.sensor;
+ this.onsuccess = other.onsuccess;
+ this.onfailure = other.onfailure;
+ this.onexception = other.onexception;
+ this.checkSuccess = other.checkSuccess;
+ this.suppressDuplicates = other.suppressDuplicates;
+ this.enabled = other.enabled;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected F self() {
+ return (F) this;
+ }
+
+ public AttributeSensor<T> getSensor() {
+ return sensor;
+ }
+
+ public Predicate<? super V> getCheckSuccess() {
+ return checkSuccess;
+ }
+
+ public Function<? super V, T> getOnSuccess() {
+ return onsuccess;
+ }
+
+ public Function<? super V, T> getOnFailure() {
+ return onfailure;
+ }
+
+ public Function<? super Exception, T> getOnException() {
+ return onexception;
+ }
+
+ public boolean getSupressDuplicates() {
+ return suppressDuplicates;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /** sets the predicate used to check whether a feed run is successful */
+ public F checkSuccess(Predicate<? super V> val) {
+ this.checkSuccess = checkNotNull(val, "checkSuccess");
+ return self();
+ }
+ /** as {@link #checkSuccess(Predicate)} */
+ public F checkSuccess(final Function<? super V,Boolean> val) {
+ return checkSuccess(Functionals.predicate(val));
+ }
++
+ @SuppressWarnings("unused")
+ /** @deprecated since 0.7.0, kept for rebind */ @Deprecated
+ private F checkSuccessLegacy(final Function<? super V,Boolean> val) {
+ return checkSuccess(new Predicate<V>() {
+ @Override
+ public boolean apply(V input) {
+ return val.apply(input);
+ }
+ });
+ }
+
+ public F onSuccess(Function<? super V,T> val) {
+ this.onsuccess = checkNotNull(val, "onSuccess");
+ return self();
+ }
-
++
+ public F setOnSuccess(T val) {
+ return onSuccess(Functions.constant(val));
+ }
-
- /** a failure is when the connection is fine (no exception) but the other end returns a result object V
- * which the feed can tell indicates a failure (e.g. HTTP code 404) */
++
++ /**
++ * A failure is when the connection is fine (no exception) but the other end returns a result object V
++ * which the feed can tell indicates a failure (e.g. HTTP code 404)
++ */
+ public F onFailure(Function<? super V,T> val) {
+ this.onfailure = checkNotNull(val, "onFailure");
+ return self();
+ }
+
++ /** @see #onFailure(Function) */
+ public F setOnFailure(T val) {
+ return onFailure(Functions.constant(val));
+ }
+
- /** registers a callback to be used {@link #onSuccess(Function)} and {@link #onFailure(Function)},
- * i.e. whenever a result comes back, but not in case of exceptions being thrown (ie problems communicating) */
++ /**
++ * Registers a callback to be used by {@link #onSuccess(Function)} and {@link #onFailure(Function)},
++ * i.e. whenever a result comes back, but not in case of exceptions being thrown (ie problems communicating)
++ */
+ public F onResult(Function<? super V, T> val) {
+ onSuccess(val);
+ return onFailure(val);
+ }
+
++ /** @see #onResult(Function) */
+ public F setOnResult(T val) {
+ return onResult(Functions.constant(val));
+ }
+
- /** an exception is when there is an error in the communication */
++ /**
++ * An exception is when there is an error in the communication
++ */
+ public F onException(Function<? super Exception,T> val) {
+ this.onexception = checkNotNull(val, "onException");
+ return self();
+ }
-
++
++ /** @see #onException(Function) */
+ public F setOnException(T val) {
+ return onException(Functions.constant(val));
+ }
+
- /** convenience for indicating a behaviour to occur for both
- * {@link #onException(Function)}
- * (error connecting) and
- * {@link #onFailure(Function)}
- * (successful communication but failure report from remote end) */
++ /**
++ * A convenience for indicating a behaviour to occur for both {@link #onException(Function)}
++ * (error connecting) and {@link #onFailure(Function)} (successful communication but failure
++ * report from remote end)
++ */
+ public F onFailureOrException(Function<Object,T> val) {
+ onFailure(val);
+ return onException(val);
+ }
-
++
++ /** @see #onFailureOrException(Function) */
+ public F setOnFailureOrException(T val) {
+ return onFailureOrException(Functions.constant(val));
+ }
+
+ public F suppressDuplicates(boolean val) {
+ suppressDuplicates = val;
+ return self();
+ }
-
++
+ /**
+ * Whether this feed is enabled (defaulting to true).
+ */
+ public F enabled(boolean val) {
+ enabled = val;
+ return self();
+ }
-
++
+ public boolean hasSuccessHandler() {
+ return this.onsuccess != null;
+ }
+
+ public boolean hasFailureHandler() {
+ return this.onfailure != null;
+ }
+
+ public boolean hasExceptionHandler() {
+ return this.onexception != null;
+ }
+
+ public boolean hasCheckSuccessHandler() {
+ return this.checkSuccess != null;
+ }
-
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append(toStringBaseName());
+ result.append("[");
+ boolean contents = false;
+ Object source = toStringPollSource();
+ AttributeSensor<T> s = getSensor();
+ if (Strings.isNonBlank(Strings.toString(source))) {
+ result.append(Strings.toUniqueString(source, 40));
+ if (s!=null) {
+ result.append("->");
+ result.append(s.getName());
+ }
+ contents = true;
+ } else if (s!=null) {
+ result.append(s.getName());
+ contents = true;
+ }
+ MutableList<Object> fields = toStringOtherFields();
+ if (fields!=null) {
+ for (Object field: fields) {
+ if (Strings.isNonBlank(Strings.toString(field))) {
+ if (contents) result.append(";");
+ contents = true;
+ result.append(field);
+ }
+ }
+ }
+ result.append("]");
+ return result.toString();
+ }
+
+ /** can be overridden to supply a simpler base name than the class name */
+ protected String toStringBaseName() {
+ return JavaClassNames.simpleClassName(this);
+ }
+ /** can be overridden to supply add'l info for the {@link #toString()}; subclasses can add to the returned value */
+ protected MutableList<Object> toStringOtherFields() {
+ return MutableList.<Object>of();
+ }
+ /** can be overridden to supply add'l info for the {@link #toString()}, placed before the sensor with -> */
+ protected Object toStringPollSource() {
+ return null;
+ }
+ /** all configs should supply a unique tag element, inserted into the feed */
+ protected String getUniqueTag() {
+ return toString();
+ }
+
+ /** returns fields which should be used for equality, including by default {@link #toStringOtherFields()} and {@link #toStringPollSource()};
+ * subclasses can add to the returned value */
+ protected MutableList<Object> equalsFields() {
+ MutableList<Object> result = MutableList.of().appendIfNotNull(getSensor()).appendIfNotNull(toStringPollSource());
+ for (Object field: toStringOtherFields()) result.appendIfNotNull(field);
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = super.hashCode();
+ for (Object f: equalsFields())
+ hc = Objects.hashCode(hc, f);
+ return hc;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!super.equals(obj)) return false;
+ PollConfig<?,?,?> other = (PollConfig<?,?,?>) obj;
+ if (!Objects.equal(getUniqueTag(), other.getUniqueTag())) return false;
+ if (!Objects.equal(equalsFields(), other.equalsFields())) return false;
+ return true;
+ }
+
+ }
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynProperties.java
----------------------------------------------------------------------
diff --cc brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynProperties.java
index 0000000,c1ffb42..75b7b34
mode 000000,100644..100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynProperties.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynProperties.java
@@@ -1,0 -1,481 +1,305 @@@
+ /*
+ * 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.internal;
+
+ import static com.google.common.base.Preconditions.checkNotNull;
-import groovy.lang.Closure;
+
+ import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
+ import java.io.InputStream;
+ import java.net.URL;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
+ import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Properties;
+
+ import org.apache.brooklyn.config.ConfigKey;
+ import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+ import org.apache.brooklyn.config.StringConfigMap;
-import org.apache.brooklyn.core.config.BasicConfigKey;
-import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.core.ResourceUtils;
+ import org.apache.brooklyn.util.core.config.ConfigBag;
-import org.apache.brooklyn.util.core.flags.TypeCoercions;
+ import org.apache.brooklyn.util.guava.Maybe;
+ import org.apache.brooklyn.util.os.Os;
-import org.apache.brooklyn.util.text.StringFunctions;
-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.base.CharMatcher;
+ import com.google.common.base.Objects;
+ import com.google.common.base.Predicate;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Maps;
+
-/** utils for accessing command-line and system-env properties;
++/**
++ * Utils for accessing command-line and system-env properties;
+ * doesn't resolve anything (unless an execution context is supplied)
+ * and treats ConfigKeys as of type object when in doubt,
+ * or string when that is likely wanted (e.g. {@link #getFirst(Map, String...)}
+ * <p>
- * TODO methods in this class are not thread safe.
- * intention is that they are set during startup and not modified thereafter. */
++ * Intention for normal use is that they are set during startup and not modified
++ * thereafter.
++ */
+ @SuppressWarnings("rawtypes")
-public class BrooklynProperties extends LinkedHashMap implements StringConfigMap {
-
- private static final long serialVersionUID = -945875483083108978L;
- private static final Logger LOG = LoggerFactory.getLogger(BrooklynProperties.class);
++public interface BrooklynProperties extends Map, StringConfigMap {
+
+ public static class Factory {
++ private static final Logger LOG = LoggerFactory.getLogger(BrooklynProperties.Factory.class);
++
+ /** creates a new empty {@link BrooklynProperties} */
+ public static BrooklynProperties newEmpty() {
- return new BrooklynProperties();
++ return new BrooklynPropertiesImpl();
+ }
+
+ /** creates a new {@link BrooklynProperties} with contents loaded
+ * from the usual places, including *.properties files and environment variables */
+ public static BrooklynProperties newDefault() {
+ return new Builder(true).build();
+ }
+
+ public static Builder builderDefault() {
+ return new Builder(true);
+ }
+
+ public static Builder builderEmpty() {
+ return new Builder(false);
+ }
+
+ public static class Builder {
+ private String defaultLocationMetadataUrl;
+ private String globalLocationMetadataFile = null;
+ private String globalPropertiesFile = null;
+ private String localPropertiesFile = null;
+ private BrooklynProperties originalProperties = null;
+
+ /** @deprecated since 0.7.0 use static methods in {@link Factory} to create */
+ public Builder() {
+ this(true);
+ }
+
+ private Builder(boolean setGlobalFileDefaults) {
+ resetDefaultLocationMetadataUrl();
+ if (setGlobalFileDefaults) {
+ resetGlobalFiles();
+ }
+ }
+
+ public Builder resetDefaultLocationMetadataUrl() {
+ defaultLocationMetadataUrl = "classpath://brooklyn/location-metadata.properties";
+ return this;
+ }
+ public Builder resetGlobalFiles() {
+ defaultLocationMetadataUrl = "classpath://brooklyn/location-metadata.properties";
+ globalLocationMetadataFile = Os.mergePaths(Os.home(), ".brooklyn", "location-metadata.properties");
+ globalPropertiesFile = Os.mergePaths(Os.home(), ".brooklyn", "brooklyn.properties");
+ return this;
+ }
+
+ /**
+ * Creates a Builder that when built, will return the BrooklynProperties passed to this constructor
+ */
+ private Builder(BrooklynProperties originalProperties) {
- this.originalProperties = new BrooklynProperties().addFromMap(originalProperties);
++ this.originalProperties = new BrooklynPropertiesImpl().addFromMap(originalProperties);
+ }
+
+ /**
+ * The URL of a default location-metadata.properties (for meta-data about different locations, such as iso3166 and global lat/lon).
+ * Defaults to classpath://brooklyn/location-metadata.properties
+ */
+ public Builder defaultLocationMetadataUrl(String val) {
+ defaultLocationMetadataUrl = checkNotNull(val, "file");
+ return this;
+ }
+
+ /**
+ * The URL of a location-metadata.properties file that appends to and overwrites values in the locationMetadataUrl.
+ * Defaults to ~/.brooklyn/location-metadata.properties
+ */
+ public Builder globalLocationMetadataFile(String val) {
+ globalLocationMetadataFile = checkNotNull(val, "file");
+ return this;
+ }
+
+ /**
+ * The URL of a shared brooklyn.properties file. Defaults to ~/.brooklyn/brooklyn.properties.
+ * Can be null to disable.
+ */
+ public Builder globalPropertiesFile(String val) {
+ globalPropertiesFile = val;
+ return this;
+ }
+
+ @Beta
+ public boolean hasDelegateOriginalProperties() {
+ return this.originalProperties==null;
+ }
+
+ /**
+ * The URL of a brooklyn.properties file specific to this launch. Appends to and overwrites values in globalPropertiesFile.
+ */
+ public Builder localPropertiesFile(String val) {
+ localPropertiesFile = val;
+ return this;
+ }
+
+ public BrooklynProperties build() {
+ if (originalProperties != null)
- return new BrooklynProperties().addFromMap(originalProperties);
++ return new BrooklynPropertiesImpl().addFromMap(originalProperties);
+
- BrooklynProperties properties = new BrooklynProperties();
++ BrooklynProperties properties = new BrooklynPropertiesImpl();
+
+ // TODO Could also read from http://brooklyn.io, for up-to-date values?
+ // But might that make unit tests run very badly when developer is offline?
+ addPropertiesFromUrl(properties, defaultLocationMetadataUrl, false);
+
+ addPropertiesFromFile(properties, globalLocationMetadataFile);
+ addPropertiesFromFile(properties, globalPropertiesFile);
+ addPropertiesFromFile(properties, localPropertiesFile);
+
+ properties.addEnvironmentVars();
+ properties.addSystemProperties();
+
+ return properties;
+ }
+
+ public static Builder fromProperties(BrooklynProperties brooklynProperties) {
+ return new Builder(brooklynProperties);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .omitNullValues()
+ .add("originalProperties", originalProperties)
+ .add("defaultLocationMetadataUrl", defaultLocationMetadataUrl)
+ .add("globalLocationMetadataUrl", globalLocationMetadataFile)
+ .add("globalPropertiesFile", globalPropertiesFile)
+ .add("localPropertiesFile", localPropertiesFile)
+ .toString();
+ }
+ }
+
+ private static void addPropertiesFromUrl(BrooklynProperties p, String url, boolean warnIfNotFound) {
+ if (url==null) return;
+
+ try {
+ p.addFrom(ResourceUtils.create(BrooklynProperties.class).getResourceFromUrl(url));
+ } catch (Exception e) {
+ if (warnIfNotFound)
+ LOG.warn("Could not load {}; continuing", url);
+ if (LOG.isTraceEnabled()) LOG.trace("Could not load "+url+"; continuing", e);
+ }
+ }
+
+ private static void addPropertiesFromFile(BrooklynProperties p, String file) {
+ if (file==null) return;
+
+ String fileTidied = Os.tidyPath(file);
+ File f = new File(fileTidied);
+
+ if (f.exists()) {
+ p.addFrom(f);
+ }
+ }
+ }
+
- protected BrooklynProperties() {
- }
++ public BrooklynProperties addEnvironmentVars();
+
- public BrooklynProperties addEnvironmentVars() {
- addFrom(System.getenv());
- return this;
- }
-
- public BrooklynProperties addSystemProperties() {
- addFrom(System.getProperties());
- return this;
- }
++ public BrooklynProperties addSystemProperties();
+
- public BrooklynProperties addFrom(ConfigBag cfg) {
- addFrom(cfg.getAllConfig());
- return this;
- }
++ public BrooklynProperties addFrom(ConfigBag cfg);
+
- @SuppressWarnings("unchecked")
- public BrooklynProperties addFrom(Map map) {
- putAll(Maps.transformValues(map, StringFunctions.trim()));
- return this;
- }
++ public BrooklynProperties addFrom(Map map);
+
- public BrooklynProperties addFrom(InputStream i) {
- // Ugly way to load them in order, but Properties is a Hashtable so loses order otherwise.
- @SuppressWarnings({ "serial" })
- Properties p = new Properties() {
- @Override
- public synchronized Object put(Object key, Object value) {
- // Trim the string values to remove leading and trailing spaces
- String s = (String) value;
- if (Strings.isBlank(s)) {
- s = Strings.EMPTY;
- } else {
- s = CharMatcher.BREAKING_WHITESPACE.trimFrom(s);
- }
- return BrooklynProperties.this.put(key, s);
- }
- };
- try {
- p.load(i);
- } catch (IOException e) {
- throw Throwables.propagate(e);
- }
- return this;
- }
++ public BrooklynProperties addFrom(InputStream i);
+
- public BrooklynProperties addFrom(File f) {
- if (!f.exists()) {
- LOG.warn("Unable to find file '"+f.getAbsolutePath()+"' when loading properties; ignoring");
- return this;
- } else {
- try {
- return addFrom(new FileInputStream(f));
- } catch (FileNotFoundException e) {
- throw Throwables.propagate(e);
- }
- }
- }
- public BrooklynProperties addFrom(URL u) {
- try {
- return addFrom(u.openStream());
- } catch (IOException e) {
- throw new RuntimeException("Error reading properties from "+u+": "+e, e);
- }
- }
++ public BrooklynProperties addFrom(File f);
++
++ public BrooklynProperties addFrom(URL u);
++
+ /**
+ * @see ResourceUtils#getResourceFromUrl(String)
+ *
+ * of the form form file:///home/... or http:// or classpath://xx ;
+ * for convenience if not starting with xxx: it is treated as a classpath reference or a file;
+ * throws if not found (but does nothing if argument is null)
+ */
- public BrooklynProperties addFromUrl(String url) {
- try {
- if (url==null) return this;
- return addFrom(ResourceUtils.create(this).getResourceFromUrl(url));
- } catch (Exception e) {
- throw new RuntimeException("Error reading properties from "+url+": "+e, e);
- }
- }
++ public BrooklynProperties addFromUrl(String url);
+
+ /** expects a property already set in scope, whose value is acceptable to {@link #addFromUrl(String)};
+ * if property not set, does nothing */
- public BrooklynProperties addFromUrlProperty(String urlProperty) {
- String url = (String) get(urlProperty);
- if (url==null) addFromUrl(url);
- return this;
- }
++ public BrooklynProperties addFromUrlProperty(String urlProperty);
+
+ /**
+ * adds the indicated properties
+ */
- public BrooklynProperties addFromMap(Map properties) {
- putAll(properties);
- return this;
- }
++ public BrooklynProperties addFromMap(Map properties);
+
+ /** inserts the value under the given key, if it was not present */
- public boolean putIfAbsent(String key, Object value) {
- if (containsKey(key)) return false;
- put(key, value);
- return true;
- }
++ public boolean putIfAbsent(String key, Object value);
+
+ /** @deprecated attempts to call get with this syntax are probably mistakes; get(key, defaultValue) is fine but
+ * Map is unlikely the key, much more likely they meant getFirst(flags, key).
+ */
+ @Deprecated
- public String get(Map flags, String key) {
- LOG.warn("Discouraged use of 'BrooklynProperties.get(Map,String)' (ambiguous); use getFirst(Map,String) or get(String) -- assuming the former");
- LOG.debug("Trace for discouraged use of 'BrooklynProperties.get(Map,String)'",
- new Throwable("Arguments: "+flags+" "+key));
- return getFirst(flags, key);
- }
++ public String get(Map flags, String key);
+
+ /** returns the value of the first key which is defined
+ * <p>
+ * takes the following flags:
+ * 'warnIfNone', 'failIfNone' (both taking a boolean (to use default message) or a string (which is the message));
+ * and 'defaultIfNone' (a default value to return if there is no such property); defaults to no warning and null response */
+ @Override
- public String getFirst(String ...keys) {
- return getFirst(MutableMap.of(), keys);
- }
- @Override
- public String getFirst(Map flags, String ...keys) {
- for (String k: keys) {
- if (k!=null && containsKey(k)) return (String) get(k);
- }
- if (flags.get("warnIfNone")!=null && !Boolean.FALSE.equals(flags.get("warnIfNone"))) {
- if (Boolean.TRUE.equals(flags.get("warnIfNone")))
- LOG.warn("Unable to find Brooklyn property "+keys);
- else
- LOG.warn(""+flags.get("warnIfNone"));
- }
- if (flags.get("failIfNone")!=null && !Boolean.FALSE.equals(flags.get("failIfNone"))) {
- Object f = flags.get("failIfNone");
- if (f instanceof Closure)
- ((Closure)f).call((Object[])keys);
- if (Boolean.TRUE.equals(f))
- throw new NoSuchElementException("Brooklyn unable to find mandatory property "+keys[0]+
- (keys.length>1 ? " (or "+(keys.length-1)+" other possible names, full list is "+Arrays.asList(keys)+")" : "") );
- else
- throw new NoSuchElementException(""+f);
- }
- if (flags.get("defaultIfNone")!=null) {
- return (String) flags.get("defaultIfNone");
- }
- return null;
- }
++ public String getFirst(String ...keys);
+
+ @Override
- public String toString() {
- return "BrooklynProperties["+size()+"]";
- }
++ public String getFirst(Map flags, String ...keys);
+
+ /** like normal map.put, except config keys are dereferenced on the way in */
- @SuppressWarnings("unchecked")
- public Object put(Object key, Object value) {
- if (key instanceof HasConfigKey) key = ((HasConfigKey)key).getConfigKey().getName();
- if (key instanceof ConfigKey) key = ((ConfigKey)key).getName();
- return super.put(key, value);
- }
++ public Object put(Object key, Object value);
+
+ /** like normal map.putAll, except config keys are dereferenced on the way in */
+ @Override
- public void putAll(Map vals) {
- for (Map.Entry<?,?> entry : ((Map<?,?>)vals).entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
++ public void putAll(Map vals);
+
- @SuppressWarnings("unchecked")
- public <T> Object put(HasConfigKey<T> key, T value) {
- return super.put(key.getConfigKey().getName(), value);
- }
++ public <T> Object put(HasConfigKey<T> key, T value);
+
- @SuppressWarnings("unchecked")
- public <T> Object put(ConfigKey<T> key, T value) {
- return super.put(key.getName(), value);
- }
++ public <T> Object put(ConfigKey<T> key, T value);
+
- public <T> boolean putIfAbsent(ConfigKey<T> key, T value) {
- return putIfAbsent(key.getName(), value);
- }
++ public <T> boolean putIfAbsent(ConfigKey<T> key, T value);
+
+ @Override
- public <T> T getConfig(ConfigKey<T> key) {
- return getConfig(key, null);
- }
++ public <T> T getConfig(ConfigKey<T> key);
+
+ @Override
- public <T> T getConfig(HasConfigKey<T> key) {
- return getConfig(key.getConfigKey(), null);
- }
++ public <T> T getConfig(HasConfigKey<T> key);
+
+ @Override
- public <T> T getConfig(HasConfigKey<T> key, T defaultValue) {
- return getConfig(key.getConfigKey(), defaultValue);
- }
++ public <T> T getConfig(HasConfigKey<T> key, T defaultValue);
+
+ @Override
- public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
- // TODO does not support MapConfigKey etc where entries use subkey notation; for now, access using submap
- if (!containsKey(key.getName())) {
- if (defaultValue!=null) return defaultValue;
- return key.getDefaultValue();
- }
- Object value = get(key.getName());
- if (value==null) return null;
- // no evaluation / key extraction here
- return TypeCoercions.coerce(value, key.getTypeToken());
- }
++ public <T> T getConfig(ConfigKey<T> key, T defaultValue);
+
+ @Override
- public Object getRawConfig(ConfigKey<?> key) {
- return get(key.getName());
- }
++ public Object getRawConfig(ConfigKey<?> key);
+
+ @Override
- public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
- if (containsKey(key.getName())) return Maybe.of(get(key.getName()));
- return Maybe.absent();
- }
++ public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited);
+
+ @Override
- public Map<ConfigKey<?>, Object> getAllConfig() {
- Map<ConfigKey<?>, Object> result = new LinkedHashMap<ConfigKey<?>, Object>();
- for (Object entry: entrySet())
- result.put(new BasicConfigKey<Object>(Object.class, ""+((Map.Entry)entry).getKey()), ((Map.Entry)entry).getValue());
- return result;
- }
++ public Map<ConfigKey<?>, Object> getAllConfig();
+
+ @Override
- public BrooklynProperties submap(Predicate<ConfigKey<?>> filter) {
- BrooklynProperties result = Factory.newEmpty();
- for (Object entry: entrySet()) {
- ConfigKey<?> k = new BasicConfigKey<Object>(Object.class, ""+((Map.Entry)entry).getKey());
- if (filter.apply(k))
- result.put(((Map.Entry)entry).getKey(), ((Map.Entry)entry).getValue());
- }
- return result;
- }
++ public BrooklynProperties submap(Predicate<ConfigKey<?>> filter);
+
- @SuppressWarnings("unchecked")
+ @Override
- public Map<String, Object> asMapWithStringKeys() {
- return this;
- }
-
++ public Map<String, Object> asMapWithStringKeys();
+ }