MEECROWAVE-197 minor fixes about the proxy config

git-svn-id: https://svn.apache.org/repos/asf/openwebbeans/meecrowave/trunk@1860782 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java
index 9c0d71c..bf91141 100644
--- a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java
+++ b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/configuration/Routes.java
@@ -19,6 +19,10 @@
 package org.apache.meecrowave.proxy.servlet.configuration;
 
 import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+
+import javax.json.bind.annotation.JsonbTransient;
+import javax.ws.rs.client.Client;
 
 public class Routes {
     public Route defaultRoute;
@@ -33,6 +37,13 @@
         public String id;
         public RequestConfiguration requestConfiguration;
         public ResponseConfiguration responseConfiguration;
+        public ClientConfiguration clientConfiguration;
+
+        @JsonbTransient
+        public Client client;
+
+        @JsonbTransient
+        public ExecutorService executor;
 
         @Override
         public String toString() {
@@ -40,6 +51,72 @@
         }
     }
 
+    public static class ExecutorConfiguration {
+        public int core = 8;
+        public int max = 512;
+        public long keepAlive = 60000;
+        public long shutdownTimeout = 1;
+
+        @Override
+        public String toString() {
+            return "ExecutorConfiguration{" +
+                    "core=" + core +
+                    ", max=" + max +
+                    ", keepAlive=" + keepAlive +
+                    ", shutdownTimeout=" + shutdownTimeout +
+                    '}';
+        }
+    }
+
+    public static class TimeoutConfiguration {
+        public long read = 30000;
+        public long connect = 30000;
+        public long execution = 60000;
+
+        @Override
+        public String toString() {
+            return "TimeoutConfiguration{" +
+                    "read=" + read +
+                    ", connect=" + connect +
+                    '}';
+        }
+    }
+
+    public static class ClientConfiguration {
+        public TimeoutConfiguration timeouts;
+        public ExecutorConfiguration executor;
+        public SslConfiguration sslConfiguration;
+
+        @Override
+        public String toString() {
+            return "ClientConfiguration{" +
+                    "timeouts=" + timeouts +
+                    ", executor=" + executor +
+                    '}';
+        }
+    }
+
+    public static class SslConfiguration {
+        public boolean acceptAnyCertificate;
+        public String keystoreLocation;
+        public String keystoreType;
+        public String keystorePassword;
+        public String truststoreType;
+        public Collection<String> verifiedHostnames;
+
+        @Override
+        public String toString() {
+            return "SslConfiguration{" +
+                    "acceptAnyCertificate=" + acceptAnyCertificate +
+                    ", keystoreLocation='" + keystoreLocation + '\'' +
+                    ", keystoreType='" + keystoreType + '\'' +
+                    ", keystorePassword='" + keystorePassword + '\'' +
+                    ", truststoreType='" + truststoreType + '\'' +
+                    ", verifiedHostnames=" + verifiedHostnames +
+                    '}';
+        }
+    }
+
     public static class ResponseConfiguration {
         public String target;
         public Collection<String> skippedHeaders;
diff --git a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java
index 3eab49c..826eedc 100644
--- a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java
+++ b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/front/ProxyServlet.java
@@ -34,10 +34,6 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.CompletionStage;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
 import java.util.stream.Stream;
 
 import javax.servlet.AsyncContext;
@@ -50,8 +46,6 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
 import javax.ws.rs.client.CompletionStageRxInvoker;
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.Invocation;
@@ -67,10 +61,6 @@
 // IMPORTANT: don't make this class depending on meecrowave, cxf or our internals, use setup class
 public class ProxyServlet extends HttpServlet {
     protected Routes routes;
-    protected Client client;
-    protected ExecutorService executor;
-    protected long awaitTimeout;
-    protected long asyncTimeout;
     protected int prefixLength;
 
     @Override
@@ -87,9 +77,9 @@
     protected CompletionStage<HttpServletResponse> doExecute(final Routes.Route route, final HttpServletRequest req, final HttpServletResponse resp,
                                                              final String prefix) throws IOException {
         final AsyncContext asyncContext = req.startAsync();
-        asyncContext.setTimeout(asyncTimeout);
+        asyncContext.setTimeout(route.clientConfiguration.timeouts.execution);
 
-        WebTarget target = client.target(route.responseConfiguration.target);
+        WebTarget target = route.client.target(route.responseConfiguration.target);
         target = target.path(prefix); // todo: query params, multipart, etc
 
         final Map<String, String> queryParams = ofNullable(req.getQueryString())
@@ -187,7 +177,7 @@
 
     protected void forwardCookies(final Routes.Route route, final Response response, final HttpServletResponse resp) {
         response.getCookies().entrySet().stream()
-                .filter(cookie -> filterCookie(route.requestConfiguration.skippedCookies, cookie.getKey(), cookie.getValue().getValue()))
+                .filter(cookie -> filterCookie(route.responseConfiguration.skippedCookies, cookie.getKey(), cookie.getValue().getValue()))
                 .forEach(cookie -> addCookie(resp, cookie));
     }
 
@@ -206,7 +196,7 @@
 
     protected void forwardHeaders(final Routes.Route route, final Response response, final HttpServletResponse resp) {
         response.getHeaders().entrySet().stream()
-                .filter(header -> filterHeader(route.requestConfiguration.skippedHeaders, header.getKey()))
+                .filter(header -> filterHeader(route.responseConfiguration.skippedHeaders, header.getKey()))
                 .flatMap(entry -> entry.getValue().stream().map(value -> new AbstractMap.SimpleEntry<>(entry.getKey(), String.valueOf(value))))
                 .forEach(header -> resp.addHeader(header.getKey(), header.getValue()));
     }
@@ -239,7 +229,12 @@
     }
 
     protected Optional<Routes> loadConfiguration() {
-        return get("configuration").flatMap(path -> new ConfigurationLoader(path).load());
+        return get("configuration").flatMap(path -> new ConfigurationLoader(path) {
+            @Override
+            protected void log(final String message) {
+                getServletContext().log(message);
+            }
+        }.load());
     }
 
     @Override
@@ -250,11 +245,6 @@
                 .map(it -> it.endsWith("/*") ? it.substring(0, it.length() - "/*".length()) : it)
                 .orElse("").length() + config.getServletContext().getContextPath().length();
 
-        awaitTimeout = getLong("shutdown.timeout").orElse(1L);
-        asyncTimeout = getLong("async.timeout").orElse(30000L);
-
-        setupClient();
-
         final Optional<Routes> configuration = loadConfiguration();
         if (!configuration.isPresent()) {
             return;
@@ -264,57 +254,24 @@
 
     @Override
     public void destroy() {
-        if (executor != null) {
-            executor.shutdown();
-            try {
-                if (!executor.awaitTermination(awaitTimeout, MILLISECONDS)) {
-                    getServletContext().log("Can't shutdown the client executor in " + awaitTimeout + "ms");
-                }
-            } catch (final InterruptedException e) {
-                Thread.currentThread().interrupt();
-            }
-        }
-        client.close();
-        super.destroy();
-    }
-
-    protected void setupClient() {
-        executor = new ThreadPoolExecutor(
-                getInt("executor.core").orElse(64),
-                getInt("executor.max").orElse(512),
-                getLong("executor.keepAlive").orElse(60000L),
-                MILLISECONDS,
-                new LinkedBlockingQueue<>(),
-                new ThreadFactory() {
-                    private final SecurityManager sm = System.getSecurityManager();
-                    private final ThreadGroup group = (sm != null) ? sm.getThreadGroup() : Thread.currentThread().getThreadGroup();
-
-                    @Override
-                    public Thread newThread(final Runnable r) {
-                        final Thread newThread = new Thread(group, r, ProxyServlet.class.getName() + "_" + hashCode());
-                        newThread.setDaemon(false);
-                        newThread.setPriority(Thread.NORM_PRIORITY);
-                        newThread.setContextClassLoader(getClass().getClassLoader());
-                        return newThread;
+        if (routes != null && routes.routes != null) {
+            routes.routes.forEach(it -> {
+                if (it.executor != null) {
+                    it.executor.shutdown();
+                    try {
+                        if (!it.executor.awaitTermination(it.clientConfiguration.executor.shutdownTimeout, MILLISECONDS)) {
+                            getServletContext().log("Can't shutdown the client executor in " + it.clientConfiguration.executor.shutdownTimeout + "ms");
+                        }
+                    } catch (final InterruptedException e) {
+                        Thread.currentThread().interrupt();
                     }
-                },
-                (r, executor) -> getServletContext().log("Proxy rejected task: " + r));
-
-        final ClientBuilder clientBuilder = ClientBuilder.newBuilder();
-        clientBuilder.executorService(executor);
-        clientBuilder.readTimeout(getLong("read.timeout").orElse(30000L), MILLISECONDS);
-        clientBuilder.connectTimeout(getLong("connect.timeout").orElse(30000L), MILLISECONDS);
-        // todo: configure ssl
-        // clientBuilder.scheduledExecutorService(); // not used by cxf for instance so no need to overkill the conf
-        client = clientBuilder.build();
-    }
-
-    private Optional<Long> getLong(final String key) {
-        return get(key).map(Long::parseLong);
-    }
-
-    private Optional<Integer> getInt(final String key) {
-        return get(key).map(Integer::parseInt);
+                }
+                if (it.client != null) {
+                    it.client.close();
+                }
+            });
+        }
+        super.destroy();
     }
 
     private Optional<String> get(final String key) {
diff --git a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/meecrowave/ProxyServletSetup.java b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/meecrowave/ProxyServletSetup.java
index c581c97..5e1e723 100644
--- a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/meecrowave/ProxyServletSetup.java
+++ b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/meecrowave/ProxyServletSetup.java
@@ -51,13 +51,6 @@
             }
             servlet.setInitParameter("mapping", config.mapping);
             servlet.setInitParameter("configuration", config.configuration);
-            servlet.setInitParameter("read.timeout", config.readTimeout);
-            servlet.setInitParameter("connect.timeout", config.connectTimeout);
-            servlet.setInitParameter("executor.core", config.threadPoolCoreSize);
-            servlet.setInitParameter("executor.max", config.threadPoolMaxSize);
-            servlet.setInitParameter("executor.keepAlive", config.threadPoolKeepAlive);
-            servlet.setInitParameter("shutdown.timeout", config.shutdownTimeout);
-            servlet.setInitParameter("async.timeout", config.asyncTimeout);
         }, null);
     }
 
@@ -91,24 +84,6 @@
         @CliOption(name = "proxy-configuration", description = "The route file.")
         private String configuration = "conf/proxy.json";
 
-        @CliOption(name = "proxy-shutdown-timeout", description = "How long the shutdown will wait for in progress tasks (executor).")
-        private String shutdownTimeout = "30000";
-
-        @CliOption(name = "proxy-executor-core", description = "HTTP client thread pool core size.")
-        private String threadPoolCoreSize = "64";
-
-        @CliOption(name = "proxy-executor-max", description = "HTTP client thread pool max size.")
-        private String threadPoolMaxSize = "512";
-
-        @CliOption(name = "proxy-executor-max", description = "HTTP client thread pool keep alive duration (in ms).")
-        private String threadPoolKeepAlive = "60000";
-
-        @CliOption(name = "proxy-read-timeout", description = "HTTP client read timeout.")
-        private String readTimeout = "30000";
-
-        @CliOption(name = "proxy-connect-timeout", description = "HTTP client connect timeout.")
-        private String connectTimeout = "30000";
-
         @CliOption(name = "proxy-async-timeout", description = "Asynchronous execution timeout.")
         private String asyncTimeout = "30000";
     }
diff --git a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java
index bd3fd53..e0b1baa 100644
--- a/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java
+++ b/meecrowave-proxy/src/main/java/org/apache/meecrowave/proxy/servlet/service/ConfigurationLoader.java
@@ -19,24 +19,46 @@
 package org.apache.meecrowave.proxy.servlet.service;
 
 import static java.util.Collections.singletonList;
+import static java.util.Optional.ofNullable;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.function.Function.identity;
 import static java.util.stream.Collectors.toMap;
 
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.json.bind.Jsonb;
 import javax.json.bind.JsonbBuilder;
 import javax.json.bind.JsonbConfig;
 import javax.json.spi.JsonProvider;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import javax.ws.rs.client.ClientBuilder;
 
 import org.apache.meecrowave.proxy.servlet.configuration.Routes;
 
-public class ConfigurationLoader {
+public abstract class ConfigurationLoader {
     private final String path;
 
     private Routes routes;
@@ -45,20 +67,39 @@
         this.path = path;
     }
 
+    protected abstract void log(String message);
+
     public Optional<Routes> load() {
         final SimpleSubstitutor simpleSubstitutor = new SimpleSubstitutor(
                 System.getProperties().stringPropertyNames().stream().collect(toMap(identity(), System::getProperty)));
-        final Path routeFile = Paths.get(simpleSubstitutor.replace(path));
-        if (!Files.exists(routeFile)) {
+        final String resource = simpleSubstitutor.replace(path);
+        final Path routeFile = Paths.get(resource);
+        final InputStream stream;
+        if (Files.exists(routeFile)) {
+            try {
+                stream = Files.newInputStream(routeFile);
+            } catch (final IOException e) {
+                throw new IllegalStateException(e);
+            }
+        } else {
+            stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
+        }
+        if (stream == null) {
             throw new IllegalArgumentException("No routes configuration for the proxy servlet");
         }
 
-        try (final InputStream stream = Files.newInputStream(routeFile);
-             final Jsonb jsonb = JsonbBuilder.newBuilder()
+        final String content;
+        try {
+            content = simpleSubstitutor.replace(load(stream));
+        } catch (final IOException e) {
+            throw new IllegalArgumentException(e);
+        }
+
+        try (final Jsonb jsonb = JsonbBuilder.newBuilder()
                      .withProvider(loadJsonpProvider())
                      .withConfig(new JsonbConfig().setProperty("org.apache.johnzon.supports-comments", true))
                      .build()) {
-            routes = jsonb.fromJson(stream, Routes.class);
+            routes = jsonb.fromJson(content, Routes.class);
         } catch (final Exception e) {
             throw new IllegalArgumentException(e);
         }
@@ -67,7 +108,6 @@
             return Optional.empty();
         }
         if (routes.defaultRoute != null) {
-            onLoad(simpleSubstitutor, routes.defaultRoute);
             if (routes.routes == null) { // no route were defined, consider it is the default route, /!\ empty means no route, don't default
                 routes.routes = singletonList(routes.defaultRoute);
             }
@@ -75,13 +115,80 @@
                 routes.routes.forEach(r -> merge(routes.defaultRoute, r));
             }
         }
-        if (hasRoutes) {
-            routes.routes.forEach(it -> onLoad(simpleSubstitutor, it));
-        }
+        routes.routes.forEach(this::loadClient);
         return Optional.of(routes);
     }
 
+    private String load(final InputStream stream) throws IOException {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int count;
+        while (-1 != (count = stream.read(buffer))) {
+            baos.write(buffer, 0, count);
+        }
+        return new String(baos.toByteArray(), StandardCharsets.UTF_8);
+    }
+
+    private void loadClient(final Routes.Route route) {
+        if (route.clientConfiguration == null) {
+            route.clientConfiguration = new Routes.ClientConfiguration();
+        }
+        if (route.clientConfiguration.executor == null) {
+            route.clientConfiguration.executor = new Routes.ExecutorConfiguration();
+        }
+        if (route.clientConfiguration.timeouts == null) {
+            route.clientConfiguration.timeouts = new Routes.TimeoutConfiguration();
+        }
+        if (route.clientConfiguration.sslConfiguration == null) {
+            route.clientConfiguration.sslConfiguration = new Routes.SslConfiguration();
+        }
+
+        final ExecutorService executor = new ThreadPoolExecutor(
+                route.clientConfiguration.executor.core,
+                route.clientConfiguration.executor.max,
+                route.clientConfiguration.executor.keepAlive,
+                MILLISECONDS,
+                new LinkedBlockingQueue<>(),
+                new ThreadFactory() {
+                    private final SecurityManager sm = System.getSecurityManager();
+                    private final ThreadGroup group = (sm != null) ? sm.getThreadGroup() : Thread.currentThread().getThreadGroup();
+
+                    @Override
+                    public Thread newThread(final Runnable r) {
+                        final Thread newThread = new Thread(group, r, "meecrowave-proxy#" + ofNullable(route.id).orElse("[noid]"));
+                        newThread.setDaemon(false);
+                        newThread.setPriority(Thread.NORM_PRIORITY);
+                        newThread.setContextClassLoader(getClass().getClassLoader());
+                        return newThread;
+                    }
+                },
+                (run, ex) -> log("Proxy rejected task: " + run + ", in " + ex));
+
+        final ClientBuilder clientBuilder = ClientBuilder.newBuilder();
+        clientBuilder.executorService(executor);
+        clientBuilder.readTimeout(route.clientConfiguration.timeouts.read, MILLISECONDS);
+        clientBuilder.connectTimeout(route.clientConfiguration.timeouts.connect, MILLISECONDS);
+        // clientBuilder.scheduledExecutorService(); // not used by cxf for instance so no need to overkill the conf
+
+        if (route.clientConfiguration.sslConfiguration.acceptAnyCertificate) {
+            clientBuilder.hostnameVerifier((host, session) -> true);
+            clientBuilder.sslContext(createUnsafeSSLContext());
+        } else if (route.clientConfiguration.sslConfiguration.keystoreLocation != null) {
+            if (route.clientConfiguration.sslConfiguration.verifiedHostnames != null) {
+                clientBuilder.hostnameVerifier((host, session) -> route.clientConfiguration.sslConfiguration.verifiedHostnames.contains(host));
+            }
+            clientBuilder.sslContext(createSSLContext(
+                    route.clientConfiguration.sslConfiguration.keystoreLocation,
+                    route.clientConfiguration.sslConfiguration.keystoreType,
+                    route.clientConfiguration.sslConfiguration.keystorePassword,
+                    route.clientConfiguration.sslConfiguration.truststoreType));
+        }
+
+        route.client = clientBuilder.build();
+    }
+
     private void merge(final Routes.Route defaultRoute, final Routes.Route route) {
+        // request matching
         if (route.requestConfiguration == null) {
             route.requestConfiguration = defaultRoute.requestConfiguration;
         } else if (defaultRoute.requestConfiguration != null) {
@@ -99,6 +206,7 @@
             }
         }
 
+        // response processing
         if (route.responseConfiguration == null) {
             route.responseConfiguration = defaultRoute.responseConfiguration;
         } else if (defaultRoute.responseConfiguration != null) {
@@ -112,6 +220,91 @@
                 route.responseConfiguration.skippedHeaders = defaultRoute.responseConfiguration.skippedHeaders;
             }
         }
+
+        // client setup
+        if (route.clientConfiguration == null) {
+            route.clientConfiguration = defaultRoute.clientConfiguration;
+        } else if (defaultRoute.clientConfiguration != null) {
+            if (route.clientConfiguration.sslConfiguration == null) {
+                route.clientConfiguration.sslConfiguration = defaultRoute.clientConfiguration.sslConfiguration;
+            } else if (defaultRoute.clientConfiguration.sslConfiguration != null) {
+                if (route.clientConfiguration.sslConfiguration.verifiedHostnames == null) {
+                    route.clientConfiguration.sslConfiguration.verifiedHostnames = defaultRoute.clientConfiguration.sslConfiguration.verifiedHostnames;
+                }
+                if (route.clientConfiguration.sslConfiguration.keystoreLocation == null) {
+                    route.clientConfiguration.sslConfiguration.keystoreLocation = defaultRoute.clientConfiguration.sslConfiguration.keystoreLocation;
+                }
+                if (route.clientConfiguration.sslConfiguration.keystorePassword == null) {
+                    route.clientConfiguration.sslConfiguration.keystorePassword = defaultRoute.clientConfiguration.sslConfiguration.keystorePassword;
+                }
+                if (route.clientConfiguration.sslConfiguration.keystoreType == null) {
+                    route.clientConfiguration.sslConfiguration.keystoreType = defaultRoute.clientConfiguration.sslConfiguration.keystoreType;
+                }
+                if (route.clientConfiguration.sslConfiguration.truststoreType == null) {
+                    route.clientConfiguration.sslConfiguration.truststoreType = defaultRoute.clientConfiguration.sslConfiguration.truststoreType;
+                }
+            }
+            if (route.clientConfiguration.executor == null) {
+                route.clientConfiguration.executor = defaultRoute.clientConfiguration.executor;
+            }
+            if (route.clientConfiguration.timeouts == null) {
+                route.clientConfiguration.timeouts = defaultRoute.clientConfiguration.timeouts;
+            }
+        }
+    }
+
+    private SSLContext createSSLContext(final String keystoreLocation, final String keystoreType,
+                                        final String keystorePassword, final String truststoreType) {
+        final File source = new File(keystoreLocation);
+        if (!source.exists()) {
+            throw new IllegalArgumentException(source + " does not exist");
+        }
+        final KeyStore keyStore;
+        try (final FileInputStream stream = new FileInputStream(source)) {
+            keyStore = KeyStore.getInstance(keystoreType == null ? KeyStore.getDefaultType() : keystoreType);
+            keyStore.load(stream, keystorePassword.toCharArray());
+        } catch (final KeyStoreException | NoSuchAlgorithmException e) {
+            throw new IllegalStateException(e);
+        } catch (final CertificateException | IOException e) {
+            throw new IllegalArgumentException(e);
+        }
+        try {
+            final TrustManagerFactory trustManagerFactory =
+                    TrustManagerFactory.getInstance(truststoreType == null ? TrustManagerFactory.getDefaultAlgorithm() : truststoreType);
+            trustManagerFactory.init(keyStore);
+            final SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(null, trustManagerFactory.getTrustManagers(), new java.security.SecureRandom());
+            return sslContext;
+        } catch (final KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private SSLContext createUnsafeSSLContext() {
+        final TrustManager[] trustManagers = { new X509TrustManager() {
+
+            @Override
+            public void checkClientTrusted(final X509Certificate[] x509Certificates, final String s) {
+                // no-op
+            }
+
+            @Override
+            public void checkServerTrusted(final X509Certificate[] x509Certificates, final String s) {
+                // no-op
+            }
+
+            @Override
+            public X509Certificate[] getAcceptedIssuers() {
+                return null;
+            }
+        } };
+        try {
+            final SSLContext sslContext = SSLContext.getInstance("SSL");
+            sslContext.init(null, trustManagers, new java.security.SecureRandom());
+            return sslContext;
+        } catch (final NoSuchAlgorithmException | KeyManagementException e) {
+            throw new IllegalStateException(e);
+        }
     }
 
     private JsonProvider loadJsonpProvider() {
@@ -123,17 +316,4 @@
             return JsonProvider.provider();
         }
     }
-
-    // filter
-    private void onLoad(final SimpleSubstitutor simpleSubstitutor, final Routes.Route route) {
-        if (route.requestConfiguration != null && route.requestConfiguration.prefix != null) {
-            route.requestConfiguration.prefix = simpleSubstitutor.replace(route.requestConfiguration.prefix);
-        }
-        if (route.requestConfiguration != null && route.requestConfiguration.method != null) {
-            route.requestConfiguration.method = simpleSubstitutor.replace(route.requestConfiguration.method);
-        }
-        if (route.responseConfiguration != null && route.responseConfiguration.target != null) {
-            route.responseConfiguration.target = simpleSubstitutor.replace(route.responseConfiguration.target);
-        }
-    }
 }