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 2017/02/15 18:57:24 UTC

[01/10] brooklyn-server git commit: Add DurationPredicates

Repository: brooklyn-server
Updated Branches:
  refs/heads/master 39301e0f7 -> 110482861


Add DurationPredicates


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/070c6f54
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/070c6f54
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/070c6f54

Branch: refs/heads/master
Commit: 070c6f54abb0c689e55656661f7f699c14c571e3
Parents: 6571bab
Author: Mike Zaccardo <mi...@cloudsoftcorp.com>
Authored: Thu Feb 9 11:16:29 2017 -0500
Committer: Mike Zaccardo <mi...@cloudsoftcorp.com>
Committed: Thu Feb 9 16:52:20 2017 -0500

----------------------------------------------------------------------
 .../brooklyn/util/time/DurationPredicates.java  | 162 +++++++++++++++++++
 .../util/time/DurationPredicatesTest.java       | 150 +++++++++++++++++
 2 files changed, 312 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/070c6f54/utils/common/src/main/java/org/apache/brooklyn/util/time/DurationPredicates.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/time/DurationPredicates.java b/utils/common/src/main/java/org/apache/brooklyn/util/time/DurationPredicates.java
new file mode 100644
index 0000000..162f6f5
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/time/DurationPredicates.java
@@ -0,0 +1,162 @@
+/*
+ * 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.util.time;
+
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Stopwatch;
+
+public class DurationPredicates {
+
+    /**
+     * @return A {@link Predicate} that checks if a {@link Duration} supplied to
+     *         {@link Predicate#apply(Object)} is positive.
+     */
+    public static Predicate<Duration> positive() {
+        return new Positive();
+    }
+
+    protected static class Positive implements Predicate<Duration> {
+        @Override
+        public boolean apply(Duration input) {
+            return input != null && input.isPositive();
+        }
+    }
+
+    /**
+     * @return A {@link Predicate} that checks if a {@link Duration} supplied to
+     *         {@link Predicate#apply(Object)} is negative.
+     */
+    public static Predicate<Duration> negative() {
+        return new Negative();
+    }
+
+    protected static class Negative implements Predicate<Duration> {
+        @Override
+        public boolean apply(Duration input) {
+            return input != null && input.isNegative();
+        }
+    }
+
+    /**
+     * @param duration
+     *            The {@link Duration} that will be the basis for comparison in
+     *            the returned {@link Predicate}.
+     * @return A {@link Predicate} that checks if a {@link Duration} supplied to
+     *         {@link Predicate#apply(Object)} is longer than the
+     *         {@link Duration} that was supplied to this method.
+     */
+    public static Predicate<Duration> longerThan(final Duration duration) {
+        return new LongerThan(duration);
+    }
+
+    protected static class LongerThan implements Predicate<Duration> {
+        private final Duration value;
+
+        protected LongerThan(Duration value) {
+            Preconditions.checkNotNull(value);
+            this.value = value;
+        }
+
+        @Override
+        public boolean apply(Duration input) {
+            return input != null && input.isLongerThan(value);
+        }
+    }
+
+    /**
+     * @param duration
+     *            The {@link Duration} that will be the basis for comparison in
+     *            the returned {@link Predicate}.
+     * @return A {@link Predicate} that checks if a {@link Duration} supplied to
+     *         {@link Predicate#apply(Object)} is shorter than the
+     *         {@link Duration} that was supplied to this method.
+     */
+    public static Predicate<Duration> shorterThan(final Duration duration) {
+        return new ShorterThan(duration);
+    }
+
+    protected static class ShorterThan implements Predicate<Duration> {
+        private final Duration value;
+
+        protected ShorterThan(Duration value) {
+            Preconditions.checkNotNull(value);
+            this.value = value;
+        }
+
+        @Override
+        public boolean apply(Duration input) {
+            return input != null && input.isShorterThan(value);
+        }
+    }
+
+    /**
+     * @param duration
+     *            The {@link Duration} that will be the basis for comparison in
+     *            the returned {@link Predicate}.
+     * @return A {@link Predicate} that checks if a {@link Stopwatch} supplied to
+     *         {@link Predicate#apply(Object)} is longer than the
+     *         {@link Duration} that was supplied to this method.
+     */
+    public static Predicate<Stopwatch> longerThanDuration(final Duration duration) {
+        return new LongerThanDuration(duration);
+    }
+
+    protected static class LongerThanDuration implements Predicate<Stopwatch> {
+        private final Duration value;
+
+        protected LongerThanDuration(Duration value) {
+            Preconditions.checkNotNull(value);
+            this.value = value;
+        }
+
+        @Override
+        public boolean apply(Stopwatch input) {
+            return input != null && Duration.millis(input.elapsed(TimeUnit.MILLISECONDS)).isLongerThan(value);
+        }
+    }
+
+    /**
+     * @param duration
+     *            The {@link Duration} that will be the basis for comparison in
+     *            the returned {@link Predicate}.
+     * @return A {@link Predicate} that checks if a {@link Stopwatch} supplied to
+     *         {@link Predicate#apply(Object)} is shorter than the
+     *         {@link Duration} that was supplied to this method.
+     */
+    public static Predicate<Stopwatch> shorterThanDuration(final Duration duration) {
+        return new ShorterThanDuration(duration);
+    }
+
+    protected static class ShorterThanDuration implements Predicate<Stopwatch> {
+        private final Duration value;
+
+        protected ShorterThanDuration(Duration value) {
+            Preconditions.checkNotNull(value);
+            this.value = value;
+        }
+
+        @Override
+        public boolean apply(Stopwatch input) {
+            return input != null && Duration.millis(input.elapsed(TimeUnit.MILLISECONDS)).isShorterThan(value);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/070c6f54/utils/common/src/test/java/org/apache/brooklyn/util/time/DurationPredicatesTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/time/DurationPredicatesTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/time/DurationPredicatesTest.java
new file mode 100644
index 0000000..39d4184
--- /dev/null
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/time/DurationPredicatesTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.util.time;
+
+import static org.testng.Assert.assertEquals;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class DurationPredicatesTest {
+
+    private static final List<Duration> TEST_DURATIONS_POSITIVE = Lists.newArrayList(null,
+            Duration.minutes(1), Duration.minutes(2), Duration.minutes(5), Duration.minutes(10));
+
+    private static final List<Duration> TEST_DURATIONS_NEGATIVE = Lists.newArrayList(null,
+            Duration.minutes(-1), Duration.minutes(-2), Duration.minutes(-5), Duration.minutes(-10));
+
+    private static final List<Stopwatch> TEST_STOPWATCHES = new ArrayList<>(TEST_DURATIONS_POSITIVE.size());
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        for (Duration duration : TEST_DURATIONS_POSITIVE) {
+            TEST_STOPWATCHES.add(createStopwatchWithElapsedTime(duration));
+        }
+    }
+
+    @Test
+    public void testPositive() {
+        Iterable<Duration> result = Iterables.filter(TEST_DURATIONS_POSITIVE, DurationPredicates.positive());
+        assertEquals(Iterables.size(result), Iterables.size(TEST_DURATIONS_POSITIVE) - 1);
+
+        result = Iterables.filter(TEST_DURATIONS_NEGATIVE, DurationPredicates.positive());
+        assertEquals(Iterables.size(result), 0);
+    }
+
+    @Test
+    public void testNegative() {
+        Iterable<Duration> result = Iterables.filter(TEST_DURATIONS_POSITIVE, DurationPredicates.negative());
+        assertEquals(Iterables.size(result), 0);
+
+        result = Iterables.filter(TEST_DURATIONS_NEGATIVE, DurationPredicates.negative());
+        assertEquals(Iterables.size(result), Iterables.size(TEST_DURATIONS_NEGATIVE) - 1);
+    }
+
+    @Test
+    public void testLongerThan() {
+        Duration testDuration = Duration.minutes(3);
+
+        Iterable<Duration> result = Iterables.filter(TEST_DURATIONS_POSITIVE,
+                DurationPredicates.longerThan(testDuration));
+        assertEquals(Iterables.size(result), 2);
+
+        result = Iterables.filter(TEST_DURATIONS_NEGATIVE, DurationPredicates.longerThan(testDuration));
+        assertEquals(Iterables.size(result), 0);
+
+        testDuration = Duration.minutes(-3);
+
+        result = Iterables.filter(TEST_DURATIONS_POSITIVE, DurationPredicates.longerThan(testDuration));
+        assertEquals(Iterables.size(result), 4);
+
+        result = Iterables.filter(TEST_DURATIONS_NEGATIVE, DurationPredicates.longerThan(testDuration));
+        assertEquals(Iterables.size(result), 2);
+    }
+
+    @Test
+    public void testShorterThan() {
+        Duration testDuration = Duration.minutes(3);
+
+        Iterable<Duration> result = Iterables.filter(TEST_DURATIONS_POSITIVE,
+                DurationPredicates.shorterThan(testDuration));
+        assertEquals(Iterables.size(result), 2);
+
+        result = Iterables.filter(TEST_DURATIONS_NEGATIVE, DurationPredicates.shorterThan(testDuration));
+        assertEquals(Iterables.size(result), 4);
+
+        testDuration = Duration.minutes(-3);
+
+        result = Iterables.filter(TEST_DURATIONS_POSITIVE, DurationPredicates.shorterThan(testDuration));
+        assertEquals(Iterables.size(result), 0);
+
+        result = Iterables.filter(TEST_DURATIONS_NEGATIVE, DurationPredicates.shorterThan(testDuration));
+        assertEquals(Iterables.size(result), 2);
+    }
+
+    @Test
+    public void testLongerThanDuration() {
+        Duration testDuration = Duration.minutes(3);
+
+        Iterable<Stopwatch> result = Iterables.filter(TEST_STOPWATCHES,
+                DurationPredicates.longerThanDuration(testDuration));
+        assertEquals(Iterables.size(result), 2);
+
+        testDuration = Duration.minutes(-3);
+
+        result = Iterables.filter(TEST_STOPWATCHES, DurationPredicates.longerThanDuration(testDuration));
+        assertEquals(Iterables.size(result), 4);
+    }
+
+    @Test
+    public void testShorterThanDuration() {
+        Duration testDuration = Duration.minutes(3);
+
+        Iterable<Stopwatch> result = Iterables.filter(TEST_STOPWATCHES,
+                DurationPredicates.shorterThanDuration(testDuration));
+        assertEquals(Iterables.size(result), 2);
+
+        testDuration = Duration.minutes(-3);
+
+        result = Iterables.filter(TEST_STOPWATCHES, DurationPredicates.shorterThanDuration(testDuration));
+        assertEquals(Iterables.size(result), 0);
+    }
+
+    private static Stopwatch createStopwatchWithElapsedTime(Duration duration) throws Exception {
+        if (duration == null) {
+            return null;
+        }
+
+        Stopwatch stopwatch = Stopwatch.createUnstarted();
+
+        Field field = stopwatch.getClass().getDeclaredField("elapsedNanos");
+        field.setAccessible(true);
+        field.set(stopwatch, duration.nanos());
+
+        return stopwatch;
+    }
+}


[08/10] brooklyn-server git commit: fix merge errors

Posted by he...@apache.org.
fix merge errors


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/1871a521
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/1871a521
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/1871a521

Branch: refs/heads/master
Commit: 1871a5219f2a25e172e2b5367315aef55bb79457
Parents: 8809d5c
Author: Andrea Turli <an...@gmail.com>
Authored: Mon Feb 13 15:39:39 2017 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Tue Feb 14 12:35:13 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/camp/brooklyn/AbstractYamlRebindTest.java   | 11 -----------
 .../core/effector/http/HttpCommandEffectorTest.java      |  2 +-
 2 files changed, 1 insertion(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1871a521/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
index 9168b25..0d6d5c2 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
@@ -29,14 +29,7 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.Task;
-<<<<<<< 3d318e4e78085519233bcc763e22320ca40403df
-import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
-import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.CampTypePlanTransformer;
-=======
->>>>>>> add more tests for HttpCommandEffector
-import org.apache.brooklyn.camp.spi.Assembly;
-import org.apache.brooklyn.camp.spi.AssemblyTemplate;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.StartableApplication;
@@ -48,7 +41,6 @@ import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixture;
 import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
 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.stream.Streams;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -56,9 +48,6 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 
 import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
 
 public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplication> {
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1871a521/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
index 0580c39..8d620a4 100644
--- a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
@@ -202,7 +202,7 @@ public class HttpCommandEffectorTest extends BrooklynAppUnitTestSupport {
       Object output = testEntity.invoke(EFFECTOR_HTTP_COMMAND, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
       assertEquals(output, "[\"key\", \"value\"]");
 
-      assertEquals(server.getRequestCount(), "[\"key\", \"value\"]");
+      assertEquals(server.getRequestCount(), 1);
       assertSent(server, "POST", "/post");
    }
 


[03/10] brooklyn-server git commit: fix CompositeEffector logic

Posted by he...@apache.org.
fix CompositeEffector logic


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/119ba5a4
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/119ba5a4
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/119ba5a4

Branch: refs/heads/master
Commit: 119ba5a47b4a5198ae99927511e35107d2ebce89
Parents: eadec9a
Author: Andrea Turli <an...@gmail.com>
Authored: Mon Jan 23 17:12:14 2017 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Feb 13 15:09:31 2017 +0100

----------------------------------------------------------------------
 .../core/effector/CompositeEffector.java         | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/119ba5a4/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
index 88cd92c..ca627bb 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
@@ -36,9 +36,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -95,19 +95,22 @@ public class CompositeEffector extends AddEffector {
 
             List <Object> results = Lists.newArrayList();
             if (!override) {
-                List<Effector<?>> originalEffectors = FluentIterable.from(entity().getEntityType().getEffectors())
-                        .filter(new Predicate<Effector<?>>() {
+                Optional<Effector<?>> effectorOptional = Iterables.tryFind(entity().getEntityType().getEffectors(), new Predicate<Effector<?>>() {
                             @Override
                             public boolean apply(@Nullable Effector<?> input) {
                                 return input.getName().equals("original-" + effector.getName());
                             }
-                        })
-                        .toList();
-
-                for (Effector<?> originalEffector : originalEffectors) {
-                    results.add(invokeEffectorNamed(originalEffector.getName(), params));
+                        });
+                // if it is a stop effector, it has to be executed as last effector
+                if (effectorOptional.isPresent()) {
+                    if (effectorOptional.get().getName().endsWith("-stop")) {
+                        effectorNames.add(effectorOptional.get().getName());
+                    } else {
+                        effectorNames.add(0, effectorOptional.get().getName());
+                    }
                 }
             }
+            
             for (String eff : effectorNames) {
                 results.add(invokeEffectorNamed(eff, params));
             }


[10/10] brooklyn-server git commit: This closes #531

Posted by he...@apache.org.
This closes #531


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/11048286
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/11048286
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/11048286

Branch: refs/heads/master
Commit: 1104828610886737323dae9a91d7a2d760cb923a
Parents: fd03312 1871a52
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Feb 15 18:57:12 2017 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Wed Feb 15 18:57:12 2017 +0000

----------------------------------------------------------------------
 .../camp/brooklyn/AbstractYamlRebindTest.java   |  26 +-
 .../CompositeEffectorYamlRebindTest.java        | 102 +++++++
 .../brooklyn/CompositeEffectorYamlTest.java     |  79 ++++++
 .../HttpCommandEffectorYamlRebindTest.java      |  92 +++++++
 .../brooklyn/HttpCommandEffectorYamlTest.java   |  79 ++++++
 .../brooklyn/core/effector/AddSensor.java       |   2 +
 .../core/effector/CompositeEffector.java        | 135 +++++++++
 .../brooklyn/core/effector/Effectors.java       |   4 +
 .../core/effector/http/HttpCommandEffector.java | 209 ++++++++++++++
 .../core/entity/EntityInitializers.java         |  32 ++-
 .../core/sensor/http/HttpRequestSensor.java     |  47 ++--
 .../CompositeEffectorIntegrationTest.java       |  91 +++++++
 .../core/effector/CompositeEffectorTest.java    | 262 ++++++++++++++++++
 .../HttpCommandEffectorIntegrationTest.java     | 125 +++++++++
 .../effector/http/HttpCommandEffectorTest.java  | 272 +++++++++++++++++++
 .../core/effector/http/int-response.json        |  16 ++
 .../core/effector/http/list-response.json       |  19 ++
 .../brooklyn/core/effector/http/login.json      |  16 ++
 .../core/effector/http/map-response.json        |  16 ++
 .../org/apache/brooklyn/core/effector/test.json |  16 ++
 .../apache/brooklyn/rest/api/EffectorApi.java   |  28 +-
 21 files changed, 1619 insertions(+), 49 deletions(-)
----------------------------------------------------------------------



[06/10] brooklyn-server git commit: add more tests for HttpCommandEffector

Posted by he...@apache.org.
add more tests for HttpCommandEffector

- add unit test (HttpCommandEffectorTest)
- add YamlTest (HttpCommandEffectorYamlTest)
- add YamlRebindTest (HttpCommandEffectorYamlRebindTest)


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/b0b83da7
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/b0b83da7
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/b0b83da7

Branch: refs/heads/master
Commit: b0b83da785944b3929866a89470b52cd8215684f
Parents: 3d318e4
Author: Andrea Turli <an...@gmail.com>
Authored: Tue Jan 31 16:51:05 2017 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Feb 13 15:16:02 2017 +0100

----------------------------------------------------------------------
 .../camp/brooklyn/AbstractYamlRebindTest.java   |  21 ++-
 .../HttpCommandEffectorYamlRebindTest.java      |  92 +++++++++
 .../brooklyn/HttpCommandEffectorYamlTest.java   |  79 ++++++++
 .../core/effector/CompositeEffector.java        |  16 +-
 .../core/effector/http/HttpCommandEffector.java | 105 ++++++++---
 .../HttpCommandEffectorIntegrationTest.java     |  42 ++---
 .../effector/http/HttpCommandEffectorTest.java  | 188 +++++++++++++++++++
 .../brooklyn/core/effector/http/login.json      |  16 ++
 8 files changed, 490 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
index 9322866..9168b25 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlRebindTest.java
@@ -29,9 +29,12 @@ import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.Task;
+<<<<<<< 3d318e4e78085519233bcc763e22320ca40403df
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatform;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer;
 import org.apache.brooklyn.camp.brooklyn.spi.creation.CampTypePlanTransformer;
+=======
+>>>>>>> add more tests for HttpCommandEffector
 import org.apache.brooklyn.camp.spi.Assembly;
 import org.apache.brooklyn.camp.spi.AssemblyTemplate;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
@@ -59,13 +62,13 @@ import com.google.common.collect.Iterables;
 
 public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplication> {
 
-    private static final Logger LOG = LoggerFactory.getLogger(AbstractYamlTest.class);
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractYamlRebindTest.class);
     protected static final String TEST_VERSION = "0.1.2";
 
     protected BrooklynCampPlatform platform;
     protected BrooklynCampPlatformLauncherNoServer launcher;
     private boolean forceUpdate;
-    
+
     @BeforeMethod(alwaysRun = true)
     @Override
     public void setUp() throws Exception {
@@ -106,7 +109,7 @@ public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplicati
         }
         return result;
     }
-    
+
     @Override
     protected StartableApplication createApp() {
         return null;
@@ -116,11 +119,11 @@ public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplicati
     protected ManagementContext mgmt() {
         return (newManagementContext != null) ? newManagementContext : origManagementContext;
     }
-    
+
     ///////////////////////////////////////////////////
     // TODO code below is duplicate of AbstractYamlTest
     ///////////////////////////////////////////////////
-    
+
     protected void waitForApplicationTasks(Entity app) {
         Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(mgmt().getExecutionManager(), app);
         getLogger().info("Waiting on " + tasks.size() + " task(s)");
@@ -149,7 +152,7 @@ public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplicati
     protected Entity createAndStartApplication(String... multiLineYaml) throws Exception {
         return createAndStartApplication(joinLines(multiLineYaml));
     }
-    
+
     protected Entity createAndStartApplication(String input) throws Exception {
         return createAndStartApplication(input, MutableMap.<String,String>of());
     }
@@ -168,18 +171,18 @@ public class AbstractYamlRebindTest extends RebindTestFixture<StartableApplicati
     protected Entity createStartWaitAndLogApplication(String... input) throws Exception {
         return createStartWaitAndLogApplication(joinLines(input));
     }
-    
+
     protected Entity createStartWaitAndLogApplication(String input) throws Exception {
         return createStartWaitAndLogApplication(new StringReader(input));
     }
-    
+
     protected Entity createStartWaitAndLogApplication(Reader input) throws Exception {
         Entity app = createAndStartApplication(input);
         waitForApplicationTasks(app);
 
         getLogger().info("App started:");
         Entities.dumpInfo(app);
-        
+
         return app;
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlRebindTest.java
new file mode 100644
index 0000000..df57bcb
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlRebindTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import static org.apache.brooklyn.test.Asserts.assertFalse;
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.effector.http.HttpCommandEffector;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+@Test
+public class HttpCommandEffectorYamlRebindTest extends AbstractYamlRebindTest {
+
+   private final static String appId = "my-app-with-http-effector";
+   private final static String appVersion = "1.0.0-SNAPSHOT";
+   static final String appVersionedId = appId + ":" + appVersion;
+
+   static final String catalogYamlSimple = Joiner.on("\n").join(
+           "brooklyn.catalog:",
+           "  id: " + appId,
+           "  version: " + appVersion,
+           "  itemType: entity",
+           "  item:",
+           "    type: " + TestEntity.class.getName(),
+           "    name: targetEntity",
+           "    brooklyn.initializers:",
+           "      - type: " + HttpCommandEffector.class.getName(),
+           "        brooklyn.config:",
+           "          name: myEffector",
+           "          description: myDescription",
+           "          uri: https://httpbin.org/get?id=myId",
+           "          httpVerb: GET",
+           "          jsonPath: $.args.id",
+           "          publishSensor: results");
+
+   @Test
+   public void testRebindWhenHealthy() throws Exception {
+      runRebindWhenIsUp(catalogYamlSimple, appVersionedId);
+   }
+
+   protected void runRebindWhenIsUp(String catalogYaml, String appId) throws Exception {
+      addCatalogItems(catalogYaml);
+
+      String appYaml = Joiner.on("\n").join(
+              "services: ",
+              "- type: " + appId);
+      createStartWaitAndLogApplication(appYaml);
+
+      // Rebind
+      StartableApplication newApp = rebind();
+      TestEntity testEntity = (TestEntity) Iterables.find(newApp.getChildren(), EntityPredicates.displayNameEqualTo("targetEntity"));
+      Effector effector = assertHasInitializers(testEntity, "myEffector");
+
+      // Confirm HttpCommandEffector still functions
+      Object result = testEntity.invoke(effector, ImmutableMap.<String, Object>of()).get();
+      assertEquals(((String)result).trim(), "myId");
+   }
+
+
+   protected static Effector<?> assertHasInitializers(Entity entity, String effectorName) {
+      Maybe<Effector<?>> effectorMaybe = entity.getEntityType().getEffectorByName(effectorName);
+      assertFalse(effectorMaybe.isAbsent());
+      return effectorMaybe.get();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlTest.java
new file mode 100644
index 0000000..b36771c
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/HttpCommandEffectorYamlTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.effector.http.HttpCommandEffector;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class HttpCommandEffectorYamlTest extends AbstractYamlTest {
+    private static final Logger log = LoggerFactory.getLogger(HttpCommandEffectorYamlTest.class);
+
+    @Test
+    public void testHttpCommandEffectorWithParameters() throws Exception {
+        Entity app = createAndStartApplication(
+            "location: localhost",
+            "services:",
+            "- type: " + EmptySoftwareProcess.class.getName(),
+            "  brooklyn.config:",
+            "    onbox.base.dir.skipResolution: true",
+            "    softwareProcess.serviceProcessIsRunningPollPeriod: forever",
+            "  brooklyn.initializers:",
+            "  - type: " + HttpCommandEffector.class.getName(),
+            "    brooklyn.config:",
+            "      name: myEffector",
+            "      description: myDescription",
+            "      uri: https://httpbin.org/get?id=myId",
+            "      httpVerb: GET",
+            "      jsonPath: $.args.id",
+            "      publishSensor: results"
+        );
+        waitForApplicationTasks(app);
+
+        EmptySoftwareProcess entity = (EmptySoftwareProcess) Iterables.getOnlyElement(app.getChildren());
+        Effector<?> effector = entity.getEntityType().getEffectorByName("myEffector").get();
+
+        // Invoke with parameters
+        {
+            Object result = entity.invoke(effector, ImmutableMap.of("uri", "https://httpbin.org/get?pwd=passwd", "jsonPath", "$.args.pwd")).get();
+            assertEquals(((String)result).trim(), "passwd");
+
+        }
+
+        // Invoke with default parameter
+        {
+            Object result = entity.invoke(effector, ImmutableMap.<String, Object>of()).get();
+            assertEquals(((String)result).trim(), "myId");
+        }
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return log;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
index ca627bb..32f0f1d 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
@@ -93,14 +93,14 @@ public class CompositeEffector extends AddEffector {
             final List<String> effectorNames = EntityInitializers.resolve(allConfig, EFFECTORS);
             final Boolean override = allConfig.get(OVERRIDE);
 
-            List <Object> results = Lists.newArrayList();
+            List<Object> results = Lists.newArrayList();
             if (!override) {
                 Optional<Effector<?>> effectorOptional = Iterables.tryFind(entity().getEntityType().getEffectors(), new Predicate<Effector<?>>() {
-                            @Override
-                            public boolean apply(@Nullable Effector<?> input) {
-                                return input.getName().equals("original-" + effector.getName());
-                            }
-                        });
+                    @Override
+                    public boolean apply(@Nullable Effector<?> input) {
+                        return input.getName().equals("original-" + effector.getName());
+                    }
+                });
                 // if it is a stop effector, it has to be executed as last effector
                 if (effectorOptional.isPresent()) {
                     if (effectorOptional.get().getName().endsWith("-stop")) {
@@ -110,7 +110,7 @@ public class CompositeEffector extends AddEffector {
                     }
                 }
             }
-            
+
             for (String eff : effectorNames) {
                 results.add(invokeEffectorNamed(eff, params));
             }
@@ -125,7 +125,7 @@ public class CompositeEffector extends AddEffector {
                 // TODO
             }
             return entity().invoke(effector.get(), params.getAllConfig()).getUnchecked();
-            
+
         }
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
index d2130dd..2d8266e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
@@ -18,9 +18,14 @@
  */
 package org.apache.brooklyn.core.effector.http;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
@@ -45,8 +50,12 @@ import org.apache.brooklyn.util.http.executor.HttpResponse;
 import org.apache.brooklyn.util.http.executor.UsernamePassword;
 import org.apache.brooklyn.util.http.executor.apacheclient.HttpExecutorImpl;
 
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.io.ByteStreams;
+import com.google.common.net.HttpHeaders;
 import com.jayway.jsonpath.JsonPath;
 
 public final class HttpCommandEffector extends AddEffector {
@@ -60,6 +69,10 @@ public final class HttpCommandEffector extends AddEffector {
     public static final ConfigKey<String> JSON_PATH = ConfigKeys.newStringConfigKey("jsonPath", "JSON path to select in HTTP response");
     public static final ConfigKey<String> PUBLISH_SENSOR = ConfigKeys.newStringConfigKey("publishSensor", "Sensor name where to store json path extracted value");
 
+    private enum HttpVerb {
+        GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
+    }
+
     public HttpCommandEffector(ConfigBag params) {
         super(newEffectorBuilder(params).build());
     }
@@ -69,7 +82,7 @@ public final class HttpCommandEffector extends AddEffector {
         eff.impl(new Body(eff.buildAbstract(), params));
         return eff;
     }
-    
+
     protected static class Body extends EffectorBody<String> {
         private final Effector<?> effector;
         private final ConfigBag params;
@@ -97,32 +110,12 @@ public final class HttpCommandEffector extends AddEffector {
                 public Object call() throws Exception {
                     HttpExecutor httpExecutor = HttpExecutorImpl.newInstance();
 
-                    String body = "";
-                    if (payload != null && !payload.isEmpty() && headers.containsKey("Content-Type")) {
-                        body = Jsonya.newInstance().put(payload).toString();
-                    }
-                    HttpRequest.Builder httpRequestBuilder = new HttpRequest.Builder()
-                            .body(body.getBytes())
-                            .uri(URI.create(uri))
-                            .method(httpVerb)
-                            .config(HttpConfig.builder()
-                                    .trustSelfSigned(true)
-                                    .trustAll(true)
-                                    .laxRedirect(true)
-                                    .build());
-
-                    if (headers != null) {
-                        httpRequestBuilder.headers(headers);
-                    }
-
-                    if (httpUsername != null && httpPassword != null) {
-                        httpRequestBuilder.credentials(new UsernamePassword(httpUsername, httpPassword));
-                    }
-                    
-                    HttpRequest httpRequest = httpRequestBuilder.build();
+                    String body = getBodyFromPayload(payload, headers);
+                    HttpRequest request = buildHttpRequest(httpVerb, uri, headers, httpUsername, httpPassword, body);
                     ByteArrayOutputStream out = new ByteArrayOutputStream();
                     try {
-                        HttpResponse response = httpExecutor.execute(httpRequest);
+                        HttpResponse response = httpExecutor.execute(request);
+                        validateResponse(response);
                         ByteStreams.copy(response.getContent(), out);
                         return new String(out.toByteArray());
                     } catch (IOException e) {
@@ -131,14 +124,64 @@ public final class HttpCommandEffector extends AddEffector {
                 }
             }).build();
 
-            String val = (String) queue(t).getUnchecked();
-            if (jsonPath != null) {
-                String extractedValue = JsonPath.parse(val).read(jsonPath, String.class);
+            String responseBody = (String) queue(t).getUnchecked();
+
+            if (jsonPath == null) return responseBody;
+            String extractedValue = JsonPath.parse(responseBody).read(jsonPath, String.class);
+            if (publishSensor != null) {
                 entity().sensors().set(Sensors.newStringSensor(publishSensor), extractedValue);
-                return extractedValue;
-            } else {
-                return val;
             }
+            return extractedValue;
+        }
+
+        private void validateResponse(HttpResponse response) {
+            int statusCode = response.code();
+            if (statusCode == 401) {
+                throw new RuntimeException("Authorization exception");
+            } else if (statusCode == 404) {
+                throw new RuntimeException("Resource not found");
+            } else if (statusCode >= 500) {
+                throw new RuntimeException("Server error");
+            }
+        }
+
+        private HttpRequest buildHttpRequest(String httpVerb, String url, Map<String, String> headers, String httpUsername, String httpPassword, String body) throws MalformedURLException, URISyntaxException {
+            // validate url string
+            URI uri = new URL(url).toURI();
+            // validate HTTP verb
+            validateHttpVerb(httpVerb);
+            HttpRequest.Builder httpRequestBuilder = new HttpRequest.Builder()
+                    .body(body.getBytes())
+                    .uri(uri)
+                    .method(httpVerb)
+                    .config(HttpConfig.builder()
+                            .trustSelfSigned(true)
+                            .trustAll(true)
+                            .laxRedirect(true)
+                            .build());
+
+            if (headers != null) {
+                httpRequestBuilder.headers(headers);
+            }
+
+            if (httpUsername != null && httpPassword != null) {
+                httpRequestBuilder.credentials(new UsernamePassword(httpUsername, httpPassword));
+            }
+
+            return httpRequestBuilder.build();
+        }
+
+        private void validateHttpVerb(String httpVerb) {
+            Optional<HttpVerb> state = Enums.getIfPresent(HttpVerb.class, httpVerb.toUpperCase());
+            checkArgument(state.isPresent(), "Expected one of %s but was %s", Joiner.on(',').join(HttpVerb.values()), httpVerb);
+        }
+
+        private String getBodyFromPayload(Map<String, Object> payload, Map<String, String> headers) {
+            String body = "";
+            if (payload != null && !payload.isEmpty() && headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
+                body = Jsonya.newInstance().put(payload).toString();
+            }
+            return body;
         }
 
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java
index 080e08a..7d1a91a 100644
--- a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java
@@ -40,7 +40,7 @@ import com.jayway.jsonpath.JsonPath;
 
 public class HttpCommandEffectorIntegrationTest {
 
-    final static Effector<String> EFFECTOR_GITHUB_APACHE_ACCOUNT = Effectors.effector(String.class, "GithubApacheAccount").buildAbstract();
+    final static Effector<String> EFFECTOR_HTTPBIN = Effectors.effector(String.class, "Httpbin").buildAbstract();
 
     private TestApplication app;
     private EntityLocal entity;
@@ -60,20 +60,20 @@ public class HttpCommandEffectorIntegrationTest {
     @Test(groups="Integration")
     public void testHttpEffector() throws Exception {
         new HttpCommandEffector(ConfigBag.newInstance()
-                .configure(HttpCommandEffector.EFFECTOR_NAME, "GithubApacheAccount")
-                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/apache")
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "Httpbin")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://httpbin.org/get?login=myLogin")
                 .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
         ).apply(entity);
 
-        String val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.<String,String>of()).get();
-        Assert.assertEquals(JsonPath.parse(val).read("$.login", String.class), "apache");
+        String val = entity.invoke(EFFECTOR_HTTPBIN, MutableMap.<String,String>of()).get();
+        Assert.assertEquals(JsonPath.parse(val).read("$.args.login", String.class), "myLogin");
     }
 
     @Test(groups="Integration")
     public void testHttpEffectorWithPayload() throws Exception {
         new HttpCommandEffector(ConfigBag.newInstance()
-                .configure(HttpCommandEffector.EFFECTOR_NAME, "CreateGist")
-                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/gists")
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "HttpbinPost")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://httpbin.org/post")
                 .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "POST")
                 .configure(HttpCommandEffector.EFFECTOR_HTTP_PAYLOAD, ImmutableMap.<String, Object>of(
                         "description", "Created via API", 
@@ -84,42 +84,42 @@ public class HttpCommandEffectorIntegrationTest {
                 .configure(HttpCommandEffector.PUBLISH_SENSOR, "result")
         ).apply(entity);
 
-        String url = entity.invoke(Effectors.effector(String.class, "CreateGist").buildAbstract(), MutableMap.<String,String>of()).get();
+        String url = entity.invoke(Effectors.effector(String.class, "HttpbinPost").buildAbstract(), MutableMap.<String,String>of()).get();
         Assert.assertNotNull(url, "url");
     }
 
     @Test(groups="Integration")
     public void testHttpEffectorWithJsonPath() throws Exception {
         new HttpCommandEffector(ConfigBag.newInstance()
-                .configure(HttpCommandEffector.EFFECTOR_NAME, "GithubApacheAccount")
-                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/apache")
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "Httpbin")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://httpbin.org/get?id=myId")
                 .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
-                .configure(HttpCommandEffector.JSON_PATH, "$.login")
+                .configure(HttpCommandEffector.JSON_PATH, "$.args.id")
                 .configure(HttpCommandEffector.PUBLISH_SENSOR, "result")
         ).apply(entity);
 
-        String val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.<String,String>of()).get();
-        Assert.assertEquals(val, "apache");
-        Assert.assertEquals(entity.sensors().get(Sensors.newStringSensor("result")), "apache");
+        String val = entity.invoke(EFFECTOR_HTTPBIN, MutableMap.<String,String>of()).get();
+        Assert.assertEquals(val, "myId");
+        Assert.assertEquals(entity.sensors().get(Sensors.newStringSensor("result")), "myId");
     }
     
     @Test(groups="Integration")
     public void testHttpEffectorWithParameters() throws Exception {
         new HttpCommandEffector(ConfigBag.newInstance()
-                .configure(HttpCommandEffector.EFFECTOR_NAME, "GithubApacheAccount")
-                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/$user")
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "Httpbin")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://httpbin.org/get")                
                 .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
                 .configure(HttpCommandEffector.EFFECTOR_PARAMETER_DEFS,
-                        MutableMap.<String,Object>of("user", MutableMap.of("defaultValue", "apache"))))
+                        MutableMap.<String,Object>of("uri", MutableMap.of("defaultValue", "https://httpbin.org/get"))))
                 .apply(entity);
 
         String val;
         // explicit value
-        val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.of("user", "github")).get();
-        Assert.assertEquals(JsonPath.parse(val).read("$.login", String.class), "github");
+        val = entity.invoke(EFFECTOR_HTTPBIN, MutableMap.of("uri", "https://httpbin.org/ip")).get();
+        Assert.assertNotNull(JsonPath.parse(val).read("$.origin", String.class));
 
         // default value
-        val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.<String,String>of()).get();
-        Assert.assertEquals(JsonPath.parse(val).read("$.login", String.class), "apache");
+        val = entity.invoke(EFFECTOR_HTTPBIN, MutableMap.<String,String>of()).get();
+        Assert.assertEquals(JsonPath.parse(val).read("$.url", String.class), "https://httpbin.org/get");
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
new file mode 100644
index 0000000..a4b8ee0
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.effector.http;
+
+import static org.apache.brooklyn.test.Asserts.assertNotNull;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.effector.Effectors;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.http.BetterMockWebServer;
+import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Resources;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.RecordedRequest;
+
+public class HttpCommandEffectorTest extends BrooklynAppUnitTestSupport {
+
+    private static final Logger log = LoggerFactory.getLogger(HttpCommandEffectorTest.class);
+    private static final String DEFAULT_ENDPOINT = "/";
+
+    final static Effector<String> EFFECTOR_HTTP_COMMAND = Effectors.effector(String.class, "http-command-effector").buildAbstract();
+
+    protected BetterMockWebServer server;
+    protected URL baseUrl;
+
+    protected Location loc;
+    protected HttpCommandEffector httpCommandEffector;
+
+   @BeforeMethod
+   public void start() throws IOException {
+      server = BetterMockWebServer.newInstanceLocalhost();
+      server.play();
+   }
+
+   @AfterMethod(alwaysRun = true)
+   public void stop() throws IOException {
+      server.shutdown();
+   }
+
+   protected String url(String path) {
+      return server.getUrl(path).toString();
+   }
+
+   protected MockResponse jsonResponse(String resource) {
+      return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
+   }
+
+   protected MockResponse response404() {
+      return new MockResponse().setStatus("HTTP/1.1 404 Not Found");
+   }
+
+   protected String stringFromResource(String resourceName) {
+      return stringFromResource("/org/apache/brooklyn/core/effector/http", resourceName);
+   }
+
+   private String stringFromResource(String prefix, String resourceName) {
+      try {
+         return Resources.toString(getClass().getResource(String.format("%s/%s", prefix, resourceName)), Charsets.UTF_8)
+                 .replace(DEFAULT_ENDPOINT, url(""));
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   protected RecordedRequest assertSent(BetterMockWebServer server, String method, String path) throws InterruptedException {
+      RecordedRequest request = server.takeRequest();
+      assertEquals(request.getMethod(), method);
+      assertEquals(request.getPath(), path);
+      return request;
+   }
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testMissingURI() {
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+      );
+   }
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testMissingVerb() {
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url(""))
+      );
+   }
+
+   @Test(expectedExceptions = ExecutionException.class)
+   public void testInvalidURI() throws ExecutionException, InterruptedException {
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.EFFECTOR_URI, "invalid-uri")
+      );
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      testEntity.invoke(EFFECTOR_HTTP_COMMAND, MutableMap.<String,String>of()).get();
+   }
+
+   @Test(expectedExceptions = ExecutionException.class)
+   public void testInvalidVerb() throws ExecutionException, InterruptedException {
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "CHANGE")
+              .configure(HttpCommandEffector.EFFECTOR_URI, url(""))
+      );
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      testEntity.invoke(EFFECTOR_HTTP_COMMAND, MutableMap.<String,String>of()).get();
+   }
+
+   @Test
+   public void testHappyPath() throws InterruptedException {
+      server.enqueue(jsonResponse("login.json"));
+
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login")
+      );
+      assertNotNull(httpCommandEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      Object output = testEntity.invoke(EFFECTOR_HTTP_COMMAND, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      assertEquals(output, "myLogin");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/get?login=myLogin");
+   }
+
+   @Test(expectedExceptions = PropagatedRuntimeException.class)
+   public void testWhen404() throws InterruptedException {
+      server.enqueue(response404());
+
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login")
+      );
+      assertNotNull(httpCommandEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      Object output = testEntity.invoke(EFFECTOR_HTTP_COMMAND, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      assertEquals(output, "myLogin");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/get?login=myLogin");
+   }
+
+   private EntitySpec<TestEntity> buildEntitySpec(HttpCommandEffector httpCommandEffector) {
+      return EntitySpec.create(TestEntity.class).addInitializer(httpCommandEffector);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b0b83da7/core/src/test/resources/org/apache/brooklyn/core/effector/http/login.json
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/effector/http/login.json b/core/src/test/resources/org/apache/brooklyn/core/effector/http/login.json
new file mode 100644
index 0000000..b39889a
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/effector/http/login.json
@@ -0,0 +1,16 @@
+{
+  "args": {
+    "login": "myLogin"
+  },
+  "headers": {
+    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+    "Accept-Encoding": "gzip, deflate, sdch, br",
+    "Accept-Language": "en-US,en;q=0.8,it;q=0.6",
+    "Cookie": "_ga=GA1.2.1060288368.1484053495; _gat=1",
+    "Host": "httpbin.org",
+    "Upgrade-Insecure-Requests": "1",
+    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"
+  },
+  "origin": "93.61.99.89",
+  "url": "https://httpbin.org/get?login=myLogin"
+}


[05/10] brooklyn-server git commit: add CompositeEffectorYamlRebindTest

Posted by he...@apache.org.
add CompositeEffectorYamlRebindTest


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/8809d5cd
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/8809d5cd
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/8809d5cd

Branch: refs/heads/master
Commit: 8809d5cdde60f65636fafdce2917ea37e550f1e1
Parents: b32581d
Author: Andrea Turli <an...@gmail.com>
Authored: Thu Feb 2 17:34:09 2017 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Feb 13 15:16:02 2017 +0100

----------------------------------------------------------------------
 .../CompositeEffectorYamlRebindTest.java        | 102 +++++++++++++++++++
 1 file changed, 102 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8809d5cd/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlRebindTest.java
new file mode 100644
index 0000000..09a9075
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlRebindTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+import static org.apache.brooklyn.test.Asserts.assertFalse;
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.effector.CompositeEffector;
+import org.apache.brooklyn.core.effector.http.HttpCommandEffector;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+@Test
+public class CompositeEffectorYamlRebindTest extends AbstractYamlRebindTest {
+
+   private final static String appId = "my-app-with-composite-effector";
+   private final static String appVersion = "1.0.0-SNAPSHOT";
+   static final String appVersionedId = appId + ":" + appVersion;
+
+   static final String catalogYamlSimple = Joiner.on("\n").join(
+           "brooklyn.catalog:",
+           "  id: " + appId,
+           "  version: " + appVersion,
+           "  itemType: entity",
+           "  item:",
+           "    type: " + TestEntity.class.getName(),
+           "    name: targetEntity",
+           "    brooklyn.initializers:",
+           "    - type: " + HttpCommandEffector.class.getName(),
+           "      brooklyn.config:",
+           "        name: myEffector",
+           "        description: myDescription",
+           "        uri: https://httpbin.org/get?id=myId",
+           "        httpVerb: GET",
+           "        jsonPath: $.args.id",
+           "        publishSensor: results",
+           "    - type: " + CompositeEffector.class.getName(),
+           "      brooklyn.config:",
+           "        name: start",
+           "        override: true",
+           "        effectors:",
+           "        - myEffector"
+   );
+
+   @Test
+   public void testRebindWhenHealthy() throws Exception {
+      runRebindWhenIsUp(catalogYamlSimple, appVersionedId);
+   }
+
+   protected void runRebindWhenIsUp(String catalogYaml, String appId) throws Exception {
+      addCatalogItems(catalogYaml);
+
+      String appYaml = Joiner.on("\n").join(
+              "services: ",
+              "- type: " + appId);
+      createStartWaitAndLogApplication(appYaml);
+
+      // Rebind
+      StartableApplication newApp = rebind();
+      TestEntity testEntity = (TestEntity) Iterables.find(newApp.getChildren(), EntityPredicates.displayNameEqualTo("targetEntity"));
+      Effector effector = assertHasInitializers(testEntity, "start");
+
+      // Confirm HttpCommandEffector still functions
+      Object results = testEntity.invoke(effector, ImmutableMap.<String, Object>of()).get();
+      assertEquals(((List<Object>)results).get(0), "myId");
+   }
+
+
+   protected static Effector<?> assertHasInitializers(Entity entity, String effectorName) {
+      Maybe<Effector<?>> effectorMaybe = entity.getEntityType().getEffectorByName(effectorName);
+      assertFalse(effectorMaybe.isAbsent());
+      return effectorMaybe.get();
+   }
+
+}


[02/10] brooklyn-server git commit: remove default value for JSON_PATH

Posted by he...@apache.org.
remove default value for JSON_PATH

- some http calls may not require to parse the output


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/3d318e4e
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/3d318e4e
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/3d318e4e

Branch: refs/heads/master
Commit: 3d318e4e78085519233bcc763e22320ca40403df
Parents: 119ba5a
Author: Andrea Turli <an...@gmail.com>
Authored: Mon Jan 23 17:24:31 2017 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Feb 13 15:09:31 2017 +0100

----------------------------------------------------------------------
 .../apache/brooklyn/core/effector/http/HttpCommandEffector.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3d318e4e/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
index 7ef2126..d2130dd 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
@@ -57,7 +57,7 @@ public final class HttpCommandEffector extends AddEffector {
     public static final ConfigKey<String> EFFECTOR_HTTP_PASSWORD = ConfigKeys.newStringConfigKey("httpPassword");
     public static final ConfigKey<Map<String, String>> EFFECTOR_HTTP_HEADERS = new MapConfigKey(String.class, "headers");
     public static final ConfigKey<Map<String, Object>> EFFECTOR_HTTP_PAYLOAD = new MapConfigKey(String.class, "httpPayload");
-    public static final ConfigKey<String> JSON_PATH = ConfigKeys.newStringConfigKey("jsonPath", "JSON path to select in HTTP response; default $", "$");
+    public static final ConfigKey<String> JSON_PATH = ConfigKeys.newStringConfigKey("jsonPath", "JSON path to select in HTTP response");
     public static final ConfigKey<String> PUBLISH_SENSOR = ConfigKeys.newStringConfigKey("publishSensor", "Sensor name where to store json path extracted value");
 
     public HttpCommandEffector(ConfigBag params) {


[04/10] brooklyn-server git commit: initial work to support HttpEntity

Posted by he...@apache.org.
initial work to support HttpEntity

- add HttpCommnadEffector
- add CompositeEffector
- add EntityInitializers util class to resolve DSL injected as params
  into the HttpCommandEffector


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/eadec9ac
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/eadec9ac
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/eadec9ac

Branch: refs/heads/master
Commit: eadec9ac9ca82ab1b5baea8c69b6ab35b55c452b
Parents: 0f649fe
Author: Andrea Turli <an...@gmail.com>
Authored: Wed Dec 21 12:03:27 2016 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Feb 13 15:09:31 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/core/effector/AddSensor.java       |   2 +
 .../core/effector/CompositeEffector.java        | 130 +++++++++++++++++
 .../brooklyn/core/effector/Effectors.java       |   4 +
 .../core/effector/http/HttpCommandEffector.java | 145 +++++++++++++++++++
 .../core/entity/EntityInitializers.java         |  32 +++-
 .../core/sensor/http/HttpRequestSensor.java     |  47 +++---
 .../CompositeEffectorIntegrationTest.java       |  78 ++++++++++
 .../HttpCommandEffectorIntegrationTest.java     | 125 ++++++++++++++++
 .../apache/brooklyn/rest/api/EffectorApi.java   |  28 ++--
 9 files changed, 559 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java b/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java
index ba8d679..92cc4ec 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java
@@ -57,6 +57,7 @@ public class AddSensor<T> implements EntityInitializer {
     protected final Duration period;
     protected final String type;
     protected AttributeSensor<T> sensor;
+    protected final ConfigBag params;
 
     public AddSensor(Map<String, String> params) {
         this(ConfigBag.newInstance(params));
@@ -66,6 +67,7 @@ public class AddSensor<T> implements EntityInitializer {
         this.name = Preconditions.checkNotNull(params.get(SENSOR_NAME), "Name must be supplied when defining a sensor");
         this.period = params.get(SENSOR_PERIOD);
         this.type = params.get(SENSOR_TYPE);
+        this.params = params;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
new file mode 100644
index 0000000..88cd92c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.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.effector;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.Effectors.EffectorBuilder;
+import org.apache.brooklyn.core.entity.EntityInitializers;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.reflect.TypeToken;
+
+@Beta
+public class CompositeEffector extends AddEffector {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CompositeEffector.class);
+
+    public static final ConfigKey<List<String>> EFFECTORS = ConfigKeys.newConfigKey(new TypeToken<List<String>>() {}, "effectors",
+            "Effector names to be chained together in the composite effector", ImmutableList.<String>of());
+    public static final ConfigKey<Boolean> OVERRIDE = ConfigKeys.newBooleanConfigKey("override",
+            "Wheter additional defined effectors should override pre-existing effector with same name or not (default: false)", Boolean.FALSE);
+    public CompositeEffector(ConfigBag params) {
+        super(newEffectorBuilder(params).build());
+    }
+
+    public CompositeEffector(Map<?, ?> params) {
+        this(ConfigBag.newInstance(params));
+    }
+
+    public static EffectorBuilder<String> newEffectorBuilder(ConfigBag params) {
+        EffectorBuilder<String> eff = AddEffector.newEffectorBuilder(String.class, params);
+        eff.impl(new Body(eff.buildAbstract(), params));
+        return eff;
+    }
+
+    @Override
+    public void apply(EntityLocal entity) {
+        Maybe<Effector<?>> effectorMaybe = entity.getEntityType().getEffectorByName(effector.getName());
+        if (!effectorMaybe.isAbsentOrNull()) {
+            Effector<?> original = Effectors.effector(effectorMaybe.get()).name("original-" + effector.getName()).build();
+            ((EntityInternal)entity).getMutableEntityType().addEffector(original);
+        }
+        super.apply(entity);
+    }
+
+    protected static class Body extends EffectorBody<String> {
+        private final Effector<?> effector;
+        private final ConfigBag params;
+
+        public Body(Effector<?> eff, ConfigBag params) {
+            this.effector = eff;
+            Preconditions.checkNotNull(params.getAllConfigRaw().get(EFFECTORS.getName()), "Effector names must be supplied when defining this effector");
+            this.params = params;
+        }
+
+        @Override
+        public String call(final ConfigBag params) {
+            ConfigBag allConfig = ConfigBag.newInstanceCopying(this.params).putAll(params);
+            final List<String> effectorNames = EntityInitializers.resolve(allConfig, EFFECTORS);
+            final Boolean override = allConfig.get(OVERRIDE);
+
+            List <Object> results = Lists.newArrayList();
+            if (!override) {
+                List<Effector<?>> originalEffectors = FluentIterable.from(entity().getEntityType().getEffectors())
+                        .filter(new Predicate<Effector<?>>() {
+                            @Override
+                            public boolean apply(@Nullable Effector<?> input) {
+                                return input.getName().equals("original-" + effector.getName());
+                            }
+                        })
+                        .toList();
+
+                for (Effector<?> originalEffector : originalEffectors) {
+                    results.add(invokeEffectorNamed(originalEffector.getName(), params));
+                }
+            }
+            for (String eff : effectorNames) {
+                results.add(invokeEffectorNamed(eff, params));
+            }
+            return Iterables.toString(results);
+        }
+
+        private Object invokeEffectorNamed(String effectorName, ConfigBag params) {
+            LOG.debug("{} invoking effector on {}, effector={}, parameters={}",
+                    new Object[]{this, entity(), effectorName, params});
+            Maybe<Effector<?>> effector = entity().getEntityType().getEffectorByName(effectorName);
+            if (effector.isAbsent()) {
+                // TODO
+            }
+            return entity().invoke(effector.get(), params.getAllConfig()).getUnchecked();
+            
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java b/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
index c644001..53db25a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
@@ -64,6 +64,10 @@ public class Effectors {
             this.returnType = returnType;
             this.effectorName = effectorName;
         }
+        public EffectorBuilder<T> name(String name) {
+            this.effectorName = name;
+            return this;
+        }
         public EffectorBuilder<T> description(String description) {
             this.description = description;
             return this;                

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
new file mode 100644
index 0000000..7ef2126
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
@@ -0,0 +1,145 @@
+/*
+ * 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.effector.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.MapConfigKey;
+import org.apache.brooklyn.core.effector.AddEffector;
+import org.apache.brooklyn.core.effector.EffectorBody;
+import org.apache.brooklyn.core.effector.Effectors.EffectorBuilder;
+import org.apache.brooklyn.core.entity.EntityInitializers;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.util.collections.Jsonya;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.http.executor.HttpConfig;
+import org.apache.brooklyn.util.http.executor.HttpExecutor;
+import org.apache.brooklyn.util.http.executor.HttpRequest;
+import org.apache.brooklyn.util.http.executor.HttpResponse;
+import org.apache.brooklyn.util.http.executor.UsernamePassword;
+import org.apache.brooklyn.util.http.executor.apacheclient.HttpExecutorImpl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteStreams;
+import com.jayway.jsonpath.JsonPath;
+
+public final class HttpCommandEffector extends AddEffector {
+
+    public static final ConfigKey<String> EFFECTOR_URI = ConfigKeys.newStringConfigKey("uri");
+    public static final ConfigKey<String> EFFECTOR_HTTP_VERB = ConfigKeys.newStringConfigKey("httpVerb");
+    public static final ConfigKey<String> EFFECTOR_HTTP_USERNAME = ConfigKeys.newStringConfigKey("httpUsername");
+    public static final ConfigKey<String> EFFECTOR_HTTP_PASSWORD = ConfigKeys.newStringConfigKey("httpPassword");
+    public static final ConfigKey<Map<String, String>> EFFECTOR_HTTP_HEADERS = new MapConfigKey(String.class, "headers");
+    public static final ConfigKey<Map<String, Object>> EFFECTOR_HTTP_PAYLOAD = new MapConfigKey(String.class, "httpPayload");
+    public static final ConfigKey<String> JSON_PATH = ConfigKeys.newStringConfigKey("jsonPath", "JSON path to select in HTTP response; default $", "$");
+    public static final ConfigKey<String> PUBLISH_SENSOR = ConfigKeys.newStringConfigKey("publishSensor", "Sensor name where to store json path extracted value");
+
+    public HttpCommandEffector(ConfigBag params) {
+        super(newEffectorBuilder(params).build());
+    }
+
+    public static EffectorBuilder<String> newEffectorBuilder(ConfigBag params) {
+        EffectorBuilder<String> eff = AddEffector.newEffectorBuilder(String.class, params);
+        eff.impl(new Body(eff.buildAbstract(), params));
+        return eff;
+    }
+    
+    protected static class Body extends EffectorBody<String> {
+        private final Effector<?> effector;
+        private final ConfigBag params;
+
+        public Body(Effector<?> eff, final ConfigBag params) {
+            this.effector = eff;
+            Preconditions.checkNotNull(params.getAllConfigRaw().get(EFFECTOR_URI.getName()), "uri must be supplied when defining this effector");
+            Preconditions.checkNotNull(params.getAllConfigRaw().get(EFFECTOR_HTTP_VERB.getName()), "HTTP verb must be supplied when defining this effector");
+            this.params = params;
+        }
+
+        @Override
+        public String call(final ConfigBag params) {
+            ConfigBag allConfig = ConfigBag.newInstanceCopying(this.params).putAll(params);
+            final String uri = EntityInitializers.resolve(allConfig, EFFECTOR_URI);
+            final String httpVerb = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_VERB);
+            final String httpUsername = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_USERNAME);
+            final String httpPassword = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_PASSWORD);
+            final Map<String, String> headers = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_HEADERS);
+            final Map<String, Object> payload = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_PAYLOAD);
+            final String jsonPath = EntityInitializers.resolve(allConfig, JSON_PATH);
+            final String publishSensor = EntityInitializers.resolve(allConfig, PUBLISH_SENSOR);
+            Task t = Tasks.builder().displayName(effector.getName()).body(new Callable<Object>() {
+                @Override
+                public Object call() throws Exception {
+                    HttpExecutor httpExecutor = HttpExecutorImpl.newInstance();
+
+                    String body = "";
+                    if (payload != null && !payload.isEmpty() && headers.containsKey("Content-Type")) {
+                        body = Jsonya.newInstance().put(payload).toString();
+                    }
+                    HttpRequest.Builder httpRequestBuilder = new HttpRequest.Builder()
+                            .body(body.getBytes())
+                            .uri(URI.create(uri))
+                            .method(httpVerb)
+                            .config(HttpConfig.builder()
+                                    .trustSelfSigned(true)
+                                    .trustAll(true)
+                                    .laxRedirect(true)
+                                    .build());
+
+                    if (headers != null) {
+                        httpRequestBuilder.headers(headers);
+                    }
+
+                    if (httpUsername != null && httpPassword != null) {
+                        httpRequestBuilder.credentials(new UsernamePassword(httpUsername, httpPassword));
+                    }
+                    
+                    HttpRequest httpRequest = httpRequestBuilder.build();
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    try {
+                        HttpResponse response = httpExecutor.execute(httpRequest);
+                        ByteStreams.copy(response.getContent(), out);
+                        return new String(out.toByteArray());
+                    } catch (IOException e) {
+                        throw Exceptions.propagate(e);
+                    }
+                }
+            }).build();
+
+            String val = (String) queue(t).getUnchecked();
+            if (jsonPath != null) {
+                String extractedValue = JsonPath.parse(val).read(jsonPath, String.class);
+                entity().sensors().set(Sensors.newStringSensor(publishSensor), extractedValue);
+                return extractedValue;
+            } else {
+                return val;
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java
index a258007..cffcced7 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java
@@ -22,6 +22,11 @@ import java.util.List;
 
 import org.apache.brooklyn.api.entity.EntityInitializer;
 import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.mgmt.ExecutionContext;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.internal.ConfigKeySelfExtracting;
+import org.apache.brooklyn.util.core.task.BasicExecutionContext;
 
 import com.google.common.collect.ImmutableList;
 
@@ -41,9 +46,32 @@ public class EntityInitializers {
         }
     }
 
-    
     public static EntityInitializer addingTags(Object... tags) {
         return new AddTags(tags);
     }
-    
+
+    /**
+     * Resolves key in the
+     * {@link BasicExecutionContext#getCurrentExecutionContext current execution context}.
+     * @see #resolve(ConfigBag, ConfigKey, ExecutionContext)
+     */
+    public static <T> T resolve(ConfigBag configBag, ConfigKey<T> key) {
+        return resolve(configBag, key, BasicExecutionContext.getCurrentExecutionContext());
+    }
+
+    /**
+     * Gets the value for key from configBag.
+     * <p>
+     * If key is an instance of {@link ConfigKeySelfExtracting} and executionContext is
+     * not null then its value will be retrieved per the key's implementation of
+     * {@link ConfigKeySelfExtracting#extractValue extractValue}. Otherwise, the value
+     * will be retrieved from configBag directly.
+     */
+    public static <T> T resolve(ConfigBag configBag, ConfigKey<T> key, ExecutionContext executionContext) {
+        if (key instanceof ConfigKeySelfExtracting && executionContext != null) {
+            ConfigKeySelfExtracting<T> ckse = ((ConfigKeySelfExtracting<T>) key);
+            return ckse.extractValue(configBag.getAllConfigAsConfigKeyMap(), executionContext);
+        }
+        return configBag.get(key);
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java
index dea44d3..966a88c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java
+++ b/core/src/main/java/org/apache/brooklyn/core/sensor/http/HttpRequestSensor.java
@@ -19,13 +19,14 @@
 package org.apache.brooklyn.core.sensor.http;
 
 import java.net.URI;
-
-import net.minidev.json.JSONObject;
+import java.util.Map;
 
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.MapConfigKey;
 import org.apache.brooklyn.core.effector.AddSensor;
+import org.apache.brooklyn.core.entity.EntityInitializers;
 import org.apache.brooklyn.core.sensor.ssh.SshCommandSensor;
 import org.apache.brooklyn.feed.http.HttpFeed;
 import org.apache.brooklyn.feed.http.HttpPollConfig;
@@ -38,6 +39,8 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Functions;
 import com.google.common.base.Supplier;
 
+import net.minidev.json.JSONObject;
+
 /**
  * Configurable {@link org.apache.brooklyn.api.entity.EntityInitializer} which adds an HTTP sensor feed to retrieve the
  * {@link JSONObject} from a JSON response in order to populate the sensor with the data at the {@code jsonPath}.
@@ -53,24 +56,10 @@ public final class HttpRequestSensor<T> extends AddSensor<T> {
     public static final ConfigKey<String> JSON_PATH = ConfigKeys.newStringConfigKey("jsonPath", "JSON path to select in HTTP response; default $", "$");
     public static final ConfigKey<String> USERNAME = ConfigKeys.newStringConfigKey("username", "Username for HTTP request, if required");
     public static final ConfigKey<String> PASSWORD = ConfigKeys.newStringConfigKey("password", "Password for HTTP request, if required");
-
-    protected final Supplier<URI> uri;
-    protected final String jsonPath;
-    protected final String username;
-    protected final String password;
+    public static final ConfigKey<Map<String, String>> HEADERS = new MapConfigKey(String.class, "headers");
 
     public HttpRequestSensor(final ConfigBag params) {
         super(params);
-
-        uri = new Supplier<URI>() {
-            @Override
-            public URI get() {
-                return URI.create(params.get(SENSOR_URI));
-            }
-        };
-        jsonPath = params.get(JSON_PATH);
-        username = params.get(USERNAME);
-        password = params.get(PASSWORD);
     }
 
     @Override
@@ -81,18 +70,36 @@ public final class HttpRequestSensor<T> extends AddSensor<T> {
             LOG.debug("Adding HTTP JSON sensor {} to {}", name, entity);
         }
 
+        final ConfigBag allConfig = ConfigBag.newInstanceCopying(this.params).putAll(params);
+        final Supplier<URI> uri = new Supplier<URI>() {
+            @Override
+            public URI get() {
+                return URI.create(EntityInitializers.resolve(allConfig, SENSOR_URI));
+            }
+        };
+        final String jsonPath = EntityInitializers.resolve(allConfig, JSON_PATH);
+        final String username = EntityInitializers.resolve(allConfig, USERNAME);
+        final String password = EntityInitializers.resolve(allConfig, PASSWORD);
+        final Map<String, String> headers = EntityInitializers.resolve(allConfig, HEADERS);
+
+        
         HttpPollConfig<T> pollConfig = new HttpPollConfig<T>(sensor)
                 .checkSuccess(HttpValueFunctions.responseCodeEquals(200))
                 .onFailureOrException(Functions.constant((T) null))
                 .onSuccess(HttpValueFunctions.<T>jsonContentsFromPath(jsonPath))
                 .period(period);
 
-        HttpFeed feed = HttpFeed.builder().entity(entity)
+        HttpFeed.Builder httpRequestBuilder = HttpFeed.builder().entity(entity)
                 .baseUri(uri)
                 .credentialsIfNotNull(username, password)
-                .poll(pollConfig)
-                .build();
+                .poll(pollConfig);
 
+        if (headers != null) {
+            httpRequestBuilder.headers(headers);
+        }
+        
+        HttpFeed feed = httpRequestBuilder.build();
         entity.addFeed(feed);
     }
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java
new file mode 100644
index 0000000..e8cc30e
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.effector;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.effector.http.HttpCommandEffector;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class CompositeEffectorIntegrationTest {
+
+    final static Effector<String> EFFECTOR_START = Effectors.effector(String.class, "start").buildAbstract();
+
+    private TestApplication app;
+    private EntityLocal entity;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).location(TestApplication.LOCALHOST_MACHINE_SPEC));
+        app.start(ImmutableList.<Location>of());
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+    }
+
+    @Test(groups="Integration")
+    public void testCompositeEffector() throws Exception {
+        new HttpCommandEffector(ConfigBag.newInstance()
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "eff1")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/apache")
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET"))
+                .apply(entity);
+        new HttpCommandEffector(ConfigBag.newInstance()
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "eff2")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/brooklyncentral")
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET"))
+                .apply(entity);
+        new CompositeEffector(ConfigBag.newInstance()
+                .configure(CompositeEffector.EFFECTOR_NAME, "start")
+                .configure(CompositeEffector.EFFECTORS, ImmutableList.of("eff1", "eff2")))
+                .apply(entity);
+
+        String val = entity.invoke(EFFECTOR_START, MutableMap.<String,String>of()).get();
+        // TODO
+        System.out.println(val);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java
new file mode 100644
index 0000000..080e08a
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorIntegrationTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.effector.http;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.effector.Effectors;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.jayway.jsonpath.JsonPath;
+
+public class HttpCommandEffectorIntegrationTest {
+
+    final static Effector<String> EFFECTOR_GITHUB_APACHE_ACCOUNT = Effectors.effector(String.class, "GithubApacheAccount").buildAbstract();
+
+    private TestApplication app;
+    private EntityLocal entity;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).location(TestApplication.LOCALHOST_MACHINE_SPEC));
+        app.start(ImmutableList.<Location>of());
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+    }
+
+    @Test(groups="Integration")
+    public void testHttpEffector() throws Exception {
+        new HttpCommandEffector(ConfigBag.newInstance()
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "GithubApacheAccount")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/apache")
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+        ).apply(entity);
+
+        String val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.<String,String>of()).get();
+        Assert.assertEquals(JsonPath.parse(val).read("$.login", String.class), "apache");
+    }
+
+    @Test(groups="Integration")
+    public void testHttpEffectorWithPayload() throws Exception {
+        new HttpCommandEffector(ConfigBag.newInstance()
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "CreateGist")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/gists")
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "POST")
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_PAYLOAD, ImmutableMap.<String, Object>of(
+                        "description", "Created via API", 
+                        "public", "false",
+                        "files", ImmutableMap.of("demo.txt", ImmutableMap.of("content","Demo"))))
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_HEADERS, ImmutableMap.of("Content-Type", "application/json"))
+                .configure(HttpCommandEffector.JSON_PATH, "$.url")
+                .configure(HttpCommandEffector.PUBLISH_SENSOR, "result")
+        ).apply(entity);
+
+        String url = entity.invoke(Effectors.effector(String.class, "CreateGist").buildAbstract(), MutableMap.<String,String>of()).get();
+        Assert.assertNotNull(url, "url");
+    }
+
+    @Test(groups="Integration")
+    public void testHttpEffectorWithJsonPath() throws Exception {
+        new HttpCommandEffector(ConfigBag.newInstance()
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "GithubApacheAccount")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/apache")
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+                .configure(HttpCommandEffector.JSON_PATH, "$.login")
+                .configure(HttpCommandEffector.PUBLISH_SENSOR, "result")
+        ).apply(entity);
+
+        String val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.<String,String>of()).get();
+        Assert.assertEquals(val, "apache");
+        Assert.assertEquals(entity.sensors().get(Sensors.newStringSensor("result")), "apache");
+    }
+    
+    @Test(groups="Integration")
+    public void testHttpEffectorWithParameters() throws Exception {
+        new HttpCommandEffector(ConfigBag.newInstance()
+                .configure(HttpCommandEffector.EFFECTOR_NAME, "GithubApacheAccount")
+                .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/$user")
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+                .configure(HttpCommandEffector.EFFECTOR_PARAMETER_DEFS,
+                        MutableMap.<String,Object>of("user", MutableMap.of("defaultValue", "apache"))))
+                .apply(entity);
+
+        String val;
+        // explicit value
+        val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.of("user", "github")).get();
+        Assert.assertEquals(JsonPath.parse(val).read("$.login", String.class), "github");
+
+        // default value
+        val = entity.invoke(EFFECTOR_GITHUB_APACHE_ACCOUNT, MutableMap.<String,String>of()).get();
+        Assert.assertEquals(JsonPath.parse(val).read("$.login", String.class), "apache");
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/eadec9ac/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EffectorApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EffectorApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EffectorApi.java
index 2865223..6143b6f 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EffectorApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/EffectorApi.java
@@ -18,19 +18,27 @@
  */
 package org.apache.brooklyn.rest.api;
 
-import io.swagger.annotations.Api;
-import org.apache.brooklyn.rest.domain.EffectorSummary;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import java.util.List;
+import java.util.Map;
 
 import javax.validation.Valid;
-import javax.ws.rs.*;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import java.util.List;
-import java.util.Map;
+
+import org.apache.brooklyn.rest.domain.EffectorSummary;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 @Path("/applications/{application}/entities/{entity}/effectors")
 @Api("Entity Effectors")
@@ -54,7 +62,7 @@ public interface EffectorApi {
     @POST
     @Path("/{effector}")
     @ApiOperation(value = "Trigger an effector",
-            notes="Returns the return value (status 200) if it completes, or an activity task ID (status 202) if it times out")
+            notes="Returns the return value (status 200) if it completes, or an activity task ID (status 202) if it times out", response = String.class)
     @ApiResponses(value = {
             @ApiResponse(code = 404, message = "Could not find application, entity or effector")
     })


[07/10] brooklyn-server git commit: add CompositeEffector tests and improve its implementation

Posted by he...@apache.org.
add CompositeEffector tests and improve its implementation


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/b32581dd
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/b32581dd
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/b32581dd

Branch: refs/heads/master
Commit: b32581dd81a390d69ff63779465cd83520c7dcab
Parents: b0b83da
Author: Andrea Turli <an...@gmail.com>
Authored: Thu Feb 2 17:27:41 2017 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Feb 13 15:16:02 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/CompositeEffectorYamlTest.java     |  79 ++++++
 .../core/effector/CompositeEffector.java        |  68 ++---
 .../core/effector/http/HttpCommandEffector.java |  71 +++--
 .../CompositeEffectorIntegrationTest.java       |  27 +-
 .../core/effector/CompositeEffectorTest.java    | 262 +++++++++++++++++++
 .../effector/http/HttpCommandEffectorTest.java  |  88 ++++++-
 .../core/effector/http/int-response.json        |  16 ++
 .../core/effector/http/list-response.json       |  19 ++
 .../core/effector/http/map-response.json        |  16 ++
 .../org/apache/brooklyn/core/effector/test.json |  16 ++
 10 files changed, 595 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlTest.java
new file mode 100644
index 0000000..f114f23
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/CompositeEffectorYamlTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.effector.CompositeEffector;
+import org.apache.brooklyn.core.effector.http.HttpCommandEffector;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class CompositeEffectorYamlTest extends AbstractYamlTest {
+    private static final Logger log = LoggerFactory.getLogger(CompositeEffectorYamlTest.class);
+
+    @Test
+    public void testCompositeEffector() throws Exception {
+        Entity app = createAndStartApplication(
+            "location: localhost",
+            "services:",
+            "- type: " + EmptySoftwareProcess.class.getName(),
+            "  brooklyn.config:",
+            "    onbox.base.dir.skipResolution: true",
+            "    softwareProcess.serviceProcessIsRunningPollPeriod: forever",
+            "  brooklyn.initializers:",
+            "  - type: " + HttpCommandEffector.class.getName(),
+            "    brooklyn.config:",
+            "      name: myEffector",
+            "      description: myDescription",
+            "      uri: https://httpbin.org/get?id=myId",
+            "      httpVerb: GET",
+            "      jsonPath: $.args.id",
+            "      publishSensor: results",
+            "  - type: " + CompositeEffector.class.getName(),
+            "    brooklyn.config:",
+            "      name: start",
+            "      override: true",
+            "      effectors:",
+            "      - myEffector"
+        );
+        waitForApplicationTasks(app);
+
+        EmptySoftwareProcess entity = (EmptySoftwareProcess) Iterables.getOnlyElement(app.getChildren());
+        Effector<?> effector = entity.getEntityType().getEffectorByName("start").get();
+
+        // Invoke without parameter
+        Object results = entity.invoke(effector, ImmutableMap.<String, Object>of()).get();
+        assertEquals(((List<Object>)results).get(0), "myId");
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return log;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
index 32f0f1d..5345b4a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/CompositeEffector.java
@@ -18,11 +18,12 @@
  */
 package org.apache.brooklyn.core.effector;
 
+import static org.apache.brooklyn.core.entity.trait.Startable.START;
+import static org.apache.brooklyn.core.entity.trait.Startable.STOP;
+
 import java.util.List;
 import java.util.Map;
 
-import javax.annotation.Nullable;
-
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.config.ConfigKey;
@@ -36,11 +37,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.reflect.TypeToken;
 
@@ -48,11 +46,14 @@ import com.google.common.reflect.TypeToken;
 public class CompositeEffector extends AddEffector {
 
     private static final Logger LOG = LoggerFactory.getLogger(CompositeEffector.class);
+    private static final String ORIGINAL_PREFIX = "original-";
 
-    public static final ConfigKey<List<String>> EFFECTORS = ConfigKeys.newConfigKey(new TypeToken<List<String>>() {}, "effectors",
+    public static final ConfigKey<List<String>> EFFECTORS = ConfigKeys.newConfigKey(new TypeToken<List<String>>() {
+                                                                                    }, "effectors",
             "Effector names to be chained together in the composite effector", ImmutableList.<String>of());
     public static final ConfigKey<Boolean> OVERRIDE = ConfigKeys.newBooleanConfigKey("override",
             "Wheter additional defined effectors should override pre-existing effector with same name or not (default: false)", Boolean.FALSE);
+
     public CompositeEffector(ConfigBag params) {
         super(newEffectorBuilder(params).build());
     }
@@ -61,8 +62,8 @@ public class CompositeEffector extends AddEffector {
         this(ConfigBag.newInstance(params));
     }
 
-    public static EffectorBuilder<String> newEffectorBuilder(ConfigBag params) {
-        EffectorBuilder<String> eff = AddEffector.newEffectorBuilder(String.class, params);
+    public static EffectorBuilder<List> newEffectorBuilder(ConfigBag params) {
+        EffectorBuilder<List> eff = AddEffector.newEffectorBuilder(List.class, params);
         eff.impl(new Body(eff.buildAbstract(), params));
         return eff;
     }
@@ -71,13 +72,13 @@ public class CompositeEffector extends AddEffector {
     public void apply(EntityLocal entity) {
         Maybe<Effector<?>> effectorMaybe = entity.getEntityType().getEffectorByName(effector.getName());
         if (!effectorMaybe.isAbsentOrNull()) {
-            Effector<?> original = Effectors.effector(effectorMaybe.get()).name("original-" + effector.getName()).build();
-            ((EntityInternal)entity).getMutableEntityType().addEffector(original);
+            Effector<?> original = Effectors.effector(effectorMaybe.get()).name(ORIGINAL_PREFIX + effector.getName()).build();
+            ((EntityInternal) entity).getMutableEntityType().addEffector(original);
         }
         super.apply(entity);
     }
 
-    protected static class Body extends EffectorBody<String> {
+    protected static class Body extends EffectorBody<List> {
         private final Effector<?> effector;
         private final ConfigBag params;
 
@@ -88,46 +89,47 @@ public class CompositeEffector extends AddEffector {
         }
 
         @Override
-        public String call(final ConfigBag params) {
+        public List<Object> call(final ConfigBag params) {
             ConfigBag allConfig = ConfigBag.newInstanceCopying(this.params).putAll(params);
             final List<String> effectorNames = EntityInitializers.resolve(allConfig, EFFECTORS);
             final Boolean override = allConfig.get(OVERRIDE);
 
             List<Object> results = Lists.newArrayList();
-            if (!override) {
-                Optional<Effector<?>> effectorOptional = Iterables.tryFind(entity().getEntityType().getEffectors(), new Predicate<Effector<?>>() {
-                    @Override
-                    public boolean apply(@Nullable Effector<?> input) {
-                        return input.getName().equals("original-" + effector.getName());
-                    }
-                });
-                // if it is a stop effector, it has to be executed as last effector
-                if (effectorOptional.isPresent()) {
-                    if (effectorOptional.get().getName().endsWith("-stop")) {
-                        effectorNames.add(effectorOptional.get().getName());
-                    } else {
-                        effectorNames.add(0, effectorOptional.get().getName());
-                    }
-                }
-            }
 
+            if (!override && isStartRedefined()) {
+                results.add(invokeEffectorNamed(ORIGINAL_PREFIX + START.getName(), params));
+            }
             for (String eff : effectorNames) {
                 results.add(invokeEffectorNamed(eff, params));
             }
-            return Iterables.toString(results);
+            if (!override && isStopRedefined()) {
+                results.add(invokeEffectorNamed(ORIGINAL_PREFIX + STOP.getName(), params));
+            }
+            return results;
+        }
+
+        private boolean isStartRedefined() {
+            return isEffectorRedefined(ORIGINAL_PREFIX + START.getName());
+        }
+
+        private boolean isStopRedefined() {
+            return isEffectorRedefined(ORIGINAL_PREFIX + STOP.getName());
+        }
+
+        private boolean isEffectorRedefined(String effectorName) {
+            return entity().getEntityType().getEffectorByName(effectorName).isPresent();
         }
 
         private Object invokeEffectorNamed(String effectorName, ConfigBag params) {
-            LOG.debug("{} invoking effector on {}, effector={}, parameters={}",
+            LOG.info("{} invoking effector on {}, effector={}, parameters={}",
                     new Object[]{this, entity(), effectorName, params});
             Maybe<Effector<?>> effector = entity().getEntityType().getEffectorByName(effectorName);
             if (effector.isAbsent()) {
-                // TODO
+                throw new IllegalStateException("Cannot find effector " + effectorName);
             }
             return entity().invoke(effector.get(), params.getAllConfig()).getUnchecked();
-
         }
-    }
 
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
index 2d8266e..1388a926 100644
--- a/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/http/HttpCommandEffector.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.core.effector.http;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -49,28 +50,33 @@ import org.apache.brooklyn.util.http.executor.HttpRequest;
 import org.apache.brooklyn.util.http.executor.HttpResponse;
 import org.apache.brooklyn.util.http.executor.UsernamePassword;
 import org.apache.brooklyn.util.http.executor.apacheclient.HttpExecutorImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Enums;
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.io.ByteStreams;
 import com.google.common.net.HttpHeaders;
 import com.jayway.jsonpath.JsonPath;
 
 public final class HttpCommandEffector extends AddEffector {
 
+    private static final Logger LOG = LoggerFactory.getLogger(HttpCommandEffector.class);
+
     public static final ConfigKey<String> EFFECTOR_URI = ConfigKeys.newStringConfigKey("uri");
     public static final ConfigKey<String> EFFECTOR_HTTP_VERB = ConfigKeys.newStringConfigKey("httpVerb");
     public static final ConfigKey<String> EFFECTOR_HTTP_USERNAME = ConfigKeys.newStringConfigKey("httpUsername");
     public static final ConfigKey<String> EFFECTOR_HTTP_PASSWORD = ConfigKeys.newStringConfigKey("httpPassword");
     public static final ConfigKey<Map<String, String>> EFFECTOR_HTTP_HEADERS = new MapConfigKey(String.class, "headers");
-    public static final ConfigKey<Map<String, Object>> EFFECTOR_HTTP_PAYLOAD = new MapConfigKey(String.class, "httpPayload");
+    public static final ConfigKey<Object> EFFECTOR_HTTP_PAYLOAD = ConfigKeys.newConfigKey(Object.class, "httpPayload");
     public static final ConfigKey<String> JSON_PATH = ConfigKeys.newStringConfigKey("jsonPath", "JSON path to select in HTTP response");
     public static final ConfigKey<String> PUBLISH_SENSOR = ConfigKeys.newStringConfigKey("publishSensor", "Sensor name where to store json path extracted value");
 
+    public static final String APPLICATION_JSON = "application/json";
+
     private enum HttpVerb {
-        GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
+        GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
     }
 
     public HttpCommandEffector(ConfigBag params) {
@@ -89,29 +95,28 @@ public final class HttpCommandEffector extends AddEffector {
 
         public Body(Effector<?> eff, final ConfigBag params) {
             this.effector = eff;
-            Preconditions.checkNotNull(params.getAllConfigRaw().get(EFFECTOR_URI.getName()), "uri must be supplied when defining this effector");
-            Preconditions.checkNotNull(params.getAllConfigRaw().get(EFFECTOR_HTTP_VERB.getName()), "HTTP verb must be supplied when defining this effector");
+            checkNotNull(params.getAllConfigRaw().get(EFFECTOR_URI.getName()), "uri must be supplied when defining this effector");
+            checkNotNull(params.getAllConfigRaw().get(EFFECTOR_HTTP_VERB.getName()), "HTTP verb must be supplied when defining this effector");
             this.params = params;
         }
 
         @Override
         public String call(final ConfigBag params) {
             ConfigBag allConfig = ConfigBag.newInstanceCopying(this.params).putAll(params);
-            final String uri = EntityInitializers.resolve(allConfig, EFFECTOR_URI);
-            final String httpVerb = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_VERB);
+            final URI uri = convertToURI(EntityInitializers.resolve(allConfig, EFFECTOR_URI));
+            final String httpVerb = isValidHttpVerb(EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_VERB));
             final String httpUsername = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_USERNAME);
             final String httpPassword = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_PASSWORD);
             final Map<String, String> headers = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_HEADERS);
-            final Map<String, Object> payload = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_PAYLOAD);
+            final Object payload = EntityInitializers.resolve(allConfig, EFFECTOR_HTTP_PAYLOAD);
             final String jsonPath = EntityInitializers.resolve(allConfig, JSON_PATH);
             final String publishSensor = EntityInitializers.resolve(allConfig, PUBLISH_SENSOR);
+            final HttpExecutor httpExecutor = HttpExecutorImpl.newInstance();
+
+            final HttpRequest request = buildHttpRequest(httpVerb, uri, headers, httpUsername, httpPassword, payload);
             Task t = Tasks.builder().displayName(effector.getName()).body(new Callable<Object>() {
                 @Override
                 public Object call() throws Exception {
-                    HttpExecutor httpExecutor = HttpExecutorImpl.newInstance();
-
-                    String body = getBodyFromPayload(payload, headers);
-                    HttpRequest request = buildHttpRequest(httpVerb, uri, headers, httpUsername, httpPassword, body);
                     ByteArrayOutputStream out = new ByteArrayOutputStream();
                     try {
                         HttpResponse response = httpExecutor.execute(request);
@@ -127,6 +132,7 @@ public final class HttpCommandEffector extends AddEffector {
             String responseBody = (String) queue(t).getUnchecked();
 
             if (jsonPath == null) return responseBody;
+
             String extractedValue = JsonPath.parse(responseBody).read(jsonPath, String.class);
             if (publishSensor != null) {
                 entity().sensors().set(Sensors.newStringSensor(publishSensor), extractedValue);
@@ -134,6 +140,16 @@ public final class HttpCommandEffector extends AddEffector {
             return extractedValue;
         }
 
+        private URI convertToURI(String url) {
+            try {
+                return new URL(url).toURI();
+            } catch (MalformedURLException e) {
+                throw Exceptions.propagate(e);
+            } catch (URISyntaxException e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+
         private void validateResponse(HttpResponse response) {
             int statusCode = response.code();
             if (statusCode == 401) {
@@ -145,13 +161,8 @@ public final class HttpCommandEffector extends AddEffector {
             }
         }
 
-        private HttpRequest buildHttpRequest(String httpVerb, String url, Map<String, String> headers, String httpUsername, String httpPassword, String body) throws MalformedURLException, URISyntaxException {
-            // validate url string
-            URI uri = new URL(url).toURI();
-            // validate HTTP verb
-            validateHttpVerb(httpVerb);
+        private HttpRequest buildHttpRequest(String httpVerb, URI uri, Map<String, String> headers, String httpUsername, String httpPassword, Object payload) {
             HttpRequest.Builder httpRequestBuilder = new HttpRequest.Builder()
-                    .body(body.getBytes())
                     .uri(uri)
                     .method(httpVerb)
                     .config(HttpConfig.builder()
@@ -164,6 +175,19 @@ public final class HttpCommandEffector extends AddEffector {
                 httpRequestBuilder.headers(headers);
             }
 
+            if (payload != null) {
+                String body = "";
+                String contentType = headers.get(HttpHeaders.CONTENT_TYPE);
+                if (contentType == null || contentType.equalsIgnoreCase(APPLICATION_JSON)) {
+                    LOG.warn("Content-Type not specified. Using {}, as default (continuing)", APPLICATION_JSON);
+                    body = toJsonString(payload);
+                } else if (!(payload instanceof String) && !contentType.equalsIgnoreCase(APPLICATION_JSON)) {
+                    LOG.warn("the http request may fail with payload {} and 'Content-Type= {}, (continuing)", payload, contentType);
+                    body = payload.toString();
+                }
+                httpRequestBuilder.body(body.getBytes());
+            }
+
             if (httpUsername != null && httpPassword != null) {
                 httpRequestBuilder.credentials(new UsernamePassword(httpUsername, httpPassword));
             }
@@ -171,17 +195,14 @@ public final class HttpCommandEffector extends AddEffector {
             return httpRequestBuilder.build();
         }
 
-        private void validateHttpVerb(String httpVerb) {
+        private String isValidHttpVerb(String httpVerb) {
             Optional<HttpVerb> state = Enums.getIfPresent(HttpVerb.class, httpVerb.toUpperCase());
             checkArgument(state.isPresent(), "Expected one of %s but was %s", Joiner.on(',').join(HttpVerb.values()), httpVerb);
+            return httpVerb;
         }
 
-        private String getBodyFromPayload(Map<String, Object> payload, Map<String, String> headers) {
-            String body = "";
-            if (payload != null && !payload.isEmpty() && headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
-                body = Jsonya.newInstance().put(payload).toString();
-            }
-            return body;
+        private String toJsonString(Object payload) {
+            return Jsonya.newInstance().add(payload).toString();
         }
 
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java
index e8cc30e..dd168ed 100644
--- a/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorIntegrationTest.java
@@ -18,6 +18,12 @@
  */
 package org.apache.brooklyn.core.effector;
 
+import static org.apache.brooklyn.test.Asserts.assertEquals;
+import static org.apache.brooklyn.test.Asserts.assertNull;
+import static org.apache.brooklyn.test.Asserts.assertTrue;
+
+import java.util.List;
+
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.entity.EntitySpec;
@@ -36,11 +42,11 @@ import com.google.common.collect.ImmutableList;
 
 public class CompositeEffectorIntegrationTest {
 
-    final static Effector<String> EFFECTOR_START = Effectors.effector(String.class, "start").buildAbstract();
+    final static Effector<List> EFFECTOR_START = Effectors.effector(List.class, "start").buildAbstract();
 
     private TestApplication app;
     private EntityLocal entity;
-    
+
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
         app = TestApplication.Factory.newManagedInstanceForTests();
@@ -58,21 +64,28 @@ public class CompositeEffectorIntegrationTest {
         new HttpCommandEffector(ConfigBag.newInstance()
                 .configure(HttpCommandEffector.EFFECTOR_NAME, "eff1")
                 .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/apache")
-                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET"))
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+                .configure(HttpCommandEffector.JSON_PATH, "$.login"))
                 .apply(entity);
         new HttpCommandEffector(ConfigBag.newInstance()
                 .configure(HttpCommandEffector.EFFECTOR_NAME, "eff2")
                 .configure(HttpCommandEffector.EFFECTOR_URI, "https://api.github.com/users/brooklyncentral")
-                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET"))
+                .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+                .configure(HttpCommandEffector.JSON_PATH, "$.login"))
                 .apply(entity);
         new CompositeEffector(ConfigBag.newInstance()
                 .configure(CompositeEffector.EFFECTOR_NAME, "start")
                 .configure(CompositeEffector.EFFECTORS, ImmutableList.of("eff1", "eff2")))
                 .apply(entity);
 
-        String val = entity.invoke(EFFECTOR_START, MutableMap.<String,String>of()).get();
-        // TODO
-        System.out.println(val);
+        List<Object> results = entity.invoke(EFFECTOR_START, MutableMap.<String,String>of()).get();
+
+        assertEquals(results.size(), 3);
+        assertNull(results.get(0));
+        assertTrue(results.get(1) instanceof String);
+        assertEquals(results.get(1), "apache");
+        assertTrue(results.get(2) instanceof String);
+        assertEquals(results.get(2), "brooklyncentral");
     }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorTest.java
new file mode 100644
index 0000000..3bf0018
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/CompositeEffectorTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.effector;
+
+import static org.apache.brooklyn.core.effector.http.HttpCommandEffectorTest.EFFECTOR_HTTP_COMMAND;
+import static org.apache.brooklyn.test.Asserts.assertNotNull;
+import static org.apache.brooklyn.test.Asserts.assertNull;
+import static org.apache.brooklyn.test.Asserts.assertTrue;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.effector.http.HttpCommandEffector;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.http.BetterMockWebServer;
+import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Resources;
+import com.google.mockwebserver.MockResponse;
+
+public class CompositeEffectorTest extends BrooklynAppUnitTestSupport {
+
+    private static final Logger log = LoggerFactory.getLogger(CompositeEffectorTest.class);
+    private static final String DEFAULT_ENDPOINT = "/";
+
+    final static Effector<List> EFFECTOR_COMPOSITE = Effectors.effector(List.class, "CompositeEffector").buildAbstract();
+
+    protected BetterMockWebServer server;
+    protected URL baseUrl;
+
+    protected Location loc;
+    protected CompositeEffector compositeEffector;
+
+   @BeforeMethod
+   public void start() throws IOException {
+      server = BetterMockWebServer.newInstanceLocalhost();
+      server.play();
+   }
+
+   @AfterMethod(alwaysRun = true)
+   public void stop() throws IOException {
+      server.shutdown();
+   }
+
+   protected String url(String path) {
+      return server.getUrl(path).toString();
+   }
+
+   protected MockResponse jsonResponse(String resource) {
+      return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
+   }
+
+   protected MockResponse response404() {
+      return new MockResponse().setStatus("HTTP/1.1 404 Not Found");
+   }
+
+   protected MockResponse response204() {
+      return new MockResponse().setStatus("HTTP/1.1 204 No Content");
+   }
+
+   protected String stringFromResource(String resourceName) {
+      return stringFromResource("/org/apache/brooklyn/core/effector", resourceName);
+   }
+
+   private String stringFromResource(String prefix, String resourceName) {
+      try {
+         return Resources.toString(getClass().getResource(String.format("%s/%s", prefix, resourceName)), Charsets.UTF_8)
+                 .replace(DEFAULT_ENDPOINT, url(""));
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   @Test
+   public void testCompositeEffectorWithNonExistingName() throws InterruptedException {
+      server.enqueue(jsonResponse("test.json"));
+
+      HttpCommandEffector httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login")
+      );
+      assertNotNull(httpCommandEffector);
+      compositeEffector = new CompositeEffector(ConfigBag.newInstance()
+              .configure(CompositeEffector.EFFECTOR_NAME, EFFECTOR_COMPOSITE.getName())
+              .configure(CompositeEffector.EFFECTORS, ImmutableList.of(EFFECTOR_HTTP_COMMAND.getName()))
+      );
+      assertNotNull(compositeEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector, compositeEffector));
+      List<Object> results = testEntity.invoke(EFFECTOR_COMPOSITE, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      Asserts.assertEquals(results.size(), 1);
+
+      assertTrue(results.get(0) instanceof String);
+      Asserts.assertEquals(results.get(0), "myLogin");
+   }
+
+   @Test
+   public void testCompositeEffectorWithStartName() throws InterruptedException {
+      server.enqueue(jsonResponse("test.json"));
+
+      HttpCommandEffector httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login")
+      );
+      assertNotNull(httpCommandEffector);
+      compositeEffector = new CompositeEffector(ConfigBag.newInstance()
+              .configure(CompositeEffector.EFFECTOR_NAME, "start")
+              .configure(CompositeEffector.EFFECTORS, ImmutableList.of(EFFECTOR_HTTP_COMMAND.getName()))
+      );
+      assertNotNull(compositeEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector, compositeEffector));
+      List<Object> results = testEntity.invoke(Effectors.effector(List.class, "start").buildAbstract(), ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      Asserts.assertEquals(results.size(), 2);
+      assertNull(results.get(0));
+      assertTrue(results.get(1) instanceof String);
+      Asserts.assertEquals(results.get(1), "myLogin");
+   }
+
+   @Test
+   public void testCompositeEffectorWithStartNameAndOverriding() throws InterruptedException {
+      server.enqueue(jsonResponse("test.json"));
+
+      HttpCommandEffector httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login")
+      );
+      assertNotNull(httpCommandEffector);
+      compositeEffector = new CompositeEffector(ConfigBag.newInstance()
+              .configure(CompositeEffector.EFFECTOR_NAME, "start")
+              .configure(CompositeEffector.OVERRIDE, true)
+              .configure(CompositeEffector.EFFECTORS, ImmutableList.of(EFFECTOR_HTTP_COMMAND.getName()))
+      );
+      assertNotNull(compositeEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector, compositeEffector));
+      List<Object> results = testEntity.invoke(Effectors.effector(List.class, "start").buildAbstract(), ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      Asserts.assertEquals(results.size(), 1);
+      assertTrue(results.get(0) instanceof String);
+      Asserts.assertEquals(results.get(0), "myLogin");
+   }
+
+   @Test
+   public void testCompositeEffectorWithStopName() throws InterruptedException {
+      server.enqueue(jsonResponse("test.json"));
+
+      HttpCommandEffector httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login")
+      );
+      assertNotNull(httpCommandEffector);
+      compositeEffector = new CompositeEffector(ConfigBag.newInstance()
+              .configure(CompositeEffector.EFFECTOR_NAME, "stop")
+              .configure(CompositeEffector.EFFECTORS, ImmutableList.of(EFFECTOR_HTTP_COMMAND.getName()))
+      );
+      assertNotNull(compositeEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector, compositeEffector));
+      List<Object> results = testEntity.invoke(Effectors.effector(List.class, "stop").buildAbstract(), ImmutableMap.<String, Object>of()).getUnchecked(Duration.minutes(1));
+      Asserts.assertEquals(results.size(), 2);
+      assertTrue(results.get(0) instanceof String);
+      Asserts.assertEquals(results.get(0), "myLogin");
+      assertNull(results.get(1));
+   }
+
+   @Test
+   public void testCompositeEffectorWithStopNameAndOverriding() throws InterruptedException {
+      server.enqueue(jsonResponse("test.json"));
+
+      HttpCommandEffector httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login")
+      );
+      assertNotNull(httpCommandEffector);
+      compositeEffector = new CompositeEffector(ConfigBag.newInstance()
+              .configure(CompositeEffector.EFFECTOR_NAME, "stop")
+              .configure(CompositeEffector.OVERRIDE, true)
+              .configure(CompositeEffector.EFFECTORS, ImmutableList.of(EFFECTOR_HTTP_COMMAND.getName()))
+      );
+      assertNotNull(compositeEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector, compositeEffector));
+      List<Object> results = testEntity.invoke(Effectors.effector(List.class, "stop").buildAbstract(), ImmutableMap.<String, Object>of()).getUnchecked(Duration.minutes(1));
+      Asserts.assertEquals(results.size(), 1);
+      assertTrue(results.get(0) instanceof String);
+      Asserts.assertEquals(results.get(0), "myLogin");
+   }
+
+   @Test(expectedExceptions = NullPointerException.class)
+   public void testMissingEffectors() {
+      compositeEffector = new CompositeEffector(ConfigBag.newInstance()
+              .configure(CompositeEffector.EFFECTOR_NAME, EFFECTOR_COMPOSITE.getName())
+              .configure(CompositeEffector.EFFECTORS, null)
+      );
+   }
+
+   @Test(expectedExceptions = PropagatedRuntimeException.class)
+   public void testWhenOneEffectorFails() throws InterruptedException {
+      server.enqueue(response404());
+
+      HttpCommandEffector eff1 = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, "eff1")
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/get?login=myLogin"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "GET")
+              .configure(HttpCommandEffector.JSON_PATH, "$.args.login"));
+      compositeEffector = new CompositeEffector(ConfigBag.newInstance()
+              .configure(CompositeEffector.EFFECTOR_NAME, EFFECTOR_COMPOSITE.getName())
+              .configure(CompositeEffector.EFFECTORS, ImmutableList.of("eff1", "eff2"))
+      );
+      assertNotNull(compositeEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(eff1, compositeEffector));
+      List<Object> results = testEntity.invoke(EFFECTOR_COMPOSITE, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      Asserts.assertEquals(results.size(), 2);
+   }
+
+   private EntitySpec<TestEntity> buildEntitySpec(AddEffector... effectors) {
+      EntitySpec<TestEntity> testEntitySpec = EntitySpec.create(TestEntity.class);
+      for (AddEffector effector : effectors) {
+         testEntitySpec.addInitializer(effector);
+      }
+      return testEntitySpec;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
index a4b8ee0..0580c39 100644
--- a/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/effector/http/HttpCommandEffectorTest.java
@@ -44,8 +44,10 @@ import org.testng.annotations.Test;
 
 import com.google.common.base.Charsets;
 import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.io.Resources;
+import com.google.common.net.HttpHeaders;
 import com.google.mockwebserver.MockResponse;
 import com.google.mockwebserver.RecordedRequest;
 
@@ -54,7 +56,7 @@ public class HttpCommandEffectorTest extends BrooklynAppUnitTestSupport {
     private static final Logger log = LoggerFactory.getLogger(HttpCommandEffectorTest.class);
     private static final String DEFAULT_ENDPOINT = "/";
 
-    final static Effector<String> EFFECTOR_HTTP_COMMAND = Effectors.effector(String.class, "http-command-effector").buildAbstract();
+    public final static Effector<String> EFFECTOR_HTTP_COMMAND = Effectors.effector(String.class, "http-command-effector").buildAbstract();
 
     protected BetterMockWebServer server;
     protected URL baseUrl;
@@ -78,7 +80,7 @@ public class HttpCommandEffectorTest extends BrooklynAppUnitTestSupport {
    }
 
    protected MockResponse jsonResponse(String resource) {
-      return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
+      return new MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, "application/json").setBody(stringFromResource(resource));
    }
 
    protected MockResponse response404() {
@@ -144,6 +146,88 @@ public class HttpCommandEffectorTest extends BrooklynAppUnitTestSupport {
    }
 
    @Test
+   public void testPayloadWithContentTypeHeaderJson() throws InterruptedException {
+      server.enqueue(jsonResponse("map-response.json"));
+
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/post"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "POST")
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_PAYLOAD, ImmutableMap.of("key", "value"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_HEADERS, ImmutableMap.of(HttpHeaders.CONTENT_TYPE, "application/json"))
+              .configure(HttpCommandEffector.JSON_PATH, "$.data")
+      );
+      assertNotNull(httpCommandEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      Object output = testEntity.invoke(EFFECTOR_HTTP_COMMAND, ImmutableMap.<String, Object>of()).getUnchecked(Duration.minutes(1));
+      assertEquals(output, "{\"key\", \"value\"}");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "POST", "/post");
+   }
+
+   @Test
+   public void testPayloadWithoutContentTypeHeader() throws InterruptedException {
+      server.enqueue(jsonResponse("map-response.json"));
+
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/post"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "POST")
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_PAYLOAD, ImmutableMap.of("key", "value"))
+              .configure(HttpCommandEffector.JSON_PATH, "$.data")
+      );
+      assertNotNull(httpCommandEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      Object output = testEntity.invoke(EFFECTOR_HTTP_COMMAND, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      assertEquals(output, "{\"key\", \"value\"}");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "POST", "/post");
+   }
+
+   @Test
+   public void testListPayloadWithoutContentTypeHeader() throws InterruptedException {
+      server.enqueue(jsonResponse("list-response.json"));
+
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/post"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "POST")
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_PAYLOAD, ImmutableList.of("key", "value"))
+              .configure(HttpCommandEffector.JSON_PATH, "$.data")
+      );
+      assertNotNull(httpCommandEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      Object output = testEntity.invoke(EFFECTOR_HTTP_COMMAND, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      assertEquals(output, "[\"key\", \"value\"]");
+
+      assertEquals(server.getRequestCount(), "[\"key\", \"value\"]");
+      assertSent(server, "POST", "/post");
+   }
+
+   @Test
+   public void testPayloadWithContentTypeHeaderXml() throws InterruptedException {
+      server.enqueue(jsonResponse("int-response.json"));
+
+      httpCommandEffector = new HttpCommandEffector(ConfigBag.newInstance()
+              .configure(HttpCommandEffector.EFFECTOR_NAME, EFFECTOR_HTTP_COMMAND.getName())
+              .configure(HttpCommandEffector.EFFECTOR_URI, url("/post"))
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_VERB, "POST")
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_PAYLOAD, 1)
+              .configure(HttpCommandEffector.EFFECTOR_HTTP_HEADERS, ImmutableMap.of(HttpHeaders.CONTENT_TYPE, "application/xml"))
+              .configure(HttpCommandEffector.JSON_PATH, "$.data")
+      );
+      assertNotNull(httpCommandEffector);
+      TestEntity testEntity = app.createAndManageChild(buildEntitySpec(httpCommandEffector));
+      Object output = testEntity.invoke(EFFECTOR_HTTP_COMMAND, ImmutableMap.<String, Object>of()).getUnchecked(Duration.seconds(1));
+      assertEquals(output, "1");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "POST", "/post");
+   }
+
+   @Test
    public void testHappyPath() throws InterruptedException {
       server.enqueue(jsonResponse("login.json"));
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/test/resources/org/apache/brooklyn/core/effector/http/int-response.json
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/effector/http/int-response.json b/core/src/test/resources/org/apache/brooklyn/core/effector/http/int-response.json
new file mode 100644
index 0000000..9b133f7
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/effector/http/int-response.json
@@ -0,0 +1,16 @@
+{
+  "args": {},
+  "data": "1",
+  "files": {},
+  "form": {},
+  "headers": {
+    "Accept": "*/*",
+    "Content-Length": "1",
+    "Content-Type": "application/json",
+    "Host": "httpbin.org",
+    "User-Agent": "curl/7.49.1"
+  },
+  "json": 1,
+  "origin": "93.61.99.89",
+  "url": "http://httpbin.org/post"
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/test/resources/org/apache/brooklyn/core/effector/http/list-response.json
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/effector/http/list-response.json b/core/src/test/resources/org/apache/brooklyn/core/effector/http/list-response.json
new file mode 100644
index 0000000..88394ea
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/effector/http/list-response.json
@@ -0,0 +1,19 @@
+{
+  "args": {},
+  "data": "[\"key\", \"value\"]",
+  "files": {},
+  "form": {},
+  "headers": {
+    "Accept": "*/*",
+    "Content-Length": "16",
+    "Content-Type": "application/json",
+    "Host": "httpbin.org",
+    "User-Agent": "curl/7.49.1"
+  },
+  "json": [
+    "key",
+    "value"
+  ],
+  "origin": "93.61.99.89",
+  "url": "http://httpbin.org/post"
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/test/resources/org/apache/brooklyn/core/effector/http/map-response.json
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/effector/http/map-response.json b/core/src/test/resources/org/apache/brooklyn/core/effector/http/map-response.json
new file mode 100644
index 0000000..959b763
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/effector/http/map-response.json
@@ -0,0 +1,16 @@
+{
+  "args": {},
+  "data": "{\"key\", \"value\"}",
+  "files": {},
+  "form": {},
+  "headers": {
+    "Accept": "*/*",
+    "Content-Length": "16",
+    "Content-Type": "application/json",
+    "Host": "httpbin.org",
+    "User-Agent": "curl/7.49.1"
+  },
+  "json": null,
+  "origin": "93.61.99.89",
+  "url": "http://httpbin.org/post"
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/b32581dd/core/src/test/resources/org/apache/brooklyn/core/effector/test.json
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/effector/test.json b/core/src/test/resources/org/apache/brooklyn/core/effector/test.json
new file mode 100644
index 0000000..b39889a
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/effector/test.json
@@ -0,0 +1,16 @@
+{
+  "args": {
+    "login": "myLogin"
+  },
+  "headers": {
+    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+    "Accept-Encoding": "gzip, deflate, sdch, br",
+    "Accept-Language": "en-US,en;q=0.8,it;q=0.6",
+    "Cookie": "_ga=GA1.2.1060288368.1484053495; _gat=1",
+    "Host": "httpbin.org",
+    "Upgrade-Insecure-Requests": "1",
+    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36"
+  },
+  "origin": "93.61.99.89",
+  "url": "https://httpbin.org/get?login=myLogin"
+}


[09/10] brooklyn-server git commit: This closes #555

Posted by he...@apache.org.
This closes #555


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/fd033127
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/fd033127
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/fd033127

Branch: refs/heads/master
Commit: fd033127c2661175c8142094faaf1c1c0b969bf8
Parents: 39301e0 070c6f5
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Feb 15 18:47:17 2017 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Wed Feb 15 18:47:17 2017 +0000

----------------------------------------------------------------------
 .../brooklyn/util/time/DurationPredicates.java  | 162 +++++++++++++++++++
 .../util/time/DurationPredicatesTest.java       | 150 +++++++++++++++++
 2 files changed, 312 insertions(+)
----------------------------------------------------------------------