You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by fm...@apache.org on 2026/05/04 17:38:43 UTC
(camel) branch main updated: CAMEL-23370: Fix PackageScanJarResource losing subdirectory paths in JARs
This is an automated email from the ASF dual-hosted git repository.
Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 178b0aee0bed CAMEL-23370: Fix PackageScanJarResource losing subdirectory paths in JARs
178b0aee0bed is described below
commit 178b0aee0bed837ff4db0d230ea6ae71d74cd7a1
Author: Croway <fe...@gmail.com>
AuthorDate: Mon May 4 18:30:51 2026 +0200
CAMEL-23370: Fix PackageScanJarResource losing subdirectory paths in JARs
Replace URLClassLoader-based stream opening in getInputStream() with
direct jar: URL resolution via URLConnection. The URLClassLoader field
was a resource leak (never closed) and its getResourceAsStream()
behavior with leading-/ paths is platform-dependent. The new approach
builds the full jar: URL from the stored location and opens it directly,
which is more reliable and avoids JAR file locking on Windows.
Add a test that creates a temporary JAR with resources in subdirectories,
scans with classpath wildcard, and verifies all resources are found with
working InputStreams.
---
.../DefaultPackageScanResourceResolverTest.java | 56 ++++++++++++++++++++++
.../camel/support/scan/PackageScanJarResource.java | 15 +++---
2 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolverTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolverTest.java
index 927df0d163d9..ff23af47adbe 100644
--- a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolverTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultPackageScanResourceResolverTest.java
@@ -17,11 +17,22 @@
package org.apache.camel.impl.engine;
import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.spi.PackageScanResourceResolver;
+import org.apache.camel.spi.Resource;
import org.apache.camel.support.PluginHelper;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,4 +59,49 @@ public class DefaultPackageScanResourceResolverTest {
assertThat(resolver.findResources("file:src/test/resources/org/apache/camel/impl/engine/c?/*.xml"))
.isEmpty();
}
+
+ @Test
+ public void testJarResourcesScanWithSubdirectories(@TempDir Path tempDir) throws Exception {
+ Path jarPath = tempDir.resolve("test-routes.jar");
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(jarPath))) {
+ addJarDirectory(jos, "routes/");
+ addJarDirectory(jos, "routes/sub1/");
+ addJarDirectory(jos, "routes/sub2/");
+ addJarEntry(jos, "routes/sub1/a.txt", "content-a");
+ addJarEntry(jos, "routes/sub2/b.txt", "content-b");
+ addJarEntry(jos, "routes/c.txt", "content-c");
+ }
+
+ URLClassLoader jarClassLoader = new URLClassLoader(new URL[] { jarPath.toUri().toURL() });
+ try (DefaultCamelContext ctx = new DefaultCamelContext()) {
+ PackageScanResourceResolver resolver = PluginHelper.getPackageScanResourceResolver(ctx);
+ resolver.addClassLoader(jarClassLoader);
+
+ Collection<Resource> resources = resolver.findResources("classpath:routes/**/*.txt");
+
+ assertThat(resources).hasSize(3);
+
+ for (Resource resource : resources) {
+ try (InputStream is = resource.getInputStream()) {
+ assertThat(is).isNotNull();
+ String content = new String(is.readAllBytes(), StandardCharsets.UTF_8);
+ assertThat(content).startsWith("content-");
+ }
+ }
+ } finally {
+ jarClassLoader.close();
+ }
+ }
+
+ private static void addJarDirectory(JarOutputStream jos, String name) throws Exception {
+ JarEntry entry = new JarEntry(name);
+ jos.putNextEntry(entry);
+ jos.closeEntry();
+ }
+
+ private static void addJarEntry(JarOutputStream jos, String name, String content) throws Exception {
+ jos.putNextEntry(new JarEntry(name));
+ jos.write(content.getBytes(StandardCharsets.UTF_8));
+ jos.closeEntry();
+ }
}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanJarResource.java b/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanJarResource.java
index ac063c4bd3a8..3c30444f65a2 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanJarResource.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanJarResource.java
@@ -22,20 +22,17 @@ import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
-import java.net.URLClassLoader;
+import java.net.URLConnection;
import org.apache.camel.support.ResourceSupport;
-import org.apache.camel.util.StringHelper;
public class PackageScanJarResource extends ResourceSupport {
private final URL url;
- private final URLClassLoader uc;
public PackageScanJarResource(String scheme, URL url, String shortName) {
super(scheme, url.getFile() + shortName);
this.url = url;
- this.uc = new URLClassLoader(new URL[] { url });
}
@Override
@@ -59,8 +56,14 @@ public class PackageScanJarResource extends ResourceSupport {
@Override
public InputStream getInputStream() throws IOException {
- String loc = StringHelper.afterLast(getLocation(), "!");
- return uc.getResourceAsStream(loc);
+ try {
+ URL jarUrl = URI.create("jar:" + getLocation()).toURL();
+ URLConnection con = jarUrl.openConnection();
+ con.setUseCaches(false);
+ return con.getInputStream();
+ } catch (Exception e) {
+ throw new IOException("Cannot open JAR resource: " + getLocation(), e);
+ }
}
}