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 2026/10/01 13:43:38 UTC
(camel) branch fix/CAMEL-25116-4.22.x created (now dde44e43682f)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a change to branch fix/CAMEL-25116-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
at dde44e43682f CAMEL-25116: camel-platform-http-main - /q/download?classpath=true lists the resources of the application (#27024)
This branch includes the following new commits:
new 7b211ec80d79 chore: camel-platform-http-main - Document listing files with /q/download
new dde44e43682f CAMEL-25116: camel-platform-http-main - /q/download?classpath=true lists the resources of the application (#27024)
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel) 01/02: chore: camel-platform-http-main - Document listing files with /q/download
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch fix/CAMEL-25116-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 7b211ec80d79051c3ee8fc103be5677830f97725
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Sep 28 11:56:18 2026 +0200
chore: camel-platform-http-main - Document listing files with /q/download
Document how the /q/download management endpoint lists and serves files.
Without a file name only route source files are listed; ?classpath=true
also lists classpath resources, an Ant-style pattern filters the list, and
Accept: text/html returns the list as links. /q/download/{name} resolves
from the classpath first, so unlisted classpath files can still be
downloaded. The classpath=true option was previously undocumented.
Closes #26982
Signed-off-by: Claus Ibsen <cl...@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <no...@anthropic.com>
(cherry picked from commit 6567486f108c6b0d13ee78c865df6bf4fe3e974f)
---
.../camel/catalog/docs/platform-http-main.adoc | 35 ++++++++++++++++++++++
.../src/main/docs/platform-http-main.adoc | 35 ++++++++++++++++++++++
2 files changed, 70 insertions(+)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc
index 824d6fb7d15b..56622c5c085f 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc
@@ -42,6 +42,41 @@ These features are as follows:
You configure these features in the `application.properties` file using the `camel.server.xxx` and `camel.management.xxx` options.
+=== Downloading files
+
+File download is enabled with `camel.management.downloadEnabled=true`.
+
+Calling `/q/download` without a file name returns a list of the files that can be downloaded.
+By default, the list only contains the source files of the routes (such as `*.camel.yaml` files).
+To also list the resources from the classpath, add the `classpath=true` query parameter:
+
+[source,bash]
+----
+# list route source files
+curl http://localhost:8080/q/download
+
+# list route source files and all classpath resources
+curl http://localhost:8080/q/download?classpath=true
+----
+
+The list can be filtered with an Ant-style pattern as the file name, for example to list only the properties files:
+
+[source,bash]
+----
+curl http://localhost:8080/q/download/*.properties?classpath=true
+----
+
+When the `Accept` header includes `html` (such as from a web browser), the list is returned as HTML links.
+
+To download a file, call `/q/download/` followed by the file name. The file is resolved from the classpath
+first, and then from the route source files. A file on the classpath can be downloaded even if it is not listed
+(when not using `classpath=true`).
+
+[source,bash]
+----
+curl http://localhost:8080/q/download/application.properties
+----
+
== JWT authentication
The embedded HTTP server can validate JWT bearer tokens on incoming requests.
diff --git a/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc b/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc
index 824d6fb7d15b..56622c5c085f 100644
--- a/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc
+++ b/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc
@@ -42,6 +42,41 @@ These features are as follows:
You configure these features in the `application.properties` file using the `camel.server.xxx` and `camel.management.xxx` options.
+=== Downloading files
+
+File download is enabled with `camel.management.downloadEnabled=true`.
+
+Calling `/q/download` without a file name returns a list of the files that can be downloaded.
+By default, the list only contains the source files of the routes (such as `*.camel.yaml` files).
+To also list the resources from the classpath, add the `classpath=true` query parameter:
+
+[source,bash]
+----
+# list route source files
+curl http://localhost:8080/q/download
+
+# list route source files and all classpath resources
+curl http://localhost:8080/q/download?classpath=true
+----
+
+The list can be filtered with an Ant-style pattern as the file name, for example to list only the properties files:
+
+[source,bash]
+----
+curl http://localhost:8080/q/download/*.properties?classpath=true
+----
+
+When the `Accept` header includes `html` (such as from a web browser), the list is returned as HTML links.
+
+To download a file, call `/q/download/` followed by the file name. The file is resolved from the classpath
+first, and then from the route source files. A file on the classpath can be downloaded even if it is not listed
+(when not using `classpath=true`).
+
+[source,bash]
+----
+curl http://localhost:8080/q/download/application.properties
+----
+
== JWT authentication
The embedded HTTP server can validate JWT bearer tokens on incoming requests.
(camel) 02/02: CAMEL-25116: camel-platform-http-main - /q/download?classpath=true lists the resources of the application (#27024)
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch fix/CAMEL-25116-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
commit dde44e43682f38926753a547313430538e038fba
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Sep 28 23:21:18 2026 +0200
CAMEL-25116: camel-platform-http-main - /q/download?classpath=true lists the resources of the application (#27024)
Co-Authored-By: Claude Opus 5.5 (1M context) <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
(cherry picked from commit 5204e4957608f2d83c713aa89a5609f8383bbfa2)
---
.../camel/catalog/docs/platform-http-main.adoc | 11 +-
.../src/main/docs/platform-http-main.adoc | 11 +-
.../platform/http/main/ManagementHttpServer.java | 196 +++++++++++++++++++--
.../http/main/ManagementHttpServerTest.java | 56 ++++++
4 files changed, 247 insertions(+), 27 deletions(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc
index 56622c5c085f..bf00766a9894 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/platform-http-main.adoc
@@ -48,14 +48,17 @@ File download is enabled with `camel.management.downloadEnabled=true`.
Calling `/q/download` without a file name returns a list of the files that can be downloaded.
By default, the list only contains the source files of the routes (such as `*.camel.yaml` files).
-To also list the resources from the classpath, add the `classpath=true` query parameter:
+To also list the resources of the application, add the `classpath=true` query parameter.
+These are the resources from the classpath directories (such as `target/classes`) and from the application JAR
+when the application is packaged, and when using `camel run` the files from the project directory
+(such as `application.properties` and `openapi.json`). The content of the dependency JARs is not included.
[source,bash]
----
# list route source files
curl http://localhost:8080/q/download
-# list route source files and all classpath resources
+# list route source files and the resources of the application
curl http://localhost:8080/q/download?classpath=true
----
@@ -69,8 +72,8 @@ curl http://localhost:8080/q/download/*.properties?classpath=true
When the `Accept` header includes `html` (such as from a web browser), the list is returned as HTML links.
To download a file, call `/q/download/` followed by the file name. The file is resolved from the classpath
-first, and then from the route source files. A file on the classpath can be downloaded even if it is not listed
-(when not using `classpath=true`).
+first, then from the route source files, and when using `camel run` from the project directory.
+A file on the classpath can be downloaded even if it is not listed (when not using `classpath=true`).
[source,bash]
----
diff --git a/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc b/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc
index 56622c5c085f..bf00766a9894 100644
--- a/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc
+++ b/components/camel-platform-http-main/src/main/docs/platform-http-main.adoc
@@ -48,14 +48,17 @@ File download is enabled with `camel.management.downloadEnabled=true`.
Calling `/q/download` without a file name returns a list of the files that can be downloaded.
By default, the list only contains the source files of the routes (such as `*.camel.yaml` files).
-To also list the resources from the classpath, add the `classpath=true` query parameter:
+To also list the resources of the application, add the `classpath=true` query parameter.
+These are the resources from the classpath directories (such as `target/classes`) and from the application JAR
+when the application is packaged, and when using `camel run` the files from the project directory
+(such as `application.properties` and `openapi.json`). The content of the dependency JARs is not included.
[source,bash]
----
# list route source files
curl http://localhost:8080/q/download
-# list route source files and all classpath resources
+# list route source files and the resources of the application
curl http://localhost:8080/q/download?classpath=true
----
@@ -69,8 +72,8 @@ curl http://localhost:8080/q/download/*.properties?classpath=true
When the `Accept` header includes `html` (such as from a web browser), the list is returned as HTML links.
To download a file, call `/q/download/` followed by the file name. The file is resolved from the classpath
-first, and then from the route source files. A file on the classpath can be downloaded even if it is not listed
-(when not using `classpath=true`).
+first, then from the route source files, and when using `camel run` from the project directory.
+A file on the classpath can be downloaded even if it is not listed (when not using `classpath=true`).
[source,bash]
----
diff --git a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java
index b04e9b4fa80b..c648a8ccc4ad 100644
--- a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java
+++ b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java
@@ -21,12 +21,21 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
+import java.net.JarURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -34,6 +43,7 @@ import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
@@ -67,7 +77,6 @@ import org.apache.camel.health.HealthCheckHelper;
import org.apache.camel.health.HealthCheckRegistry;
import org.apache.camel.http.base.HttpProtocolHeaderFilterStrategy;
import org.apache.camel.spi.HeaderFilterStrategy;
-import org.apache.camel.spi.PackageScanResourceResolver;
import org.apache.camel.spi.ReloadStrategy;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.ResourceLoader;
@@ -99,6 +108,8 @@ import org.slf4j.LoggerFactory;
public class ManagementHttpServer extends ServiceSupport implements CamelContextAware, StaticService {
private static final Logger LOG = LoggerFactory.getLogger(ManagementHttpServer.class);
+ // the files camel run adds to the classpath
+ private static final String CLASSPATH_FILES = "camel.jbang.classpathFiles";
private static final int BODY_MAX_CHARS = 128 * 1024;
private static final int DEFAULT_POLL_TIMEOUT = 20000;
@@ -805,30 +816,27 @@ public class ManagementHttpServer extends ServiceSupport implements CamelContext
if (name == null || name.isBlank() || matcher.isPattern(name)) {
Set<String> names = new TreeSet<>();
if (cp) {
- // also look inside classpath
- PackageScanResourceResolver resolver = PluginHelper.getPackageScanResourceResolver(camelContext);
- resolver.addClassLoader(camelContext.getApplicationContextClassLoader());
- try {
- String pattern = "**/*";
- if (name != null && !name.isBlank()) {
- pattern = "**/" + name;
- }
- for (Resource res : resolver.findResources(pattern)) {
- String loc = res.getLocation();
- loc = LoggerHelper.sourceNameOnly(loc);
- names.add(loc);
+ // also include the resources of the application itself
+ String pattern = "**/*";
+ if (name != null && !name.isBlank()) {
+ pattern = "**/" + name;
+ }
+ for (String n : findApplicationResources()) {
+ if (matcher.match(pattern, n)) {
+ names.add(n);
}
- } catch (Exception e) {
- // ignore
}
}
// always include routes
for (org.apache.camel.Route route : camelContext.getRoutes()) {
String loc = route.getSourceLocation();
if (loc != null) {
- loc = LoggerHelper.sourceNameOnly(loc);
- if (name == null || name.isBlank() || matcher.match(name, loc)) {
- names.add(loc);
+ final String n = sourceName(loc);
+ // skip routes from a classpath directory that are already listed as a resource
+ boolean listed = cp && FileUtil.isAbsolute(new File(n))
+ && names.stream().anyMatch(r -> n.endsWith("/" + r));
+ if (!listed && (name == null || name.isBlank() || matcher.match(name, n))) {
+ names.add(n);
}
}
}
@@ -867,7 +875,7 @@ public class ManagementHttpServer extends ServiceSupport implements CamelContext
for (org.apache.camel.Route route : camelContext.getRoutes()) {
String loc = route.getSourceLocation();
if (loc != null) {
- loc = LoggerHelper.sourceNameOnly(loc);
+ loc = sourceName(loc);
if (matcher.match(name, loc)) {
res = route.getSourceResource();
break;
@@ -875,6 +883,15 @@ public class ManagementHttpServer extends ServiceSupport implements CamelContext
}
}
}
+ if (res == null || !res.exists()) {
+ // files from the project directory (such as application.properties) when using camel run
+ for (String f : findProjectFiles()) {
+ if (name.equals(f)) {
+ res = loader.resolveResource("file:" + f);
+ break;
+ }
+ }
+ }
if (res != null && res.exists()) {
ctx.response().putHeader("Content-Type", "application/octet-stream");
ctx.response().putHeader("Content-Disposition",
@@ -901,6 +918,147 @@ public class ManagementHttpServer extends ServiceSupport implements CamelContext
null, "text/plain,application/octet-stream", null);
}
+ /**
+ * The name of the source such as a route file, which for a packaged application is the path inside the JAR.
+ */
+ private static String sourceName(String location) {
+ String answer = LoggerHelper.sourceNameOnly(location);
+ int pos = answer.lastIndexOf("!/");
+ if (pos != -1) {
+ answer = answer.substring(pos + 2);
+ }
+ return answer;
+ }
+
+ /**
+ * Finds the resources of the application itself, that is the classpath directories and the classes folder of a
+ * packaged JAR, and the files from the project directory when using camel run. The content of dependency JARs is
+ * not included.
+ */
+ protected Set<String> findApplicationResources() {
+ Set<String> answer = new TreeSet<>();
+ // the application context classloader may not be the classloader of a packaged application
+ Set<ClassLoader> loaders = new LinkedHashSet<>();
+ if (camelContext.getApplicationContextClassLoader() != null) {
+ loaders.add(camelContext.getApplicationContextClassLoader());
+ }
+ if (Thread.currentThread().getContextClassLoader() != null) {
+ loaders.add(Thread.currentThread().getContextClassLoader());
+ }
+ loaders.add(ManagementHttpServer.class.getClassLoader());
+ // the base package of the application (such as the package of the main class) is used to find the JAR of
+ // a packaged application, as a JAR is not returned as a classpath root
+ String base = camelContext.getCamelContextExtension().getBasePackageScan();
+ String basePath = base != null && !base.isBlank() ? base.replace('.', '/') + "/" : null;
+ Map<String, Boolean> roots = new LinkedHashMap<>();
+ for (ClassLoader cl : loaders) {
+ try {
+ for (URL url : Collections.list(cl.getResources(""))) {
+ roots.putIfAbsent(url.toExternalForm(), false);
+ }
+ if (basePath != null) {
+ for (URL url : Collections.list(cl.getResources(basePath))) {
+ String s = url.toExternalForm();
+ int pos = s.lastIndexOf("!/");
+ if (pos != -1) {
+ roots.put(s.substring(0, pos + 2), true);
+ } else if (s.endsWith(basePath)) {
+ roots.putIfAbsent(s.substring(0, s.length() - basePath.length()), false);
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOG.debug("Error finding application resources due to: {}. This exception is ignored.", e.getMessage(),
+ e);
+ }
+ }
+ for (Map.Entry<String, Boolean> root : roots.entrySet()) {
+ try {
+ for (String n : listResourceRoot(URI.create(root.getKey()).toURL(), root.getValue())) {
+ if (isApplicationResource(n)) {
+ answer.add(n);
+ }
+ }
+ } catch (Exception e) {
+ LOG.debug("Error listing resources in: {} due to: {}. This exception is ignored.", root.getKey(),
+ e.getMessage(), e);
+ }
+ }
+ answer.addAll(findProjectFiles());
+ return answer;
+ }
+
+ private static List<String> listResourceRoot(URL url, boolean applicationJar) throws Exception {
+ List<String> answer = new ArrayList<>();
+ if ("file".equals(url.getProtocol())) {
+ Path root = Path.of(url.toURI());
+ if (Files.isDirectory(root)) {
+ try (Stream<Path> files = Files.walk(root)) {
+ files.filter(Files::isRegularFile)
+ .map(p -> FileUtil.normalizePath(root.relativize(p).toString()).replace('\\', '/'))
+ .forEach(answer::add);
+ }
+ }
+ } else if ("jar".equals(url.getProtocol())) {
+ // the root of a dependency JAR is not part of the application, but the classes folder of a packaged
+ // application (such as jar:nested:/app.jar/!BOOT-INF/classes/!/) and the application JAR are
+ String s = url.toExternalForm();
+ if (s.endsWith("!/")) {
+ s = s.substring(0, s.length() - 2);
+ }
+ if (s.endsWith(".jar") && !applicationJar) {
+ return answer;
+ }
+ URLConnection con = url.openConnection();
+ if (con instanceof JarURLConnection jar) {
+ String prefix = jar.getEntryName() != null ? jar.getEntryName() : "";
+ // the JAR file is cached by the JDK so it must not be closed
+ jar.getJarFile().stream()
+ .filter(e -> !e.isDirectory() && e.getName().startsWith(prefix))
+ .map(e -> e.getName().substring(prefix.length()))
+ .forEach(answer::add);
+ }
+ }
+ return answer;
+ }
+
+ /**
+ * The files from the project directory that camel run has added to the classpath or loaded as properties.
+ */
+ private List<String> findProjectFiles() {
+ List<String> answer = new ArrayList<>();
+ List<String> files = new ArrayList<>();
+ camelContext.getPropertiesComponent().resolveProperty(CLASSPATH_FILES)
+ .ifPresent(s -> files.addAll(Arrays.asList(s.split(","))));
+ for (String loc : camelContext.getPropertiesComponent().getLocations()) {
+ loc = StringHelper.before(loc, ";", loc);
+ if (loc.startsWith("file:")) {
+ files.add(loc.substring(5));
+ }
+ }
+ // only files inside the project directory
+ Path dir = Path.of("").toAbsolutePath().normalize();
+ for (String f : files) {
+ f = f.trim();
+ Path p = dir.resolve(f).normalize();
+ if (!f.isEmpty() && p.startsWith(dir) && Files.isRegularFile(p)) {
+ String n = FileUtil.normalizePath(dir.relativize(p).toString()).replace('\\', '/');
+ if (isApplicationResource(n)) {
+ answer.add(n);
+ }
+ }
+ }
+ return answer;
+ }
+
+ /**
+ * Whether the file is a resource that can be listed and downloaded, which excludes classes and JARs (camel run can
+ * add both to the classpath) and META-INF files.
+ */
+ private static boolean isApplicationResource(String name) {
+ return !name.endsWith(".class") && !name.endsWith(".jar") && !name.startsWith("META-INF/");
+ }
+
protected void setupSendConsole() {
final Route send = router.route("/q/send/")
.produces("application/json")
diff --git a/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/ManagementHttpServerTest.java b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/ManagementHttpServerTest.java
index 7d451dab1238..106be95dc381 100644
--- a/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/ManagementHttpServerTest.java
+++ b/components/camel-platform-http-main/src/test/java/org/apache/camel/component/platform/http/main/ManagementHttpServerTest.java
@@ -21,6 +21,7 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
+import java.util.List;
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
@@ -29,6 +30,8 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class ManagementHttpServerTest {
@@ -64,4 +67,57 @@ class ManagementHttpServerTest {
}
+ @Test
+ public void downloadListsApplicationResources() throws Exception {
+ ManagementHttpServer server = new ManagementHttpServer();
+
+ camelContext = new DefaultCamelContext();
+ // files that camel run adds to the classpath, only files inside the project directory are included
+ String clazz = "target/classes/" + ManagementHttpServer.class.getName().replace('.', '/') + ".class";
+ camelContext.getPropertiesComponent().addInitialProperty("camel.jbang.classpathFiles",
+ "pom.xml,../pom.xml," + clazz);
+ server.setCamelContext(camelContext);
+
+ server.setHost("0.0.0.0");
+ server.setPort(port.getPort());
+ server.setPath("/");
+ server.setDownloadEnabled(true);
+ server.start();
+ try {
+ HttpResponse<String> response = get("/q/download?classpath=true");
+ assertEquals(200, response.statusCode());
+ List<String> names = response.body().lines().toList();
+ // resources from the classpath
+ assertTrue(names.contains("log4j2.properties"), names.toString());
+ assertTrue(names.contains("basic-auth.properties"), names.toString());
+ // files from the project directory
+ assertTrue(names.contains("pom.xml"), names.toString());
+ assertFalse(names.contains("../pom.xml"), names.toString());
+ assertFalse(names.contains(clazz), names.toString());
+ // no classes or content of dependency JARs
+ assertTrue(names.stream().noneMatch(n -> n.endsWith(".class")), names.toString());
+ assertTrue(names.stream().noneMatch(n -> n.startsWith("META-INF/")), names.toString());
+
+ response = get("/q/download/*.jks?classpath=true");
+ assertEquals("test-camel-main-auth-jwt.jks", response.body());
+
+ response = get("/q/download/pom.xml");
+ assertEquals(200, response.statusCode());
+ assertTrue(response.body().contains("<artifactId>camel-platform-http-main</artifactId>"));
+
+ // classes cannot be downloaded
+ response = get("/q/download/" + clazz);
+ assertEquals(204, response.statusCode());
+ } finally {
+ server.stop();
+ }
+ }
+
+ private HttpResponse<String> get(String path) throws IOException, InterruptedException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create("http://localhost:" + port.getPort() + path))
+ .build();
+ return HttpClient.newBuilder().build().send(request, HttpResponse.BodyHandlers.ofString());
+ }
+
}