You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sv...@apache.org on 2016/09/06 11:54:45 UTC

[03/10] brooklyn-server git commit: Refactoring HttpFeed to use the new HttpExecutor interface.

Refactoring HttpFeed to use the new HttpExecutor interface.


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

Branch: refs/heads/master
Commit: 5faa686bfc4752554eaf20facdffbce6d0850673
Parents: e785c29
Author: Yavor Yanchev <ya...@yanchev.com>
Authored: Tue Aug 16 09:39:48 2016 +0300
Committer: Yavor Yanchev <ya...@yanchev.com>
Committed: Sat Aug 27 03:29:50 2016 +0300

----------------------------------------------------------------------
 .../core/location/AbstractLocation.java         |  30 ++++
 .../core/location/LocationConfigKeys.java       |   5 +-
 .../location/cloud/CloudLocationConfig.java     |   5 +
 .../org/apache/brooklyn/feed/http/HttpFeed.java | 136 ++++++++++++-------
 .../location/byon/ByonLocationResolver.java     |   2 +
 .../util/http/executor/HttpExecutorFactory.java |  29 ++++
 .../http/executor/HttpExecutorFactoryImpl.java  |  75 ++++++++++
 .../apache/brooklyn/feed/http/HttpFeedTest.java |  16 ++-
 .../util/http/executor/Credentials.java         |  18 +++
 .../brooklyn/util/http/executor/HttpConfig.java |  18 +++
 .../util/http/executor/HttpExecutor.java        |  18 +++
 .../util/http/executor/HttpRequest.java         |  18 +++
 .../util/http/executor/HttpRequestImpl.java     |  18 +++
 .../util/http/executor/HttpResponse.java        |  18 +++
 .../util/http/executor/HttpResponseImpl.java    |  18 +++
 .../executor/apacheclient/HttpExecutorImpl.java |  26 ++++
 .../apacheclient/HttpResponseWrapper.java       |  18 +++
 17 files changed, 411 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
index 1ca8c2f..fb41282 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/AbstractLocation.java
@@ -65,6 +65,7 @@ import org.apache.brooklyn.core.mgmt.rebind.BasicLocationRebindSupport;
 import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
 import org.apache.brooklyn.core.objs.AbstractConfigurationSupportInternal;
 import org.apache.brooklyn.util.collections.SetFromLiveMap;
+import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
@@ -718,6 +719,35 @@ public abstract class AbstractLocation extends AbstractBrooklynObject implements
         return removed;
     }
 
+    public void init() {
+        super.init();
+        loadExtension();
+    }
+
+    public void rebind() {
+        super.rebind();
+        loadExtension();
+    }
+
+    private void loadExtension() {
+        try {
+            Map<String, String> extensions = getConfig(LocationConfigKeys.EXTENSIONS);
+            if (extensions != null) {
+                for (Map.Entry<String, String> extension: extensions.entrySet()) {
+                    Class<?> extensionClassType =  new ClassLoaderUtils(this, getManagementContext()).loadClass(extension.getKey());
+
+                    if (!hasExtension(extensionClassType)) {
+                        Object extensionClass = new ClassLoaderUtils(this, getManagementContext()).loadClass(extension.getValue()).newInstance();
+                        addExtension((Class)extensionClassType, extensionClass);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            LOG.error("Location extension can not be loaded {}", e);
+            throw Exceptions.propagate(e);
+        }
+    }
+
     protected void onChanged() {
         // currently changes simply trigger re-persistence; there is no intermediate listener as we do for EntityChangeListener
         if (isManaged()) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/main/java/org/apache/brooklyn/core/location/LocationConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/LocationConfigKeys.java b/core/src/main/java/org/apache/brooklyn/core/location/LocationConfigKeys.java
index e8e8db6..715f39a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/LocationConfigKeys.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/LocationConfigKeys.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.BasicConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.MapConfigKey;
 import org.apache.brooklyn.util.os.Os;
 
 import com.google.common.base.CaseFormat;
@@ -35,7 +36,9 @@ public class LocationConfigKeys {
     public static final ConfigKey<String> DISPLAY_NAME = ConfigKeys.newStringConfigKey("displayName");
     public static final ConfigKey<Boolean> ENABLED = ConfigKeys.newBooleanConfigKey("enabled", "Whether the location is enabled for listing and use "
         + "(only supported for selected locations)", true);
-    
+
+    public static final MapConfigKey<String> EXTENSIONS = new MapConfigKey<String>(String.class, "extensions", "Location extensions");
+
     public static final ConfigKey<String> ACCESS_IDENTITY = ConfigKeys.newStringConfigKey("identity"); 
     public static final ConfigKey<String> ACCESS_CREDENTIAL = ConfigKeys.newStringConfigKey("credential"); 
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java b/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java
index f749f64..77d8e06 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java
@@ -27,6 +27,7 @@ import org.apache.brooklyn.api.location.MachineLocationCustomizer;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.BasicConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.MapConfigKey;
 import org.apache.brooklyn.core.location.LocationConfigKeys;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
 
@@ -36,6 +37,10 @@ public interface CloudLocationConfig {
     public static final ConfigKey<String> CLOUD_REGION_ID = LocationConfigKeys.CLOUD_REGION_ID;
     public static final ConfigKey<String> CLOUD_AVAILABILITY_ZONE_ID = LocationConfigKeys.CLOUD_AVAILABILITY_ZONE_ID;
         
+
+    @SetFromFlag("extensions")
+    public static final MapConfigKey<String> EXTENSION = LocationConfigKeys.EXTENSIONS;
+
     @SetFromFlag("identity")
     public static final ConfigKey<String> ACCESS_IDENTITY = LocationConfigKeys.ACCESS_IDENTITY;
     @SetFromFlag("credential")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/main/java/org/apache/brooklyn/feed/http/HttpFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/feed/http/HttpFeed.java b/core/src/main/java/org/apache/brooklyn/feed/http/HttpFeed.java
index d3556be..b0d4383 100644
--- a/core/src/main/java/org/apache/brooklyn/feed/http/HttpFeed.java
+++ b/core/src/main/java/org/apache/brooklyn/feed/http/HttpFeed.java
@@ -20,8 +20,10 @@ package org.apache.brooklyn.feed.http;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.io.IOException;
 import java.net.URI;
 import java.net.URL;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -29,6 +31,7 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.Entities;
@@ -36,13 +39,20 @@ import org.apache.brooklyn.core.feed.AbstractFeed;
 import org.apache.brooklyn.core.feed.AttributePollHandler;
 import org.apache.brooklyn.core.feed.DelegatingPollHandler;
 import org.apache.brooklyn.core.feed.Poller;
-import org.apache.brooklyn.util.http.HttpTool;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.location.Machines;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.http.HttpToolResponse;
-import org.apache.brooklyn.util.http.HttpTool.HttpClientBuilder;
+import org.apache.brooklyn.util.http.executor.Credentials.BasicAuth;
+import org.apache.brooklyn.util.http.executor.HttpConfig;
+import org.apache.brooklyn.util.http.executor.HttpExecutor;
+import org.apache.brooklyn.util.http.executor.HttpExecutorFactory;
+import org.apache.brooklyn.util.http.executor.HttpRequest;
+import org.apache.brooklyn.util.http.executor.HttpResponse;
+import org.apache.brooklyn.util.http.executor.apacheclient.HttpExecutorImpl;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.HttpClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,6 +66,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
+import com.google.common.io.ByteStreams;
 import com.google.common.reflect.TypeToken;
 
 /**
@@ -127,6 +138,7 @@ public class HttpFeed extends AbstractFeed {
         private boolean suspended = false;
         private Credentials credentials;
         private String uniqueTag;
+        private HttpExecutor httpExecutor;
         private volatile boolean built;
 
         public Builder entity(EntityLocal val) {
@@ -207,6 +219,10 @@ public class HttpFeed extends AbstractFeed {
             this.uniqueTag = uniqueTag;
             return this;
         }
+        public Builder httpExecutor(HttpExecutor val) {
+            this.httpExecutor = val;
+            return this;
+        }
         public HttpFeed build() {
             built = true;
             HttpFeed result = new HttpFeed(this);
@@ -222,6 +238,7 @@ public class HttpFeed extends AbstractFeed {
     }
     
     private static class HttpPollIdentifier {
+        final HttpExecutor httpExecutor;
         final String method;
         final Supplier<URI> uriProvider;
         final Map<String,String> headers;
@@ -229,8 +246,9 @@ public class HttpFeed extends AbstractFeed {
         final Optional<Credentials> credentials;
         final Duration connectionTimeout;
         final Duration socketTimeout;
-        private HttpPollIdentifier(String method, Supplier<URI> uriProvider, Map<String, String> headers, byte[] body,
-                                   Optional<Credentials> credentials, Duration connectionTimeout, Duration socketTimeout) {
+        private HttpPollIdentifier(HttpExecutor httpExecutor, String method, Supplier<URI> uriProvider, Map<String, String> headers,
+                                   byte[] body, Optional<Credentials> credentials, Duration connectionTimeout, Duration socketTimeout) {
+            this.httpExecutor =  httpExecutor;
             this.method = checkNotNull(method, "method").toLowerCase();
             this.uriProvider = checkNotNull(uriProvider, "uriProvider");
             this.headers = checkNotNull(headers, "headers");
@@ -262,6 +280,7 @@ public class HttpFeed extends AbstractFeed {
                     Objects.equal(uriProvider, o.uriProvider) &&
                     Objects.equal(headers, o.headers) &&
                     Objects.equal(body, o.body) &&
+                    Objects.equal(httpExecutor, o.httpExecutor) &&
                     Objects.equal(credentials, o.credentials);
         }
     }
@@ -275,7 +294,23 @@ public class HttpFeed extends AbstractFeed {
     protected HttpFeed(Builder builder) {
         setConfig(ONLY_IF_SERVICE_UP, builder.onlyIfServiceUp);
         Map<String,String> baseHeaders = ImmutableMap.copyOf(checkNotNull(builder.headers, "headers"));
-        
+
+        HttpExecutor httpExecutor;
+        if (builder.httpExecutor != null) {
+            httpExecutor = builder.httpExecutor;
+        } else {
+            HttpExecutorFactory httpExecutorFactory = null;
+            Collection<? extends Location> locations = Locations.getLocationsCheckingAncestors(builder.entity.getLocations(), builder.entity);
+            Maybe<Location> location =  Machines.findUniqueElement(locations, Location.class);
+            if (location.isPresent() && location.get().hasExtension(HttpExecutorFactory.class)) {
+                httpExecutorFactory = location.get().getExtension(HttpExecutorFactory.class);
+                Map<String, Object> httpExecutorProps = location.get().getAllConfig(true);
+                httpExecutor = httpExecutorFactory.getHttpExecutor(httpExecutorProps);
+            } else {
+                httpExecutor = HttpExecutorImpl.newInstance();
+            }
+        }
+
         SetMultimap<HttpPollIdentifier, HttpPollConfig<?>> polls = HashMultimap.<HttpPollIdentifier,HttpPollConfig<?>>create();
         for (HttpPollConfig<?> config : builder.polls) {
             if (!config.isEnabled()) continue;
@@ -302,7 +337,7 @@ public class HttpFeed extends AbstractFeed {
             }
             checkNotNull(baseUriProvider);
 
-            polls.put(new HttpPollIdentifier(method, baseUriProvider, headers, body, credentials, connectionTimeout, socketTimeout), configCopy);
+            polls.put(new HttpPollIdentifier(httpExecutor, method, baseUriProvider, headers, body, credentials, connectionTimeout, socketTimeout), configCopy);
         }
         setConfig(POLLS, polls);
         initUniqueTag(builder.uniqueTag, polls.values());
@@ -311,7 +346,7 @@ public class HttpFeed extends AbstractFeed {
     @Override
     protected void preStart() {
         SetMultimap<HttpPollIdentifier, HttpPollConfig<?>> polls = getConfig(POLLS);
-        
+
         for (final HttpPollIdentifier pollInfo : polls.keySet()) {
             // Though HttpClients are thread safe and can take advantage of connection pooling
             // and authentication caching, the httpcomponents documentation says:
@@ -319,7 +354,6 @@ public class HttpFeed extends AbstractFeed {
             //     threads of execution, it is highly recommended that each thread maintains its
             //     own dedicated instance of HttpContext.
             //  http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html
-            final HttpClient httpClient = createHttpClient(pollInfo);
 
             Set<HttpPollConfig<?>> configs = polls.get(pollInfo);
             long minPeriod = Integer.MAX_VALUE;
@@ -331,52 +365,54 @@ public class HttpFeed extends AbstractFeed {
             }
 
             Callable<HttpToolResponse> pollJob;
-            
-            if (pollInfo.method.equals("get")) {
-                pollJob = new Callable<HttpToolResponse>() {
-                    public HttpToolResponse call() throws Exception {
-                        if (log.isTraceEnabled()) log.trace("http polling for {} sensors at {}", entity, pollInfo);
-                        return HttpTool.httpGet(httpClient, pollInfo.uriProvider.get(), pollInfo.headers);
-                    }};
-            } else if (pollInfo.method.equals("post")) {
-                pollJob = new Callable<HttpToolResponse>() {
-                    public HttpToolResponse call() throws Exception {
-                        if (log.isTraceEnabled()) log.trace("http polling for {} sensors at {}", entity, pollInfo);
-                        return HttpTool.httpPost(httpClient, pollInfo.uriProvider.get(), pollInfo.headers, pollInfo.body);
-                    }};
-            } else if (pollInfo.method.equals("head")) {
-                pollJob = new Callable<HttpToolResponse>() {
-                    public HttpToolResponse call() throws Exception {
-                        if (log.isTraceEnabled()) log.trace("http polling for {} sensors at {}", entity, pollInfo);
-                        return HttpTool.httpHead(httpClient, pollInfo.uriProvider.get(), pollInfo.headers);
-                    }};
-            } else {
-                throw new IllegalStateException("Unexpected http method: "+pollInfo.method);
-            }
-            
-            getPoller().scheduleAtFixedRate(pollJob, new DelegatingPollHandler<HttpToolResponse>(handlers), minPeriod);
-        }
-    }
+            pollJob = new Callable<HttpToolResponse>() {
+                public HttpToolResponse call() throws Exception {
+                    if (log.isTraceEnabled()) log.trace("http polling for {} sensors at {}", entity, pollInfo);
 
-    // TODO Should we really trustAll for https? Make configurable?
-    private HttpClient createHttpClient(HttpPollIdentifier pollIdentifier) {
-        URI uri = pollIdentifier.uriProvider.get();
-        HttpClientBuilder builder = HttpTool.httpClientBuilder()
-                .trustAll()
-                .laxRedirect(true);
-        if (uri != null) builder.uri(uri);
-        if (uri != null) builder.credential(pollIdentifier.credentials);
-        if (pollIdentifier.connectionTimeout != null) {
-            builder.connectionTimeout(pollIdentifier.connectionTimeout);
-        }
-        if (pollIdentifier.socketTimeout != null) {
-            builder.socketTimeout(pollIdentifier.socketTimeout);
+                    BasicAuth creds = null;
+                    if (pollInfo.credentials.isPresent()) {
+                        creds =  org.apache.brooklyn.util.http.executor.Credentials.basic(
+                                pollInfo.credentials.get().getUserPrincipal().getName(),
+                                pollInfo.credentials.get().getPassword());
+                    }
+
+                    HttpResponse response =  pollInfo.httpExecutor.execute(new HttpRequest.Builder()
+                            .headers(pollInfo.headers)
+                            .uri(pollInfo.uriProvider.get())
+                            .credentials(creds)
+                            .method(pollInfo.method)
+                            .body(pollInfo.body)
+                            .config(HttpConfig.builder()
+                                    .trustSelfSigned(true)
+                                    .trustAll(true)
+                                    .laxRedirect(true)
+                                    .build())
+                            .build());
+                    return createHttpToolRespose(response);
+                }};
+                getPoller().scheduleAtFixedRate(pollJob, new DelegatingPollHandler(handlers), minPeriod);
         }
-        return builder.build();
     }
 
     @SuppressWarnings("unchecked")
     protected Poller<HttpToolResponse> getPoller() {
-        return (Poller<HttpToolResponse>) super.getPoller();
+        return  (Poller<HttpToolResponse>) super.getPoller();
+    }
+
+    @SuppressWarnings("unchecked")
+    private HttpToolResponse createHttpToolRespose(HttpResponse response) throws IOException {
+        int responseCode = response.code();
+        if (responseCode == 400) { // Unprocessable Entity - https://stackoverflow.com/questions/6123425/rest-response-code-for-invalid-data
+            throw new IOException(" Unprocessable Entity: " + response.reasonPhrase());
+        }
+        Map<String,? extends List<String>> headers = (Map<String, List<String>>) (Map<?, ?>) response.headers().asMap();
+
+        return new HttpToolResponse(responseCode,
+                headers,
+                ByteStreams.toByteArray(response.getContent()),
+                System.currentTimeMillis(),
+                0,
+                0);
     }
 }
+

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
index ca478fd..05824dd 100644
--- a/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
+++ b/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.location.AbstractLocationResolver;
+import org.apache.brooklyn.core.location.LocationConfigKeys;
 import org.apache.brooklyn.core.mgmt.internal.LocalLocationManager;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.ClassLoaderUtils;
@@ -108,6 +109,7 @@ public class ByonLocationResolver extends AbstractLocationResolver {
         MutableMap<String, Object> defaultProps = MutableMap.of();
         defaultProps.addIfNotNull("user", user);
         defaultProps.addIfNotNull("port", port);
+        defaultProps.addIfNotNull(LocationConfigKeys.EXTENSIONS.getName(), config.get(LocationConfigKeys.EXTENSIONS));
 
         List<?> hostAddresses;
         

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactory.java b/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactory.java
new file mode 100644
index 0000000..b6489f4
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactory.java
@@ -0,0 +1,29 @@
+/*
+ * 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.http.executor;
+
+import java.util.Map;
+
+public interface HttpExecutorFactory {
+    public static final String HTTP_EXECUTOR_CLASS = "httpExecutorClass";
+
+    public static final String HTTP_EXECUTOR_CLASS_CONFIG_PREFIX = "httpExecutorClass.";
+
+    HttpExecutor getHttpExecutor(Map<?, ?> props);
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactoryImpl.java b/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactoryImpl.java
new file mode 100644
index 0000000..1d392e1
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutorFactoryImpl.java
@@ -0,0 +1,75 @@
+/*
+ * 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.http.executor;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.ClassLoaderUtils;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.http.executor.apacheclient.HttpExecutorImpl;
+import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Maps;
+
+public class HttpExecutorFactoryImpl implements HttpExecutorFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(HttpExecutorFactoryImpl.class);
+
+    public HttpExecutorFactoryImpl() {
+        // no-op
+    }
+
+    @Override
+    public HttpExecutor getHttpExecutor(Map<?, ?> props) {
+        HttpExecutor httpExecutor;
+
+        String httpExecutorClass = (String) props.get(HTTP_EXECUTOR_CLASS);
+        if (httpExecutorClass != null) {
+            Map<?, ?> executorProps = Maps.filterKeys(props, StringPredicates.isStringStartingWith(HTTP_EXECUTOR_CLASS_CONFIG_PREFIX));
+            if (executorProps.size() > 0) {
+                Map<String, String> httpExecutorProps = MutableMap.of();
+                for (Entry<?, ?> entry: executorProps.entrySet()) {
+                    String keyName = Strings.removeFromStart((String)entry.getKey(), HTTP_EXECUTOR_CLASS_CONFIG_PREFIX);
+                    httpExecutorProps.put(keyName, TypeCoercions.coerce(entry.getValue(), String.class));
+                }
+
+                try {
+                    httpExecutor = (HttpExecutor) new ClassLoaderUtils(HttpExecutorFactoryImpl.class).loadClass(httpExecutorClass).getConstructor(Map.class).newInstance(httpExecutorProps);
+                } catch (Exception e) {
+                    throw Exceptions.propagate(e);
+                }
+            } else {
+                LOG.error("Missing parameters for: " + HTTP_EXECUTOR_CLASS);
+                throw Exceptions.propagate(new IllegalArgumentException("Missing parameters for: " + HTTP_EXECUTOR_CLASS));
+            }
+        } else {
+            LOG.warn(HTTP_EXECUTOR_CLASS + " parameter not provided. Using the default implementation " + HttpExecutorImpl.class.getName());
+            httpExecutor = HttpExecutorImpl.newInstance();
+        }
+
+        return httpExecutor;
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/core/src/test/java/org/apache/brooklyn/feed/http/HttpFeedTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/feed/http/HttpFeedTest.java b/core/src/test/java/org/apache/brooklyn/feed/http/HttpFeedTest.java
index 63c3c5b..1ecef40 100644
--- a/core/src/test/java/org/apache/brooklyn/feed/http/HttpFeedTest.java
+++ b/core/src/test/java/org/apache/brooklyn/feed/http/HttpFeedTest.java
@@ -71,12 +71,12 @@ public class HttpFeedTest extends BrooklynAppUnitTestSupport {
 
     private static final long TIMEOUT_MS = 10*1000;
     
-    private BetterMockWebServer server;
-    private URL baseUrl;
+    protected BetterMockWebServer server;
+    protected URL baseUrl;
     
-    private Location loc;
-    private EntityLocal entity;
-    private HttpFeed feed;
+    protected Location loc;
+    protected EntityLocal entity;
+    protected HttpFeed feed;
     
     @BeforeMethod(alwaysRun=true)
     @Override
@@ -89,11 +89,15 @@ public class HttpFeedTest extends BrooklynAppUnitTestSupport {
         server.play();
         baseUrl = server.getUrl("/");
 
-        loc = app.newLocalhostProvisioningLocation();
+        loc = newLocation();
         entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
         app.start(ImmutableList.of(loc));
     }
 
+    protected Location newLocation() {
+        return app.newLocalhostProvisioningLocation();
+    }
+
     @AfterMethod(alwaysRun=true)
     @Override
     public void tearDown() throws Exception {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/Credentials.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/Credentials.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/Credentials.java
index 2ca1c8a..f46045d 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/Credentials.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/Credentials.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpConfig.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpConfig.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpConfig.java
index d0f5369..e9ac8eb 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpConfig.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpConfig.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutor.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutor.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutor.java
index 96891e4..26d8b1a 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutor.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpExecutor.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor;
 
 import java.io.IOException;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequest.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequest.java
index e110854..d7b6539 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequest.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequest.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor;
 
 import static com.google.common.base.Preconditions.checkNotNull;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequestImpl.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequestImpl.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequestImpl.java
index b3c21d2..c2e19e9 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequestImpl.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpRequestImpl.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor;
 
 import static com.google.common.base.Preconditions.checkNotNull;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponse.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponse.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponse.java
index 18fb5e8..c3baaf6 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponse.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponse.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor;
 
 import static com.google.common.base.Preconditions.checkNotNull;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponseImpl.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponseImpl.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponseImpl.java
index 59c4acd..a28aa05 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponseImpl.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/HttpResponseImpl.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor;
 
 import static com.google.common.base.Preconditions.checkNotNull;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpExecutorImpl.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpExecutorImpl.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpExecutorImpl.java
index 03631a0..e973843 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpExecutorImpl.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpExecutorImpl.java
@@ -1,6 +1,25 @@
+/*
+ * 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.http.executor.apacheclient;
 
 import java.io.IOException;
+import java.util.Map;
 
 import org.apache.brooklyn.util.http.HttpTool;
 import org.apache.brooklyn.util.http.HttpTool.HttpClientBuilder;
@@ -40,6 +59,13 @@ public class HttpExecutorImpl implements HttpExecutor {
     }
 
     private final HttpClientBuilder baseBuilder;
+
+    /**
+     * A must have constructor.
+     */
+    public HttpExecutorImpl(Map<?, ?> props) {
+        this(HttpTool.httpClientBuilder());
+    }
     
     protected HttpExecutorImpl(HttpClientBuilder baseBuilder) {
         this.baseBuilder = baseBuilder;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5faa686b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpResponseWrapper.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpResponseWrapper.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpResponseWrapper.java
index dc15eac..186f65e 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpResponseWrapper.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/executor/apacheclient/HttpResponseWrapper.java
@@ -1,3 +1,21 @@
+/*
+ * 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.http.executor.apacheclient;
 
 import static com.google.common.base.Preconditions.checkNotNull;