HttpValueFunctions: not anonymous inner classes - HttpValueFunctions uses static classes rather than anonymous inner classes (because they are extremely brittle in persisted state) - Adds containsHeader(), used by nginx - Nginx uses static classes, rather than anonymous inner classes
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/ee3c1ca6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/ee3c1ca6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/ee3c1ca6 Branch: refs/heads/master Commit: ee3c1ca6eb5d045bc74adc72438f613a1a912dc8 Parents: 0dc3911 Author: Aled Sage <[email protected]> Authored: Sat Apr 18 12:44:35 2015 +0100 Committer: Aled Sage <[email protected]> Committed: Tue Aug 11 17:51:12 2015 +0100 ---------------------------------------------------------------------- .../event/feed/http/HttpValueFunctions.java | 55 +++++++++++- .../brooklyn/util/http/HttpToolResponse.java | 2 +- .../event/feed/http/HttpValueFunctionsTest.java | 94 ++++++++++++++++++++ .../entity/proxy/nginx/NginxControllerImpl.java | 23 +++-- 4 files changed, 163 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee3c1ca6/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java b/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java index bcd79ad..3e7e6b2 100644 --- a/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java +++ b/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java @@ -34,6 +34,11 @@ public class HttpValueFunctions { private HttpValueFunctions() {} // instead use static utility methods public static Function<HttpToolResponse, Integer> responseCode() { + return new ResponseCode(); + } + + /** @deprecated since 0.7.0; only here for deserialization of persisted state */ + private static Function<HttpToolResponse, Integer> responseCodeLegacy() { return new Function<HttpToolResponse, Integer>() { @Override public Integer apply(HttpToolResponse input) { return input.getResponseCode(); @@ -41,6 +46,12 @@ public class HttpValueFunctions { }; } + private static class ResponseCode implements Function<HttpToolResponse, Integer> { + @Override public Integer apply(HttpToolResponse input) { + return input.getResponseCode(); + } + } + public static Function<HttpToolResponse, Boolean> responseCodeEquals(final int expected) { return Functionals.chain(HttpValueFunctions.responseCode(), Functions.forPredicate(Predicates.equalTo(expected))); } @@ -52,15 +63,26 @@ public class HttpValueFunctions { } return Functionals.chain(HttpValueFunctions.responseCode(), Functions.forPredicate(Predicates.in(expectedList))); } - + public static Function<HttpToolResponse, String> stringContentsFunction() { + return new StringContents(); + } + + /** @deprecated since 0.7.0; only here for deserialization of persisted state */ + private static Function<HttpToolResponse, String> stringContentsFunctionLegacy() { return new Function<HttpToolResponse, String>() { @Override public String apply(HttpToolResponse input) { return input.getContentAsString(); } }; } - + + private static class StringContents implements Function<HttpToolResponse, String> { + @Override public String apply(HttpToolResponse input) { + return input.getContentAsString(); + } + } + public static Function<HttpToolResponse, JsonElement> jsonContents() { return Functionals.chain(stringContentsFunction(), JsonFunctions.asJson()); } @@ -78,13 +100,42 @@ public class HttpValueFunctions { } public static Function<HttpToolResponse, Long> latency() { + return new Latency(); + } + + /** @deprecated since 0.7.0; only here for deserialization of persisted state */ + private static Function<HttpToolResponse, Long> latencyLegacy() { return new Function<HttpToolResponse, Long>() { public Long apply(HttpToolResponse input) { return input.getLatencyFullContent(); } }; } + + private static class Latency implements Function<HttpToolResponse, Long> { + public Long apply(HttpToolResponse input) { + return input.getLatencyFullContent(); + } + }; + + public static Function<HttpToolResponse, Boolean> containsHeader(String header) { + return new ContainsHeader(header); + } + + private static class ContainsHeader implements Function<HttpToolResponse, Boolean> { + private final String header; + + public ContainsHeader(String header) { + this.header = header; + } + @Override + public Boolean apply(HttpToolResponse input) { + List<String> actual = input.getHeaderLists().get(header); + return actual != null && actual.size() > 0; + } + } + /** @deprecated since 0.7.0 use {@link Functionals#chain(Function, Function)} */ @Deprecated public static <A,B,C> Function<A,C> chain(final Function<A,? extends B> f1, final Function<B,C> f2) { return Functionals.chain(f1, f2); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee3c1ca6/core/src/main/java/brooklyn/util/http/HttpToolResponse.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/util/http/HttpToolResponse.java b/core/src/main/java/brooklyn/util/http/HttpToolResponse.java index 1722e41..1837a87 100644 --- a/core/src/main/java/brooklyn/util/http/HttpToolResponse.java +++ b/core/src/main/java/brooklyn/util/http/HttpToolResponse.java @@ -86,7 +86,7 @@ public class HttpToolResponse implements HttpPollValue { } } - public HttpToolResponse(int responseCode, Map<String,List<String>> headers, byte[] content, + public HttpToolResponse(int responseCode, Map<String,? extends List<String>> headers, byte[] content, long startTime, long durationMillisOfFirstResponse, long durationMillisOfFullContent) { this.response = null; this.responseCode = responseCode; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee3c1ca6/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java b/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java new file mode 100644 index 0000000..7769427 --- /dev/null +++ b/core/src/test/java/brooklyn/event/feed/http/HttpValueFunctionsTest.java @@ -0,0 +1,94 @@ +/* + * 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 brooklyn.event.feed.http; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.util.NoSuchElementException; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.util.http.HttpToolResponse; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; + +public class HttpValueFunctionsTest { + + private int responseCode = 200; + private long fullLatency = 1000; + private String headerName = "my_header"; + private String headerVal = "my_header_val"; + private String bodyKey = "mykey"; + private String bodyVal = "myvalue"; + private String body = "{"+bodyKey+":"+bodyVal+"}"; + private long now; + private HttpToolResponse response; + + @BeforeMethod + public void setUp() throws Exception { + now = System.currentTimeMillis(); + response = new HttpToolResponse(responseCode, ImmutableMap.of(headerName, ImmutableList.of(headerVal)), + body.getBytes(), now-fullLatency, fullLatency / 2, fullLatency); + } + + @Test + public void testResponseCode() throws Exception { + assertEquals(HttpValueFunctions.responseCode().apply(response), Integer.valueOf(responseCode)); + } + + @Test + public void testContainsHeader() throws Exception { + assertTrue(HttpValueFunctions.containsHeader(headerName).apply(response)); + assertFalse(HttpValueFunctions.containsHeader("wrong_header").apply(response)); + } + + @Test + public void testStringContents() throws Exception { + assertEquals(HttpValueFunctions.stringContentsFunction().apply(response), body); + } + + @Test + public void testJsonContents() throws Exception { + JsonElement json = HttpValueFunctions.jsonContents().apply(response); + assertTrue(json.isJsonObject()); + assertEquals(json.getAsJsonObject().entrySet(), ImmutableMap.of(bodyKey, new JsonPrimitive(bodyVal)).entrySet()); + } + + @Test + public void testJsonContentsGettingElement() throws Exception { + assertEquals(HttpValueFunctions.jsonContents(bodyKey, String.class).apply(response), bodyVal); + } + + @Test(expectedExceptions=NoSuchElementException.class) + public void testJsonContentsGettingMissingElement() throws Exception { + assertNull(HttpValueFunctions.jsonContents("wrongkey", String.class).apply(response)); + } + + @Test + public void testLatency() throws Exception { + assertEquals(HttpValueFunctions.latency().apply(response), Long.valueOf(fullLatency)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ee3c1ca6/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java ---------------------------------------------------------------------- diff --git a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java index dd34e69..0253f96 100644 --- a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java +++ b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java @@ -44,6 +44,7 @@ import brooklyn.event.SensorEventListener; import brooklyn.event.feed.ConfigToAttributes; import brooklyn.event.feed.http.HttpFeed; import brooklyn.event.feed.http.HttpPollConfig; +import brooklyn.event.feed.http.HttpValueFunctions; import brooklyn.management.SubscriptionHandle; import brooklyn.policy.PolicySpec; import brooklyn.util.ResourceUtils; @@ -117,17 +118,23 @@ public class NginxControllerImpl extends AbstractControllerImpl implements Nginx .poll(new HttpPollConfig<Boolean>(NGINX_URL_ANSWERS_NICELY) // Any response from Nginx is good. .checkSuccess(Predicates.alwaysTrue()) - .onResult(new Function<HttpToolResponse, Boolean>() { - @Override - public Boolean apply(HttpToolResponse input) { - // Accept any nginx response (don't assert specific version), so that sub-classing - // for a custom nginx build is not strict about custom version numbers in headers - List<String> actual = input.getHeaderLists().get("Server"); - return actual != null && actual.size() == 1; - }}) + // Accept any nginx response (don't assert specific version), so that sub-classing + // for a custom nginx build is not strict about custom version numbers in headers + .onResult(HttpValueFunctions.containsHeader("Server")) .setOnException(false)) .build()); + // TODO PERSISTENCE WORKAROUND kept anonymous function in case referenced in persisted state + new Function<HttpToolResponse, Boolean>() { + @Override + public Boolean apply(HttpToolResponse input) { + // Accept any nginx response (don't assert specific version), so that sub-classing + // for a custom nginx build is not strict about custom version numbers in headers + List<String> actual = input.getHeaderLists().get("Server"); + return actual != null && actual.size() == 1; + } + }; + if (!Lifecycle.RUNNING.equals(getAttribute(SERVICE_STATE_ACTUAL))) { // TODO when updating the map, if it would change from empty to empty on a successful run // gate with the above check to prevent flashing on ON_FIRE during rebind (this is invoked on rebind as well as during start)
