1717package org .apache .camel .dsl .jbang .core .common ;
1818
1919import java .io .File ;
20+ import java .lang .management .ManagementFactory ;
21+ import java .net .URI ;
2022import java .net .URL ;
21- import java .net .URLDecoder ;
22- import java .nio .charset .StandardCharsets ;
23+ import java .nio .file .Path ;
2324import java .util .ArrayList ;
2425import java .util .List ;
2526
@@ -46,9 +47,13 @@ public static boolean isRunningFromLauncher() {
4647 return true ;
4748 }
4849
49- // Check JAR path as fallback
50+ // Check filename only — substring match on full path could hit any app embedding camel-jbang-core
5051 String jarPath = getLauncherJarPath ();
51- return jarPath != null && jarPath .contains ("camel-launcher" );
52+ if (jarPath == null ) {
53+ return false ;
54+ }
55+ String filename = Path .of (jarPath ).getFileName ().toString ();
56+ return filename .startsWith ("camel-launcher" );
5257 }
5358
5459 /**
@@ -66,24 +71,45 @@ public static String getLauncherJarPath() {
6671 URL location = LauncherHelper .class .getProtectionDomain ()
6772 .getCodeSource ().getLocation ();
6873 if (location != null ) {
69- String urlStr = location .toString ();
70- // Handle nested JAR (Spring Boot loader)
71- if (urlStr .startsWith ("jar:file:" )) {
72- int idx = urlStr .indexOf ("!/" );
73- if (idx > 0 ) {
74- String path = urlStr .substring (9 , idx );
75- // Decode URL-encoded characters (spaces, special chars)
76- return URLDecoder .decode (path , StandardCharsets .UTF_8 );
77- }
74+ return parseJarPath (location .toString ());
75+ }
76+ } catch (Exception e ) {
77+ System .err .println ("WARN: Failed to detect launcher JAR path: " + e .getMessage ());
78+ }
79+ return null ;
80+ }
81+
82+ /**
83+ * Parses a code-source URL string and returns the filesystem path to the outer JAR. Handles three URL forms:
84+ * <ul>
85+ * <li>{@code jar:nested:/outer.jar/!BOOT-INF/lib/inner.jar!/} — Spring Boot 3.2+/4.x loader</li>
86+ * <li>{@code jar:file:/outer.jar!/BOOT-INF/classes/} — Spring Boot 2.x / shade plugin</li>
87+ * <li>{@code file:/path/to/app.jar} — direct file URL</li>
88+ * </ul>
89+ * Uses {@link URI}-based path decoding to correctly handle percent-encoded characters and Windows drive-letter
90+ * paths (e.g. {@code /C:/...} → {@code C:\...}). {@code indexOf("/!")} is used rather than {@code lastIndexOf} so
91+ * that a JAR whose path itself contains {@code /!} (unlikely but possible) does not lose its prefix.
92+ */
93+ static String parseJarPath (String urlStr ) {
94+ try {
95+ if (urlStr .startsWith ("jar:nested:" )) {
96+ // Spring Boot 3.2+/4.x: jar:nested:/outer.jar/!BOOT-INF/lib/inner.jar!/
97+ String path = urlStr .substring ("jar:nested:" .length ());
98+ int idx = path .indexOf ("/!" );
99+ if (idx > 0 ) {
100+ return Path .of (URI .create ("file:" + path .substring (0 , idx ))).toString ();
78101 }
79- // Handle direct file URL
80- if (urlStr .startsWith ("file:" )) {
81- String path = urlStr .substring (5 );
82- return URLDecoder .decode (path , StandardCharsets .UTF_8 );
102+ } else if (urlStr .startsWith ("jar:file:" )) {
103+ // Spring Boot 2.x / shade plugin: jar:file:/outer.jar!/BOOT-INF/classes/
104+ int idx = urlStr .indexOf ("!/" );
105+ if (idx > 0 ) {
106+ return Path .of (URI .create (urlStr .substring ("jar:" .length (), idx ))).toString ();
83107 }
108+ } else if (urlStr .startsWith ("file:" )) {
109+ return Path .of (URI .create (urlStr )).toString ();
84110 }
85111 } catch (Exception e ) {
86- System .err .println ("WARN: Failed to detect launcher JAR path: " + e .getMessage ());
112+ System .err .println ("WARN: Failed to parse JAR path from URL '" + urlStr + "' : " + e .getMessage ());
87113 }
88114 return null ;
89115 }
@@ -98,10 +124,25 @@ public static List<String> getCamelCommand() {
98124 String jarPath = getLauncherJarPath ();
99125 if (jarPath != null ) {
100126 cmds .add (getJavaCommand ());
127+ // Forward -D and -X JVM arguments so child processes inherit proxy, truststore,
128+ // and memory settings. Skips -javaagent/-agentlib flags to avoid port conflicts.
129+ ManagementFactory .getRuntimeMXBean ().getInputArguments ().stream ()
130+ .filter (arg -> arg .startsWith ("-D" ) || arg .startsWith ("-X" ))
131+ .forEach (cmds ::add );
101132 cmds .add ("-jar" );
102133 cmds .add (jarPath );
103134 return cmds ;
104135 }
136+ // Launcher detected but JAR path unresolvable — log raw URL to aid diagnosis
137+ try {
138+ URL location = LauncherHelper .class .getProtectionDomain ().getCodeSource ().getLocation ();
139+ System .err .println (
140+ "WARN: Running from launcher but JAR path could not be resolved; falling back to 'camel'. Code-source URL: "
141+ + location );
142+ } catch (Exception ignored ) {
143+ System .err .println (
144+ "WARN: Running from launcher but JAR path could not be resolved; falling back to 'camel'." );
145+ }
105146 }
106147
107148 // Fall back to JBang-style command
0 commit comments