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 <[email protected]> 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); - } - -}
