1212import java .io .FileFilter ;
1313import java .io .FileOutputStream ;
1414import java .io .IOException ;
15+ import java .lang .management .ManagementFactory ;
16+ import java .lang .reflect .Field ;
17+ import java .lang .reflect .InvocationTargetException ;
18+ import java .lang .reflect .Method ;
1519import java .net .URISyntaxException ;
1620import java .net .URL ;
1721import java .net .URLClassLoader ;
1822import java .util .ArrayList ;
1923import java .util .Collections ;
2024import java .util .Iterator ;
2125import java .util .List ;
26+ import java .util .Collection ;
27+
2228import java .util .jar .Attributes ;
2329import java .util .jar .JarOutputStream ;
2430import java .util .jar .Manifest ;
@@ -92,8 +98,76 @@ public static String setupPathingJarClassPath(
9298 return jarFile .getAbsolutePath ();
9399 }
94100
101+ public static String setupPathingJarClassPath (
102+ final File dir ,
103+ final Class context ,
104+ final Collection <String > excludedJarPrefixes ,
105+ final URL ... additionalClasspathUrls ) throws IOException {
106+ return setupPathingJarClassPath (
107+ new File (dir .getParentFile ().getAbsolutePath () + File .separator + "pathing" , "pathing.jar" ),
108+ null ,
109+ context ,
110+ excludedJarPrefixes ,
111+ additionalClasspathUrls );
112+ }
113+
114+ public static String setupPathingJarClassPath (
115+ final File jarFile ,
116+ final String mainClass ,
117+ final Class context ,
118+ final Collection <String > excludedJarPrefixes ,
119+ final URL ... additionalClasspathUrls ) throws IOException {
120+
121+ final File jarDir = jarFile .getParentFile ();
122+ final String classpath = getClasspath (context , excludedJarPrefixes , additionalClasspathUrls );
123+
124+ if (!jarDir .exists ()) {
125+ try {
126+ jarDir .mkdirs ();
127+ } catch (final Exception e ) {
128+ LOGGER .error ("Failed to create pathing jar directory: " + e );
129+ return null ;
130+ }
131+ }
132+
133+ if (jarFile .exists ()) {
134+ try {
135+ jarFile .delete ();
136+ } catch (final Exception e ) {
137+ LOGGER .error ("Failed to delete old pathing jar: " + e );
138+ return null ;
139+ }
140+ }
141+
142+ // build jar
143+ final Manifest manifest = new Manifest ();
144+ manifest .getMainAttributes ().put (Attributes .Name .MANIFEST_VERSION , "1.0" );
145+ manifest .getMainAttributes ().put (Attributes .Name .CLASS_PATH , classpath );
146+ if (mainClass != null ) {
147+ manifest .getMainAttributes ().put (Attributes .Name .MAIN_CLASS , mainClass );
148+ }
149+ // HP Fortify "Improper Resource Shutdown or Release" false positive
150+ // target is inside try-as-resource clause (and is auto-closeable) and
151+ // the FileOutputStream
152+ // is closed implicitly by target.close()
153+ try (final JarOutputStream target =
154+ new JarOutputStream (new FileOutputStream (jarFile ), manifest )) {
155+
156+ target .close ();
157+ }
158+
159+ return jarFile .getAbsolutePath ();
160+ }
161+
95162 private static String getClasspath (final Class context , final URL ... additionalUrls )
96163 throws IOException {
164+ return getClasspath (context , (Collection <String >) null , additionalUrls );
165+ }
166+
167+ private static String getClasspath (
168+ final Class context ,
169+ final Collection <String > excludedJarPrefixes ,
170+ final URL ... additionalUrls ) throws IOException {
97171
98172 try {
99173 final ArrayList <ClassLoader > classloaders = new ArrayList <>();
@@ -109,49 +183,105 @@ private static String getClasspath(final Class context, final URL... additionalU
109183
110184 final StringBuilder classpathBuilder = new StringBuilder ();
111185 for (final URL u : additionalUrls ) {
112- append (classpathBuilder , u );
186+ append (classpathBuilder , u , Collections . emptySet () );
113187 }
114188
115- // assume 0 is the system classloader and skip it
189+ boolean foundAnyUrls = false ;
190+
191+ // Process each classloader
116192 for (int i = 0 ; i < classloaders .size (); i ++) {
117193 final ClassLoader classLoader = classloaders .get (i );
118194
119195 if (classLoader instanceof URLClassLoader ) {
196+ // Java 8 and custom URLClassLoaders (including HBaseMiniClusterClassLoader)
120197 for (final URL u : ((URLClassLoader ) classLoader ).getURLs ()) {
121- append (classpathBuilder , u );
198+ append (classpathBuilder , u , excludedJarPrefixes );
199+ foundAnyUrls = true ;
122200 }
123201 } else if (classLoader instanceof VFSClassLoader ) {
124202 final VFSClassLoader vcl = (VFSClassLoader ) classLoader ;
125203 for (final FileObject f : vcl .getFileObjects ()) {
126- append (classpathBuilder , f .getURL ());
204+ append (classpathBuilder , f .getURL (), excludedJarPrefixes );
205+ foundAnyUrls = true ;
127206 }
128207 } else {
129- // Java 9+ classloaders (e.g., AppClassLoader/PlatformClassLoader) are not URLClassLoader.
130- // Skip explicit URL extraction; we'll fall back to the system classpath if nothing was
131- // added.
132- continue ;
208+ // Java 9+ classloaders (AppClassLoader, PlatformClassLoader)
209+ // Try to get URLs via reflection
210+ URL [] urls = null ;
211+
212+ // Try the BuiltinClassLoader.ucp field (Java 9-16)
213+ try {
214+ final Field ucpField = classLoader .getClass ().getDeclaredField ("ucp" );
215+ ucpField .setAccessible (true );
216+ final Object ucp = ucpField .get (classLoader );
217+ if (ucp != null ) {
218+ final Method getURLsMethod = ucp .getClass ().getMethod ("getURLs" );
219+ urls = (URL []) getURLsMethod .invoke (ucp );
220+ }
221+ } catch (Exception e ) {
222+ // Field/method not available, try alternative approach
223+ }
224+
225+ // Alternative: try getURLs() method directly (some custom loaders)
226+ if (urls == null ) {
227+ try {
228+ final Method getUrlsMethod = classLoader .getClass ().getMethod ("getURLs" );
229+ urls = (URL []) getUrlsMethod .invoke (classLoader );
230+ } catch (Exception e ) {
231+ // Method not available
232+ }
233+ }
234+
235+ if (urls != null ) {
236+ for (final URL u : urls ) {
237+ append (classpathBuilder , u , excludedJarPrefixes );
238+ foundAnyUrls = true ;
239+ }
240+ }
133241 }
134242 }
135243
136- if (classpathBuilder .length () == 0 ) {
244+ // If we didn't find any URLs from classloaders, fall back to system classpath
245+ if (!foundAnyUrls || classpathBuilder .length () == 0 ) {
137246 final String sysCp = System .getProperty ("java.class.path" );
138- return (sysCp == null ) ? "" : sysCp ;
247+ if (sysCp != null && !sysCp .isEmpty ()) {
248+ final String [] parts = sysCp .split (java .io .File .pathSeparator );
249+ for (final String part : parts ) {
250+ if (part == null || part .isEmpty ())
251+ continue ;
252+ try {
253+ append (classpathBuilder , new java .io .File (part ).toURI ().toURL (), excludedJarPrefixes );
254+ } catch (java .net .MalformedURLException e ) {
255+ // skip invalid entry
256+ }
257+ }
258+ }
139259 }
140- if (classpathBuilder .charAt (0 ) == ' ' ) {
260+
261+ // Remove leading space if present
262+ if (classpathBuilder .length () > 0 && classpathBuilder .charAt (0 ) == ' ' ) {
141263 classpathBuilder .deleteCharAt (0 );
142264 }
265+
143266 return classpathBuilder .toString ();
144267
145268 } catch (final URISyntaxException e ) {
146269 throw new IOException (e );
147270 }
148271 }
149272
150- private static void append (final StringBuilder classpathBuilder , final URL url )
151- throws URISyntaxException {
273+ private static void append (
274+ final StringBuilder classpathBuilder ,
275+ final URL url ,
276+ final Collection <String > excludedJarPrefixes ) throws URISyntaxException {
152277
153278 final File file = new File (url .toURI ());
154279
280+ // Skip excluded JARs by filename prefix
281+ if (isExcludedJar (file , excludedJarPrefixes )) {
282+ return ;
283+ }
284+
155285 // do not include dirs containing hadoop or accumulo site files
156286 if (!containsSiteFile (file )) {
157287 final int index = file .getAbsolutePath ().indexOf (":\\ " );
@@ -173,6 +303,27 @@ private static void append(final StringBuilder classpathBuilder, final URL url)
173303 }
174304 }
175305
306+ private static boolean isExcludedJar (
307+ final File file ,
308+ final Collection <String > excludedJarPrefixes ) {
309+ if (excludedJarPrefixes == null || excludedJarPrefixes .isEmpty ()) {
310+ return false ;
311+ }
312+ if (!file .isFile ()) {
313+ return false ;
314+ }
315+ final String name = file .getName ();
316+ if (!name .endsWith (".jar" )) {
317+ return false ;
318+ }
319+ for (final String prefix : excludedJarPrefixes ) {
320+ if (prefix != null && !prefix .isEmpty () && name .startsWith (prefix )) {
321+ return true ;
322+ }
323+ }
324+ return false ;
325+ }
326+
176327 private static boolean containsSiteFile (final File f ) {
177328 if (f .isDirectory ()) {
178329 final File [] sitefile = f .listFiles (new FileFilter () {
0 commit comments