You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/07/10 11:33:11 UTC

[camel] 02/04: CAMEL-19593: camel-jbang should reuse camel-platform-http-main

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 36485f50f8085c0071c79c747ff83bf9469c4b98
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Jul 10 13:23:10 2023 +0200

    CAMEL-19593: camel-jbang should reuse camel-platform-http-main
---
 .../main/HttpServerConfigurationProperties.java    |   4 +-
 dsl/camel-kamelet-main/pom.xml                     |  14 +
 .../java/org/apache/camel/main/KameletMain.java    |  26 +-
 .../DependencyDownloaderComponentResolver.java     |  37 +-
 .../apache/camel/main/http/VertxHttpServer.java    | 560 ---------------------
 5 files changed, 63 insertions(+), 578 deletions(-)

diff --git a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
index debafab296d..2d9fb21e1ef 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
@@ -228,8 +228,8 @@ public class HttpServerConfigurationProperties implements BootstrapCloseable {
      * <tt>camelContext.setDevConsole(true);</tt> If enabled then you can access a basic developer console on
      * context-path: /q/dev.
      */
-    public HttpServerConfigurationProperties withDevConsumeEnabled(boolean devConsumeEnabled) {
-        this.devConsoleEnabled = devConsumeEnabled;
+    public HttpServerConfigurationProperties withDevConsoleEnabled(boolean devConsoleEnabled) {
+        this.devConsoleEnabled = devConsoleEnabled;
         return this;
     }
 
diff --git a/dsl/camel-kamelet-main/pom.xml b/dsl/camel-kamelet-main/pom.xml
index a3b8cc56c3f..8ff95f6defa 100644
--- a/dsl/camel-kamelet-main/pom.xml
+++ b/dsl/camel-kamelet-main/pom.xml
@@ -79,6 +79,13 @@
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-platform-http-main</artifactId>
+            <exclusions>
+                <!-- avoid WARN logs from netty -->
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-resolver-dns</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
@@ -91,6 +98,13 @@
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-vertx-http</artifactId>
+            <exclusions>
+                <!-- avoid WARN logs from netty -->
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-resolver-dns</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index 4a42b9eb3a2..40d69c36e58 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -60,7 +60,6 @@ import org.apache.camel.main.download.KnownDependenciesResolver;
 import org.apache.camel.main.download.KnownReposResolver;
 import org.apache.camel.main.download.MavenDependencyDownloader;
 import org.apache.camel.main.download.TypeConverterLoaderDownloadListener;
-import org.apache.camel.main.http.VertxHttpServer;
 import org.apache.camel.main.injection.AnnotationDependencyInjection;
 import org.apache.camel.main.util.ExtraFilesClassLoader;
 import org.apache.camel.spi.ClassResolver;
@@ -410,15 +409,13 @@ public class KameletMain extends MainCommandLineSupport {
         // embed HTTP server if port is specified
         Object port = getInitialProperties().get("camel.jbang.platform-http.port");
         if (port != null) {
-            VertxHttpServer.registerServer(answer, Integer.parseInt(port.toString()), stub);
+            configure().httpServer().withEnabled(true);
+            configure().httpServer().withPort(Integer.parseInt(port.toString()));
         }
         boolean console = "true".equals(getInitialProperties().get("camel.jbang.console"));
-        if (console && port == null) {
-            // use default port 8080 if console is enabled
-            VertxHttpServer.registerServer(answer, 8080, stub);
-        }
         if (console) {
-            VertxHttpServer.registerConsole(answer);
+            configure().httpServer().withEnabled(true);
+            configure().httpServer().withDevConsoleEnabled(true);
         }
 
         // always enable developer console as it is needed by camel-cli-connector
@@ -441,12 +438,13 @@ public class KameletMain extends MainCommandLineSupport {
         }
 
         boolean health = "true".equals(getInitialProperties().get("camel.jbang.health"));
-        if (health && port == null) {
-            // use default port 8080 if console is enabled
-            VertxHttpServer.registerServer(answer, 8080, stub);
-        }
         if (health) {
-            VertxHttpServer.registerHealthCheck(answer);
+            configure().httpServer().withEnabled(true);
+            configure().httpServer().withHealthCheckEnabled(true);
+        }
+        if (stub) {
+            // stub should not include http server
+            configure().httpServer().withEnabled(false);
         }
 
         // need to setup jfr early
@@ -511,7 +509,9 @@ public class KameletMain extends MainCommandLineSupport {
             if (sourceDir != null) {
                 if (console || health) {
                     // allow to upload source via http when HTTP console enabled
-                    VertxHttpServer.registerUploadSourceDir(answer, sourceDir);
+                    configure().httpServer().withEnabled(true);
+                    configure().httpServer().withUploadEnabled(true);
+                    configure().httpServer().withUploadSourceDir(sourceDir);
                 }
                 RouteOnDemandReloadStrategy reloader = new RouteOnDemandReloadStrategy(sourceDir, true);
                 reloader.setPattern("*");
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderComponentResolver.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderComponentResolver.java
index 6e405da6ae7..416eddbbab1 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderComponentResolver.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderComponentResolver.java
@@ -20,12 +20,18 @@ import java.util.List;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Component;
+import org.apache.camel.Service;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.catalog.DefaultCamelCatalog;
 import org.apache.camel.component.platform.http.PlatformHttpComponent;
+import org.apache.camel.component.platform.http.main.DefaultMainHttpServerFactory;
+import org.apache.camel.component.platform.http.main.MainHttpServer;
 import org.apache.camel.component.stub.StubComponent;
 import org.apache.camel.impl.engine.DefaultComponentResolver;
-import org.apache.camel.main.http.VertxHttpServer;
+import org.apache.camel.main.HttpServerConfigurationProperties;
+import org.apache.camel.main.MainConstants;
+import org.apache.camel.main.MainHttpServerFactory;
+import org.apache.camel.main.util.CamelJBangSettingsHelper;
 import org.apache.camel.main.util.SuggestSimilarHelper;
 import org.apache.camel.tooling.model.ComponentModel;
 
@@ -70,8 +76,21 @@ public final class DependencyDownloaderComponentResolver extends DefaultComponen
         }
         if (answer instanceof PlatformHttpComponent) {
             // setup a default http server on port 8080 if not already done
-            VertxHttpServer.setPlatformHttpComponent((PlatformHttpComponent) answer);
-            VertxHttpServer.registerServer(camelContext, stub);
+            MainHttpServer server = camelContext.hasService(MainHttpServer.class);
+            if (server == null) {
+                // need to capture we use http-server
+                HttpServerConfigurationProperties config = new HttpServerConfigurationProperties(null);
+                CamelJBangSettingsHelper.writeSettings("camel.jbang.platform-http.port", String.valueOf(config.getPort()));
+                if (!stub) {
+                    MainHttpServerFactory factory = new DefaultMainHttpServerFactory();
+                    Service httpServer = factory.newHttpServer(config);
+                    try {
+                        camelContext.addService(httpServer, true, true);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
         }
         if (answer == null) {
             List<String> suggestion = SuggestSimilarHelper.didYouMean(catalog.findComponentNames(), name);
@@ -93,4 +112,16 @@ public final class DependencyDownloaderComponentResolver extends DefaultComponen
         return ACCEPTED_STUB_NAMES.contains(name);
     }
 
+    private static MainHttpServerFactory resolveMainHttpServerFactory(CamelContext camelContext) throws Exception {
+        // lookup in service registry first
+        MainHttpServerFactory answer = camelContext.getRegistry().findSingleByType(MainHttpServerFactory.class);
+        if (answer == null) {
+            answer = camelContext.getCamelContextExtension().getBootstrapFactoryFinder()
+                    .newInstance(MainConstants.PLATFORM_HTTP_SERVER, MainHttpServerFactory.class)
+                    .orElseThrow(() -> new IllegalArgumentException(
+                            "Cannot find MainHttpServerFactory on classpath. Add camel-platform-http-main to classpath."));
+        }
+        return answer;
+    }
+
 }
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/http/VertxHttpServer.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/http/VertxHttpServer.java
deleted file mode 100644
index c6f1aea0f49..00000000000
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/http/VertxHttpServer.java
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
- * 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.camel.main.http;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
-
-import io.vertx.core.Handler;
-import io.vertx.core.http.HttpMethod;
-import io.vertx.ext.web.RequestBody;
-import io.vertx.ext.web.Route;
-import io.vertx.ext.web.RoutingContext;
-import io.vertx.ext.web.handler.BodyHandler;
-import io.vertx.ext.web.impl.BlockingHandlerDecorator;
-import org.apache.camel.CamelContext;
-import org.apache.camel.Exchange;
-import org.apache.camel.RuntimeCamelException;
-import org.apache.camel.StartupListener;
-import org.apache.camel.component.platform.http.HttpEndpointModel;
-import org.apache.camel.component.platform.http.PlatformHttpComponent;
-import org.apache.camel.component.platform.http.main.MainHttpServer;
-import org.apache.camel.component.platform.http.vertx.VertxPlatformHttpRouter;
-import org.apache.camel.console.DevConsole;
-import org.apache.camel.console.DevConsoleRegistry;
-import org.apache.camel.health.HealthCheck;
-import org.apache.camel.health.HealthCheckHelper;
-import org.apache.camel.health.HealthCheckRegistry;
-import org.apache.camel.main.util.CamelJBangSettingsHelper;
-import org.apache.camel.spi.CamelEvent;
-import org.apache.camel.support.SimpleEventNotifierSupport;
-import org.apache.camel.util.AntPathMatcher;
-import org.apache.camel.util.FileUtil;
-import org.apache.camel.util.IOHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.StringHelper;
-import org.apache.camel.util.json.JsonObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * To setup vertx http server in the running Camel application
- */
-public final class VertxHttpServer {
-
-    static VertxPlatformHttpRouter router;
-    static MainHttpServer server;
-    static PlatformHttpComponent phc;
-
-    private static final Logger LOG = LoggerFactory.getLogger(VertxHttpServer.class);
-    private static final AtomicBoolean REGISTERED = new AtomicBoolean();
-    private static final AtomicBoolean CONSOLE = new AtomicBoolean();
-    private static final AtomicBoolean HEALTH_CHECK = new AtomicBoolean();
-    private static final AtomicBoolean UPLOAD = new AtomicBoolean();
-
-    private VertxHttpServer() {
-    }
-
-    public static void setPlatformHttpComponent(PlatformHttpComponent phc) {
-        VertxHttpServer.phc = phc;
-    }
-
-    public static void registerServer(CamelContext camelContext, boolean stub) {
-        if (REGISTERED.compareAndSet(false, true)) {
-            doRegisterServer(camelContext, 8080, stub);
-        }
-    }
-
-    public static void registerServer(CamelContext camelContext, int port, boolean stub) {
-        if (REGISTERED.compareAndSet(false, true)) {
-            doRegisterServer(camelContext, port, stub);
-        }
-    }
-
-    private static void doRegisterServer(CamelContext camelContext, int port, boolean stub) {
-        // need to capture we use http-server
-        CamelJBangSettingsHelper.writeSettings("camel.jbang.platform-http.port", Integer.toString(port));
-
-        if (stub) {
-            return;
-        }
-
-        try {
-            server = new MainHttpServer();
-            server.setCamelContext(camelContext);
-            server.setPort(port);
-            server.start();
-            router = server.getRouter();
-            if (phc == null) {
-                phc = camelContext.getComponent("platform-http", PlatformHttpComponent.class);
-            }
-
-            // after camel is started then add event notifier
-            camelContext.addStartupListener(new StartupListener() {
-
-                private volatile Set<HttpEndpointModel> last;
-
-                @Override
-                public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception {
-                    camelContext.getManagementStrategy().addEventNotifier(new SimpleEventNotifierSupport() {
-
-                        @Override
-                        public boolean isEnabled(CamelEvent event) {
-                            return event instanceof CamelEvent.CamelContextStartedEvent
-                                    || event instanceof CamelEvent.RouteReloadedEvent;
-                        }
-
-                        @Override
-                        public void notify(CamelEvent event) throws Exception {
-                            // when reloading then there may be more routes in the same batch, so we only want
-                            // to log the summary at the end
-                            if (event instanceof CamelEvent.RouteReloadedEvent) {
-                                CamelEvent.RouteReloadedEvent re = (CamelEvent.RouteReloadedEvent) event;
-                                if (re.getIndex() < re.getTotal()) {
-                                    return;
-                                }
-                            }
-
-                            Set<HttpEndpointModel> endpoints = phc.getHttpEndpoints();
-                            if (endpoints.isEmpty()) {
-                                return;
-                            }
-
-                            // log only if changed
-                            if (last == null || last.size() != endpoints.size() || !last.containsAll(endpoints)) {
-                                LOG.info("HTTP endpoints summary");
-                                for (HttpEndpointModel u : endpoints) {
-                                    String line = "http://0.0.0.0:" + port + u.getUri();
-                                    if (u.getVerbs() != null) {
-                                        line += " (" + u.getVerbs() + ")";
-                                    }
-                                    LOG.info("    {}", line);
-                                }
-                            }
-
-                            // use a defensive copy of last known endpoints
-                            last = new HashSet<>(endpoints);
-                        }
-                    });
-                }
-            });
-
-        } catch (Exception e) {
-            throw RuntimeCamelException.wrapRuntimeException(e);
-        }
-    }
-
-    public static void registerConsole(CamelContext camelContext) {
-        if (CONSOLE.compareAndSet(false, true)) {
-            doRegisterConsole(camelContext);
-        }
-    }
-
-    private static void doRegisterConsole(CamelContext context) {
-        Route dev = router.route("/q/dev");
-        dev.method(HttpMethod.GET);
-        dev.produces("text/plain");
-        dev.produces("application/json");
-        Route devSub = router.route("/q/dev/*");
-        devSub.method(HttpMethod.GET);
-        devSub.produces("text/plain");
-        devSub.produces("application/json");
-
-        Handler<RoutingContext> handler = new Handler<RoutingContext>() {
-            @Override
-            public void handle(RoutingContext ctx) {
-                String acp = ctx.request().getHeader("Accept");
-                int pos1 = acp != null ? acp.indexOf("html") : Integer.MAX_VALUE;
-                if (pos1 == -1) {
-                    pos1 = Integer.MAX_VALUE;
-                }
-                int pos2 = acp != null ? acp.indexOf("json") : Integer.MAX_VALUE;
-                if (pos2 == -1) {
-                    pos2 = Integer.MAX_VALUE;
-                }
-                final boolean html = pos1 < pos2;
-                final boolean json = pos2 < pos1;
-                final DevConsole.MediaType mediaType = json ? DevConsole.MediaType.JSON : DevConsole.MediaType.TEXT;
-
-                ctx.response().putHeader("content-type", "text/plain");
-
-                DevConsoleRegistry dcr = context.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class);
-                if (dcr == null || !dcr.isEnabled()) {
-                    ctx.end("Developer Console is not enabled");
-                    return;
-                }
-
-                String path = StringHelper.after(ctx.request().path(), "/q/dev/");
-                String s = path;
-                if (s != null && s.contains("/")) {
-                    s = StringHelper.before(s, "/");
-                }
-                String id = s;
-
-                // index/home should list each console
-                if (id == null || id.isEmpty() || id.equals("index")) {
-                    StringBuilder sb = new StringBuilder();
-                    JsonObject root = new JsonObject();
-
-                    dcr.stream().forEach(c -> {
-                        if (json) {
-                            JsonObject jo = new JsonObject();
-                            jo.put("id", c.getId());
-                            jo.put("displayName", c.getDisplayName());
-                            jo.put("description", c.getDescription());
-                            root.put(c.getId(), jo);
-                        } else {
-                            String link = c.getId();
-                            String eol = "\n";
-                            if (html) {
-                                link = "<a href=\"dev/" + link + "\">" + c.getId() + "</a>";
-                                eol = "<br/>\n";
-                            }
-                            sb.append(link).append(": ").append(c.getDescription()).append(eol);
-                            // special for top in processor mode
-                            if ("top".equals(c.getId())) {
-                                link = link.replace("top", "top/*");
-                                sb.append(link).append(": ").append("Display the top processors").append(eol);
-                            }
-                        }
-                    });
-                    if (sb.length() > 0) {
-                        String out = sb.toString();
-                        if (html) {
-                            ctx.response().putHeader("content-type", "text/html");
-                        }
-                        ctx.end(out);
-                    } else if (!root.isEmpty()) {
-                        ctx.response().putHeader("content-type", "application/json");
-                        String out = root.toJson();
-                        ctx.end(out);
-                    }
-                } else {
-                    Map<String, Object> params = new HashMap<>();
-                    ctx.queryParams().forEach(params::put);
-                    params.put(Exchange.HTTP_PATH, path);
-                    StringBuilder sb = new StringBuilder();
-                    JsonObject root = new JsonObject();
-
-                    // sort according to index by given id
-                    dcr.stream().sorted((o1, o2) -> {
-                        int p1 = id.indexOf(o1.getId());
-                        int p2 = id.indexOf(o2.getId());
-                        return Integer.compare(p1, p2);
-                    }).forEach(c -> {
-                        boolean include = "all".equals(id) || c.getId().equalsIgnoreCase(id);
-                        if (include && c.supportMediaType(mediaType)) {
-                            Object out = c.call(mediaType, params);
-                            if (out != null && mediaType == DevConsole.MediaType.TEXT) {
-                                sb.append(c.getDisplayName()).append(":");
-                                sb.append("\n\n");
-                                sb.append(out);
-                                sb.append("\n\n");
-                            } else if (out != null && mediaType == DevConsole.MediaType.JSON) {
-                                root.put(c.getId(), out);
-                            }
-                        }
-                    });
-                    if (sb.length() > 0) {
-                        String out = sb.toString();
-                        ctx.end(out);
-                    } else if (!root.isEmpty()) {
-                        ctx.response().putHeader("content-type", "application/json");
-                        String out = root.toJson();
-                        ctx.end(out);
-                    } else {
-                        ctx.end("Developer Console not found: " + id);
-                    }
-                }
-            }
-        };
-        // use blocking handler as the task can take longer time to complete
-        dev.handler(new BlockingHandlerDecorator(handler, true));
-        devSub.handler(new BlockingHandlerDecorator(handler, true));
-
-        phc.addHttpEndpoint("/q/dev", null, null);
-    }
-
-    public static void registerHealthCheck(CamelContext camelContext) {
-        if (HEALTH_CHECK.compareAndSet(false, true)) {
-            doRegisterHealthCheck(camelContext);
-        }
-    }
-
-    private static void doRegisterHealthCheck(CamelContext context) {
-        final Route health = router.route("/q/health");
-        health.method(HttpMethod.GET);
-        health.produces("application/json");
-        final Route live = router.route("/q/health/live");
-        live.method(HttpMethod.GET);
-        live.produces("application/json");
-        final Route ready = router.route("/q/health/ready");
-        ready.method(HttpMethod.GET);
-        ready.produces("application/json");
-
-        Handler<RoutingContext> handler = new Handler<RoutingContext>() {
-            @Override
-            public void handle(RoutingContext ctx) {
-                ctx.response().putHeader("content-type", "application/json");
-
-                boolean all = ctx.currentRoute() == health;
-                boolean liv = ctx.currentRoute() == live;
-                boolean rdy = ctx.currentRoute() == ready;
-
-                Collection<HealthCheck.Result> res;
-                if (all) {
-                    res = HealthCheckHelper.invoke(context);
-                } else if (liv) {
-                    res = HealthCheckHelper.invokeLiveness(context);
-                } else {
-                    res = HealthCheckHelper.invokeReadiness(context);
-                }
-
-                StringBuilder sb = new StringBuilder();
-                sb.append("{\n");
-
-                HealthCheckRegistry registry = HealthCheckRegistry.get(context);
-                String level = ctx.request().getParam("exposureLevel");
-                if (level == null) {
-                    level = registry.getExposureLevel();
-                }
-
-                // are we UP
-                boolean up = HealthCheckHelper.isResultsUp(res, rdy);
-
-                if ("oneline".equals(level)) {
-                    // only brief status
-                    healthCheckStatus(sb, up);
-                } else if ("full".equals(level)) {
-                    // include all details
-                    List<HealthCheck.Result> list = new ArrayList<>(res);
-                    healthCheckDetails(sb, list, up);
-                } else {
-                    // include only DOWN details
-                    List<HealthCheck.Result> downs = res.stream().filter(r -> r.getState().equals(HealthCheck.State.DOWN))
-                            .collect(Collectors.toList());
-                    healthCheckDetails(sb, downs, up);
-                }
-                sb.append("}\n");
-
-                if (!up) {
-                    // we need to fail with a http status so lets use 500
-                    ctx.response().setStatusCode(500);
-                }
-                ctx.end(sb.toString());
-            }
-        };
-        // use blocking handler as the task can take longer time to complete
-        health.handler(new BlockingHandlerDecorator(handler, true));
-        live.handler(new BlockingHandlerDecorator(handler, true));
-        ready.handler(new BlockingHandlerDecorator(handler, true));
-
-        phc.addHttpEndpoint("/q/health", null, null);
-    }
-
-    private static void healthCheckStatus(StringBuilder sb, boolean up) {
-        if (up) {
-            sb.append("    \"status\": \"UP\"\n");
-        } else {
-            sb.append("    \"status\": \"DOWN\"\n");
-        }
-    }
-
-    private static void healthCheckDetails(StringBuilder sb, List<HealthCheck.Result> checks, boolean up) {
-        healthCheckStatus(sb, up);
-
-        if (!checks.isEmpty()) {
-            sb.append(",\n");
-            sb.append("    \"checks\": [\n");
-            for (int i = 0; i < checks.size(); i++) {
-                HealthCheck.Result d = checks.get(i);
-                sb.append("        {\n");
-                reportHealthCheck(sb, d);
-                if (i < checks.size() - 1) {
-                    sb.append("        },\n");
-                } else {
-                    sb.append("        }\n");
-                }
-            }
-            sb.append("    ]\n");
-        }
-    }
-
-    private static void reportHealthCheck(StringBuilder sb, HealthCheck.Result d) {
-        sb.append("            \"name\": \"").append(d.getCheck().getId()).append("\",\n");
-        sb.append("            \"status\": \"").append(d.getState()).append("\",\n");
-        if (d.getError().isPresent()) {
-            String msg = allCausedByErrorMessages(d.getError().get());
-            sb.append("            \"error-message\": \"").append(msg)
-                    .append("\",\n");
-            sb.append("            \"error-stacktrace\": \"").append(errorStackTrace(d.getError().get()))
-                    .append("\",\n");
-        }
-        if (d.getMessage().isPresent()) {
-            sb.append("            \"message\": \"").append(d.getMessage().get()).append("\",\n");
-        }
-        if (d.getDetails() != null && !d.getDetails().isEmpty()) {
-            // lets use sorted keys
-            Iterator<String> it = new TreeSet<>(d.getDetails().keySet()).iterator();
-            sb.append("            \"data\": {\n");
-            while (it.hasNext()) {
-                String k = it.next();
-                Object v = d.getDetails().get(k);
-                if (v == null) {
-                    v = ""; // in case of null value
-                }
-                boolean last = !it.hasNext();
-                sb.append("                 \"").append(k).append("\": \"").append(v).append("\"");
-                if (!last) {
-                    sb.append(",");
-                }
-                sb.append("\n");
-            }
-            sb.append("            }\n");
-        }
-    }
-
-    private static String allCausedByErrorMessages(Throwable e) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(e.getMessage());
-
-        while (e.getCause() != null) {
-            e = e.getCause();
-            if (e.getMessage() != null) {
-                sb.append("; Caused by: ");
-                sb.append(ObjectHelper.classCanonicalName(e));
-                sb.append(": ");
-                sb.append(e.getMessage());
-            }
-        }
-
-        return sb.toString();
-    }
-
-    private static String errorStackTrace(Throwable e) {
-        StringWriter sw = new StringWriter();
-        e.printStackTrace(new PrintWriter(sw));
-
-        String trace = sw.toString();
-        // because the stacktrace is printed in json we need to make it safe
-        trace = trace.replace('"', '\'');
-        trace = trace.replace('\t', ' ');
-        trace = trace.replace(System.lineSeparator(), " ");
-        return trace;
-    }
-
-    public static void registerUploadSourceDir(CamelContext camelContext, String dir) {
-        if (UPLOAD.compareAndSet(false, true)) {
-            doRegisterUploadSourceDir(camelContext, dir);
-        }
-    }
-
-    private static void doRegisterUploadSourceDir(CamelContext context, final String dir) {
-        final Route upload = router.route("/q/upload/:filename")
-                .method(HttpMethod.PUT)
-                // need body handler to handle file uploads
-                .handler(BodyHandler.create(true));
-
-        final Route uploadDelete = router.route("/q/upload/:filename");
-        uploadDelete.method(HttpMethod.DELETE);
-
-        Handler<RoutingContext> handler = new Handler<RoutingContext>() {
-            @Override
-            public void handle(RoutingContext ctx) {
-                String name = ctx.pathParam("filename");
-                if (name == null) {
-                    ctx.response().setStatusCode(400);
-                    ctx.end();
-                    return;
-                }
-
-                int status = 200;
-                boolean delete = HttpMethod.DELETE == ctx.request().method();
-                if (delete) {
-                    if (name.contains("*")) {
-                        if (name.equals("*")) {
-                            name = "**";
-                        }
-                        AntPathMatcher match = AntPathMatcher.INSTANCE;
-                        File[] files = new File(dir).listFiles();
-                        if (files != null) {
-                            for (File f : files) {
-                                if (f.getName().startsWith(".") || f.isHidden()) {
-                                    continue;
-                                }
-                                if (match.match(name, f.getName())) {
-                                    LOG.info("Deleting file: {}/{}", dir, name);
-                                    FileUtil.deleteFile(f);
-                                }
-                            }
-                        }
-                    } else {
-                        File f = new File(dir, name);
-                        if (f.exists() && f.isFile()) {
-                            LOG.info("Deleting file: {}/{}", dir, name);
-                            FileUtil.deleteFile(f);
-                        }
-                    }
-                } else {
-                    File f = new File(dir, name);
-                    boolean exists = f.isFile() && f.exists();
-                    LOG.info("{} file: {}/{}", exists ? "Updating" : "Creating", dir, name);
-
-                    File tmp = new File(dir, name + ".tmp");
-                    FileOutputStream fos = null;
-                    try {
-                        fos = new FileOutputStream(tmp, false);
-                        RequestBody rb = ctx.body();
-                        IOHelper.writeText(rb.asString(), fos);
-
-                        FileUtil.renameFileUsingCopy(tmp, f);
-                        FileUtil.deleteFile(tmp);
-                    } catch (Exception e) {
-                        // some kind of error
-                        LOG.warn("Error saving file: {}/{} due to: {}", dir, name, e.getMessage(), e);
-                        status = 500;
-                    } finally {
-                        IOHelper.close(fos);
-                        FileUtil.deleteFile(tmp);
-                    }
-                }
-
-                ctx.response().setStatusCode(status);
-                ctx.end();
-            }
-        };
-        // use blocking handler as the task can take longer time to complete
-        upload.handler(new BlockingHandlerDecorator(handler, true));
-        uploadDelete.handler(new BlockingHandlerDecorator(handler, true));
-
-        phc.addHttpEndpoint("/q/upload", "PUT,DELETE", null);
-    }
-
-}