GlueGen v2.6.0-rc-20250822
GlueGen, Native Binding Generator for Java™ (public API).
JarUtil.java
Go to the documentation of this file.
1/**
2 * Copyright 2011 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.common.util;
29
30import java.io.BufferedInputStream;
31import java.io.BufferedOutputStream;
32import java.io.File;
33import java.io.FileOutputStream;
34import java.io.IOException;
35import java.io.InputStream;
36import java.io.OutputStream;
37import java.net.JarURLConnection;
38import java.net.URISyntaxException;
39import java.net.URL;
40import java.net.URLConnection;
41import java.security.cert.Certificate;
42import java.util.Enumeration;
43import java.util.HashMap;
44import java.util.Map;
45import java.util.jar.JarEntry;
46import java.util.jar.JarFile;
47
48import com.jogamp.common.net.Uri;
49import com.jogamp.common.os.NativeLibrary;
50import com.jogamp.common.os.Platform;
51
52import jogamp.common.Debug;
53
54public class JarUtil {
55 private static final boolean DEBUG = Debug.debug("JarUtil");
56
57 private static final int BUFFER_SIZE = 4096;
58
59 /**
60 * Interface allowing users to provide an URL resolver that will convert custom classloader
61 * URLs like Eclipse/OSGi <i>bundleresource:</i> URLs to normal <i>jar:</i> URLs.
62 * <p>
63 * This might be required for custom classloader where the URI protocol is unknown
64 * to the standard runtime environment.
65 * </p>
66 * <p>
67 * Note: The provided resolver is only utilized if a given URI's protocol could not be resolved.
68 * I.e. it will not be invoked for known protocols like <i>http</i>, <i>https</i>, <i>jar</i> or <i>file</i>.
69 * </p>
70 */
71 public interface Resolver {
72 URL resolve(URL url);
73 }
74
75 private static Resolver resolver;
76
77 /**
78 * Setting a custom {@link Resolver} instance.
79 *
80 * @param r {@link Resolver} to use after querying class file URLs from the classloader.
81 * @throws IllegalArgumentException if the passed resolver is <code>null</code>
82 * @throws IllegalStateException if the resolver has already been set.
83 * @throws SecurityException if the security manager doesn't have the setFactory
84 * permission
85 */
86 public static void setResolver(final Resolver r) throws IllegalArgumentException, IllegalStateException, SecurityException {
87 if(r == null) {
88 throw new IllegalArgumentException("Null Resolver passed");
89 }
90
91 if(resolver != null) {
92 throw new IllegalStateException("Resolver already set!");
93 }
94
95 final SecurityManager security = System.getSecurityManager();
96 if(security != null) {
97 security.checkSetFactory();
98 }
99
100 resolver = r;
101 }
102
103 /**
104 * Returns <code>true</code> if the Class's <code>"com.jogamp.common.GlueGenVersion"</code>
105 * is loaded from a JarFile and hence has a Jar URI like
106 * URI <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>.
107 * <p>
108 * <i>sub_protocol</i> may be "file", "http", etc..
109 * </p>
110 *
111 * @param clazzBinName "com.jogamp.common.GlueGenVersion"
112 * @param cl
113 * @return true if the class is loaded from a Jar file, otherwise false.
114 * @see {@link #getJarUri(String, ClassLoader)}
115 */
116 public static boolean hasJarUri(final String clazzBinName, final ClassLoader cl) {
117 try {
118 return null != getJarUri(clazzBinName, cl);
119 } catch (final Exception e) { /* ignore */ }
120 return false;
121 }
122
123 /**
124 * The Class's <code>"com.jogamp.common.GlueGenVersion"</code>
125 * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>
126 * will be returned.
127 * <p>
128 * <i>sub_protocol</i> may be "file", "http", etc..
129 * </p>
130 *
131 * @param clazzBinName "com.jogamp.common.GlueGenVersion"
132 * @param cl ClassLoader to locate the JarFile
133 * @return "jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"
134 * @throws IllegalArgumentException if the Uri doesn't match the expected formatting or null arguments
135 * @throws IOException if the class's Jar file could not been found by the ClassLoader
136 * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
137 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
138 */
139 public static Uri getJarUri(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
140 if(null == clazzBinName || null == cl) {
141 throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl);
142 }
143 final Uri uri;
144 final URL url;
145 {
146 url = IOUtil.getClassURL(clazzBinName, cl);
147 final String scheme = url.getProtocol();
148 if( null != resolver &&
149 !scheme.equals( Uri.JAR_SCHEME ) &&
150 !scheme.equals( Uri.FILE_SCHEME ) &&
151 !scheme.equals( Uri.HTTP_SCHEME ) &&
152 !scheme.equals( Uri.HTTPS_SCHEME ) )
153 {
154 final URL _url = resolver.resolve( url );
155 uri = Uri.valueOf(_url);
156 if(DEBUG) {
157 System.err.println("getJarUri Resolver: "+url+"\n\t-> "+_url+"\n\t-> "+uri);
158 }
159 } else {
160 uri = Uri.valueOf(url);
161 if(DEBUG) {
162 System.err.println("getJarUri Default "+url+"\n\t-> "+uri);
163 }
164 }
165 }
166 if( !uri.isJarScheme() ) {
167 throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+uri+">");
168 }
169 if(DEBUG) {
170 System.err.println("getJarUri res: "+clazzBinName+" -> "+url+" -> "+uri);
171 }
172 return uri;
173 }
174
175
176 /**
177 * The Class's Jar Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
178 * Jar basename <code>gluegen-rt.jar</code> will be returned.
179 * <p>
180 * <i>sub_protocol</i> may be "file", "http", etc..
181 * </p>
182 *
183 * @param classJarUri as retrieved w/ {@link #getJarUri(String, ClassLoader) getJarUri("com.jogamp.common.GlueGenVersion", cl)},
184 * i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
185 * @return <code>gluegen-rt.jar</code>
186 * @throws IllegalArgumentException if the Uri doesn't match the expected formatting or is null
187 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
188 */
189 public static Uri.Encoded getJarBasename(final Uri classJarUri) throws IllegalArgumentException {
190 if(null == classJarUri) {
191 throw new IllegalArgumentException("Uri is null");
192 }
193 if( !classJarUri.isJarScheme() ) {
194 throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">");
195 }
196 Uri.Encoded ssp = classJarUri.schemeSpecificPart;
197
198 // from
199 // file:/some/path/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class
200 // to
201 // file:/some/path/gluegen-rt.jar
202 int idx = ssp.lastIndexOf(Uri.JAR_SCHEME_SEPARATOR);
203 if (0 <= idx) {
204 ssp = ssp.substring(0, idx); // exclude '!/'
205 } else {
206 throw new IllegalArgumentException("Uri does not contain jar uri terminator '!', in <"+classJarUri+">");
207 }
208
209 // from
210 // file:/some/path/gluegen-rt.jar
211 // to
212 // gluegen-rt.jar
213 idx = ssp.lastIndexOf('/');
214 if(0 > idx) {
215 // no abs-path, check for protocol terminator ':'
216 idx = ssp.lastIndexOf(':');
217 if(0 > idx) {
218 throw new IllegalArgumentException("Uri does not contain protocol terminator ':', in <"+classJarUri+">");
219 }
220 }
221 ssp = ssp.substring(idx+1); // just the jar name
222
223 if(DEBUG) {
224 System.err.println("getJarName res: "+ssp);
225 }
226 return ssp;
227 }
228
229 /**
230 * The Class's <code>com.jogamp.common.GlueGenVersion</code>
231 * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
232 * Jar basename <code>gluegen-rt.jar</code> will be returned.
233 * <p>
234 * <i>sub_protocol</i> may be "file", "http", etc..
235 * </p>
236 *
237 * @param clazzBinName <code>com.jogamp.common.GlueGenVersion</code>
238 * @param cl
239 * @return <code>gluegen-rt.jar</code>
240 * @throws IllegalArgumentException if the Uri doesn't match the expected formatting
241 * @throws IOException if the class's Jar file could not been found by the ClassLoader.
242 * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
243 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
244 */
245 public static Uri.Encoded getJarBasename(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
246 return getJarBasename( getJarUri(clazzBinName, cl) );
247 }
248
249 /**
250 * The Class's Jar Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
251 * Jar file's entry <code>/com/jogamp/common/GlueGenVersion.class</code> will be returned.
252 *
253 * @param classJarUri as retrieved w/ {@link #getJarUri(String, ClassLoader) getJarUri("com.jogamp.common.GlueGenVersion", cl)},
254 * i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
255 * @return <code>/com/jogamp/common/GlueGenVersion.class</code>
256 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
257 */
258 public static Uri.Encoded getJarEntry(final Uri classJarUri) {
259 if(null == classJarUri) {
260 throw new IllegalArgumentException("Uri is null");
261 }
262 if( !classJarUri.isJarScheme() ) {
263 throw new IllegalArgumentException("Uri is not a using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">");
264 }
265 final Uri.Encoded uriSSP = classJarUri.schemeSpecificPart;
266
267 // from
268 // file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
269 // to
270 // /com/jogamp/common/GlueGenVersion.class
271 final int idx = uriSSP.lastIndexOf(Uri.JAR_SCHEME_SEPARATOR);
272 if (0 <= idx) {
273 final Uri.Encoded res = uriSSP.substring(idx+1); // right of '!'
274 // Uri TODO ? final String res = Uri.decode(uriSSP.substring(idx+1)); // right of '!'
275 if(DEBUG) {
276 System.err.println("getJarEntry res: "+classJarUri+" -> "+uriSSP+" -> "+idx+" -> "+res);
277 }
278 return res;
279 } else {
280 throw new IllegalArgumentException("JAR Uri does not contain jar uri terminator '!', uri <"+classJarUri+">");
281 }
282 }
283
284 /**
285 * The Class's <code>"com.jogamp.common.GlueGenVersion"</code>
286 * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>
287 * Jar file Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/</code> will be returned.
288 * <p>
289 * <i>sub_protocol</i> may be "file", "http", etc..
290 * </p>
291 *
292 * @param clazzBinName "com.jogamp.common.GlueGenVersion"
293 * @param cl
294 * @return "jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/"
295 * @throws IllegalArgumentException if the Uri doesn't match the expected formatting or null arguments
296 * @throws IOException if the class's Jar file could not been found by the ClassLoader
297 * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
298 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
299 */
300 public static Uri getJarFileUri(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
301 if(null == clazzBinName || null == cl) {
302 throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl);
303 }
304 final Uri jarSubUri = getJarUri(clazzBinName, cl).getContainedUri();
305 final Uri uri = Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUri.toString()+"!/");
306 if(DEBUG) {
307 System.err.println("getJarFileUri res: "+uri);
308 }
309 return uri;
310 }
311
312 /**
313 * @param baseUri file:/some/path/
314 * @param jarFileName gluegen-rt.jar (Uri encoded)
315 * @return jar:file:/some/path/gluegen-rt.jar!/
316 * @throws URISyntaxException
317 * @throws IllegalArgumentException null arguments
318 */
319 public static Uri getJarFileUri(final Uri baseUri, final Uri.Encoded jarFileName) throws IllegalArgumentException, URISyntaxException {
320 if(null == baseUri || null == jarFileName) {
321 throw new IllegalArgumentException("null arguments: baseUri "+baseUri+", jarFileName "+jarFileName);
322 }
323 return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+baseUri.toString()+jarFileName+"!/");
324 }
325
326 /**
327 * @param jarSubUri file:/some/path/gluegen-rt.jar
328 * @return jar:file:/some/path/gluegen-rt.jar!/
329 * @throws IllegalArgumentException null arguments
330 * @throws URISyntaxException
331 */
332 public static Uri getJarFileUri(final Uri jarSubUri) throws IllegalArgumentException, URISyntaxException {
333 if(null == jarSubUri) {
334 throw new IllegalArgumentException("jarSubUri is null");
335 }
336 return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUri.toString()+"!/");
337 }
338
339 /**
340 * @param jarSubUriS file:/some/path/gluegen-rt.jar (Uri encoded)
341 * @return jar:file:/some/path/gluegen-rt.jar!/
342 * @throws IllegalArgumentException null arguments
343 * @throws URISyntaxException
344 */
345 public static Uri getJarFileUri(final Uri.Encoded jarSubUriS) throws IllegalArgumentException, URISyntaxException {
346 if(null == jarSubUriS) {
347 throw new IllegalArgumentException("jarSubUriS is null");
348 }
349 return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUriS+"!/");
350 }
351
352 /**
353 * @param jarFileUri jar:file:/some/path/gluegen-rt.jar!/
354 * @param jarEntry com/jogamp/common/GlueGenVersion.class
355 * @return jar:file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
356 * @throws IllegalArgumentException null arguments
357 * @throws URISyntaxException
358 */
359 public static Uri getJarEntryUri(final Uri jarFileUri, final Uri.Encoded jarEntry) throws IllegalArgumentException, URISyntaxException {
360 if(null == jarEntry) {
361 throw new IllegalArgumentException("jarEntry is null");
362 }
363 return Uri.cast(jarFileUri.toString()+jarEntry);
364 }
365
366 /**
367 * @param clazzBinName com.jogamp.common.util.cache.TempJarCache
368 * @param cl domain
369 * @return JarFile containing the named class within the given ClassLoader
370 * @throws IOException if the class's Jar file could not been found by the ClassLoader
371 * @throws IllegalArgumentException null arguments
372 * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
373 * @see {@link #getJarFileUri(String, ClassLoader)}
374 */
375 public static JarFile getJarFile(final String clazzBinName, final ClassLoader cl) throws IOException, IllegalArgumentException, URISyntaxException {
376 return getJarFile( getJarFileUri(clazzBinName, cl) );
377 }
378
379 /**
380 * @param jarFileUri jar:file:/some/path/gluegen-rt.jar!/
381 * @return JarFile as named by Uri within the given ClassLoader
382 * @throws IllegalArgumentException null arguments
383 * @throws IOException if the Jar file could not been found
384 * @throws URISyntaxException
385 */
386 public static JarFile getJarFile(final Uri jarFileUri) throws IOException, IllegalArgumentException, URISyntaxException {
387 if(null == jarFileUri) {
388 throw new IllegalArgumentException("null jarFileUri");
389 }
390 if(DEBUG) {
391 System.err.println("getJarFile.0: "+jarFileUri.toString());
392 }
393 final URL jarFileURL = jarFileUri.toURL();
394 if(DEBUG) {
395 System.err.println("getJarFile.1: "+jarFileURL.toString());
396 }
397 final URLConnection urlc = jarFileURL.openConnection();
398 if(urlc instanceof JarURLConnection) {
399 final JarURLConnection jarConnection = (JarURLConnection)jarFileURL.openConnection();
400 final JarFile jarFile = jarConnection.getJarFile();
401 if(DEBUG) {
402 System.err.println("getJarFile res: "+jarFile.getName());
403 }
404 return jarFile;
405 }
406 if(DEBUG) {
407 System.err.println("getJarFile res: NULL");
408 }
409 return null;
410 }
411
412 /**
413 * Locates the {@link JarUtil#getJarFileUri(Uri) Jar file Uri} of a given resource
414 * relative to a given class's Jar's Uri.
415 * <pre>
416 * class's jar url path + cutOffInclSubDir + relResPath,
417 * </pre>
418 * or with <code>cutOffInclSubDir</code> being <code>null</code> just
419 * <pre>
420 * class's jar url path + relResPath,
421 * </pre>
422 * Example #1
423 * <pre>
424 * classFromJavaJar = com.lighting.Test (in: file:/storage/TestLighting.jar)
425 * cutOffInclSubDir = null
426 * relResPath = LightAssets.jar
427 * Result : file:/storage/LightAssets.jar
428 * </pre>
429 * Example #2
430 * <pre>
431 * classFromJavaJar = com.lighting.Test (in: file:/storage/TestLighting.jar)
432 * cutOffInclSubDir = lights/
433 * relResPath = LightAssets.jar
434 * Result : file:/storage/lights/LightAssets.jar
435 * </pre>
436 * Example #3
437 * <pre>
438 * classFromJavaJar = com.lighting.Test (in: file:/storage/lights/TestLighting.jar)
439 * cutOffInclSubDir = lights/
440 * relResPath = LightAssets.jar
441 * Result : file:/storage/lights/LightAssets.jar
442 * </pre>
443 *
444 * TODO: Enhance documentation!
445 *
446 * @param classFromJavaJar Used to get the root Uri for the class's Jar Uri.
447 * @param cutOffInclSubDir Optional <i>cut off</i> included sub-directory prepending the relative resource path.
448 * If the root Uri includes cutOffInclSubDir, it is no more added to the result.
449 * Parameter will be ignored if <code>null</code>.
450 * @param relResPath The relative resource path. (Uri encoded)
451 * @return The resulting resource Uri, which is not tested.
452 * @throws IllegalArgumentException
453 * @throws IOException
454 * @throws URISyntaxException
455 */
456 public static Uri getRelativeOf(final Class<?> classFromJavaJar, final Uri.Encoded cutOffInclSubDir, final Uri.Encoded relResPath) throws IllegalArgumentException, IOException, URISyntaxException {
457 final ClassLoader cl = classFromJavaJar.getClassLoader();
458 final Uri classJarUri = JarUtil.getJarUri(classFromJavaJar.getName(), cl);
459 if( DEBUG ) {
460 System.err.println("JarUtil.getRelativeOf: "+"(classFromJavaJar "+classFromJavaJar+", classJarUri "+classJarUri+
461 ", cutOffInclSubDir "+cutOffInclSubDir+", relResPath "+relResPath+"): ");
462 }
463 final Uri jarSubUri = classJarUri.getContainedUri();
464 if(null == jarSubUri) {
465 throw new IllegalArgumentException("JarSubUri is null of: "+classJarUri);
466 }
467 final Uri.Encoded jarUriRoot = jarSubUri.getDirectory().getEncoded();
468 if( DEBUG ) {
469 System.err.println("JarUtil.getRelativeOf: "+"uri "+jarSubUri.toString()+" -> "+jarUriRoot);
470 }
471 final Uri.Encoded resUri;
472 if( null == cutOffInclSubDir || jarUriRoot.endsWith(cutOffInclSubDir.get()) ) {
473 resUri = jarUriRoot.concat(relResPath);
474 } else {
475 resUri = jarUriRoot.concat(cutOffInclSubDir).concat(relResPath);
476 }
477 if( DEBUG ) {
478 System.err.println("JarUtil.getRelativeOf: "+"... -> "+resUri);
479 }
480 final Uri resJarUri = JarUtil.getJarFileUri(resUri);
481 if( DEBUG ) {
482 System.err.println("JarUtil.getRelativeOf: "+"fin "+resJarUri);
483 }
484 return resJarUri;
485 }
486
487 /**
488 * Return a map from native-lib-base-name to entry-name.
489 */
490 public static Map<String, String> getNativeLibNames(final JarFile jarFile) {
491 if (DEBUG) {
492 System.err.println("JarUtil: getNativeLibNames: "+jarFile);
493 }
494
495 final Map<String,String> nameMap = new HashMap<String, String>();
496 final Enumeration<JarEntry> entries = jarFile.entries();
497
498 while (entries.hasMoreElements()) {
499 final JarEntry entry = entries.nextElement();
500 final String entryName = entry.getName();
501 final String baseName = NativeLibrary.isValidNativeLibraryName(entryName, false);
502
503 if(null != baseName) {
504 nameMap.put(baseName, entryName);
505 }
506 }
507
508 return nameMap;
509 }
510
511 /**
512 * Extract the files of the given jar file.
513 * <p>
514 * If <code>extractNativeLibraries</code> is true,
515 * native libraries are added to the given <code>nativeLibMap</code>
516 * with the base name to temp file location.<br>
517 * A file is identified as a native library,
518 * if it's name complies with the running platform's native library naming scheme.<br>
519 * Root entries are favored over non root entries in case of naming collisions.<br>
520 * Example on a Unix like machine:<br>
521 * <pre>
522 * mylib.jar!/sub1/libsour.so -> sour (mapped, unique name)
523 * mylib.jar!/sub1/libsweet.so (dropped, root entry favored)
524 * mylib.jar!/libsweet.so -> sweet (mapped, root entry favored)
525 * mylib.jar!/sweet.dll -> (dropped, not a unix library name)
526 * </pre>
527 * </p>
528 * <p>
529 * In order to be compatible with Java Web Start, we need
530 * to extract all root entries from the jar file.<br>
531 * In this case, set all flags to true <code>extractNativeLibraries </code>.
532 * <code>extractClassFiles</code>, <code>extractOtherFiles</code>.
533 * </p>
534 *
535 * @param dest
536 * @param nativeLibMap
537 * @param jarFile
538 * @param nativeLibraryPath if not null, only extracts native libraries within this path.
539 * @param extractNativeLibraries
540 * @param extractClassFiles
541 * @param extractOtherFiles
542 * @param deepDirectoryTraversal
543 * @return
544 * @throws IOException
545 */
546 public static final int extract(final File dest, final Map<String, String> nativeLibMap,
547 final JarFile jarFile,
548 final String nativeLibraryPath,
549 final boolean extractNativeLibraries,
550 final boolean extractClassFiles, final boolean extractOtherFiles) throws IOException {
551
552 if (DEBUG) {
553 System.err.println("JarUtil: extract: "+jarFile.getName()+" -> "+dest+
554 ", extractNativeLibraries "+extractNativeLibraries+" ("+nativeLibraryPath+")"+
555 ", extractClassFiles "+extractClassFiles+
556 ", extractOtherFiles "+extractOtherFiles);
557 }
558 int num = 0;
559
560 final Enumeration<JarEntry> entries = jarFile.entries();
561 while (entries.hasMoreElements()) {
562 final JarEntry entry = entries.nextElement();
563 final String entryName = entry.getName();
564
565 // Match entries with correct prefix and suffix (ignoring case)
566 final String libBaseName = NativeLibrary.isValidNativeLibraryName(entryName, false);
567 final boolean isNativeLib = null != libBaseName;
568 if(isNativeLib) {
569 if(!extractNativeLibraries) {
570 if (DEBUG) {
571 System.err.println("JarUtil: JarEntry : " + entryName + " native-lib skipped, skip all native libs");
572 }
573 continue;
574 }
575 if(null != nativeLibraryPath) {
576 final String nativeLibraryPathS;
577 final String dirnameS;
578 try {
579 nativeLibraryPathS = IOUtil.slashify(nativeLibraryPath, false /* startWithSlash */, true /* endWithSlash */);
580 dirnameS = IOUtil.getDirname(entryName);
581 } catch (final URISyntaxException e) {
582 throw new IOException(e);
583 }
584 if( !nativeLibraryPathS.equals(dirnameS) ) {
585 if (DEBUG) {
586 System.err.println("JarUtil: JarEntry : " + entryName + " native-lib skipped, not in path: "+nativeLibraryPathS);
587 }
588 continue;
589 }
590 }
591 }
592
593 final boolean isClassFile = entryName.endsWith(".class");
594 if(isClassFile && !extractClassFiles) {
595 if (DEBUG) {
596 System.err.println("JarUtil: JarEntry : " + entryName + " class-file skipped");
597 }
598 continue;
599 }
600
601 if(!isNativeLib && !isClassFile && !extractOtherFiles) {
602 if (DEBUG) {
603 System.err.println("JarUtil: JarEntry : " + entryName + " other-file skipped");
604 }
605 continue;
606 }
607
608 final boolean isDir = entryName.endsWith("/");
609
610 final boolean isRootEntry = entryName.indexOf('/') == -1 &&
611 entryName.indexOf(File.separatorChar) == -1;
612
613 if (DEBUG) {
614 System.err.println("JarUtil: JarEntry : isNativeLib " + isNativeLib +
615 ", isClassFile " + isClassFile + ", isDir " + isDir +
616 ", isRootEntry " + isRootEntry );
617 }
618
619 final File destFile = new File(dest, entryName);
620 if(isDir) {
621 if (DEBUG) {
622 System.err.println("JarUtil: MKDIR: " + entryName + " -> " + destFile );
623 }
624 destFile.mkdirs();
625 } else {
626 final File destFolder = new File(destFile.getParent());
627 if(!destFolder.exists()) {
628 if (DEBUG) {
629 System.err.println("JarUtil: MKDIR (parent): " + entryName + " -> " + destFolder );
630 }
631 destFolder.mkdirs();
632 }
633 final InputStream in = new BufferedInputStream(jarFile.getInputStream(entry));
634 final OutputStream out = new BufferedOutputStream(new FileOutputStream(destFile));
635 int numBytes = -1;
636 try {
637 numBytes = IOUtil.copyStream2Stream(BUFFER_SIZE, in, out);
638 } finally {
639 in.close();
640 out.close();
641 }
642 boolean addedAsNativeLib = false;
643 if (numBytes>0) {
644 num++;
645 if (isNativeLib && ( isRootEntry || !nativeLibMap.containsKey(libBaseName) ) ) {
646 nativeLibMap.put(libBaseName, destFile.getAbsolutePath());
647 addedAsNativeLib = true;
648 fixNativeLibAttribs(destFile);
649 }
650 }
651 if (DEBUG) {
652 System.err.println("JarUtil: EXTRACT["+num+"]: [" + libBaseName + " -> ] " + entryName + " -> " + destFile + ": "+numBytes+" bytes, addedAsNativeLib: "+addedAsNativeLib);
653 }
654 }
655 }
656 return num;
657 }
658
659 /**
660 * Mitigate file permission issues of native library files, i.e.:
661 * <ul>
662 * <li>Bug 865: Safari >= 6.1 [OSX]: May employ xattr on 'com.apple.quarantine' on 'PluginProcess.app'</li>
663 * </ul>
664 */
665 private final static void fixNativeLibAttribs(final File file) {
666 // We tolerate UnsatisfiedLinkError (and derived) to solve the chicken and egg problem
667 // of loading gluegen's native library.
668 // On Safari(OSX), Bug 865, we simply hope the destination folder is executable.
670 final String fileAbsPath = file.getAbsolutePath();
671 try {
672 fixNativeLibAttribs(fileAbsPath);
673 if( DEBUG ) {
674 System.err.println("JarUtil.fixNativeLibAttribs: "+fileAbsPath+" - OK");
675 }
676 } catch (final Throwable t) {
677 if( DEBUG ) {
678 System.err.println("JarUtil.fixNativeLibAttribs: "+fileAbsPath+" - "+t.getClass().getSimpleName()+": "+t.getMessage());
679 }
680 }
681 }
682 }
683 private native static boolean fixNativeLibAttribs(String fname);
684
685 /**
686 * Validate the certificates for each native Lib in the jar file.
687 * Throws an IOException if any certificate is not valid.
688 * <pre>
689 Certificate[] rootCerts = Something.class.getProtectionDomain().
690 getCodeSource().getCertificates();
691 </pre>
692 */
693 public static final void validateCertificates(final Certificate[] rootCerts, final JarFile jarFile)
694 throws IOException, SecurityException {
695
696 if (DEBUG) {
697 System.err.println("JarUtil: validateCertificates: "+jarFile.getName());
698 }
699
700 if (rootCerts == null || rootCerts.length == 0) {
701 throw new IllegalArgumentException("Null certificates passed");
702 }
703
704 final byte[] buf = new byte[1024];
705 final Enumeration<JarEntry> entries = jarFile.entries();
706 while (entries.hasMoreElements()) {
707 final JarEntry entry = entries.nextElement();
708 if( ! entry.isDirectory() && ! entry.getName().startsWith("META-INF/") ) {
709 // only validate non META-INF and non directories
710 validateCertificate(rootCerts, jarFile, entry, buf);
711 }
712 }
713 }
714
715 /**
716 * Check the certificates with the ones in the jar file
717 * (all must match).
718 */
719 private static final void validateCertificate(final Certificate[] rootCerts,
720 final JarFile jar, final JarEntry entry, final byte[] buf) throws IOException, SecurityException {
721
722 if (DEBUG) {
723 System.err.println("JarUtil: validate JarEntry : " + entry.getName());
724 }
725
726 // API states that we must read all of the data from the entry's
727 // InputStream in order to be able to get its certificates
728
729 final InputStream is = jar.getInputStream(entry);
730 try {
731 while (is.read(buf) > 0) { }
732 } finally {
733 is.close();
734 }
735
736 // Get the certificates for the JAR entry
737 final Certificate[] nativeCerts = entry.getCertificates();
738 if (nativeCerts == null || nativeCerts.length == 0) {
739 throw new SecurityException("no certificate for " + entry.getName() + " in " + jar.getName());
740 }
741
742 if( !SecurityUtil.equals(rootCerts, nativeCerts) ) {
743 throw new SecurityException("certificates not equal for " + entry.getName() + " in " + jar.getName());
744 }
745 }
746}
Immutable RFC3986 encoded string.
Definition: Uri.java:296
final int lastIndexOf(final int ch)
See String#lastIndexOf(int).
Definition: Uri.java:430
Encoded concat(final Encoded encoded)
See String#concat(String).
Definition: Uri.java:413
This class implements an immutable Uri as defined by RFC 2396.
Definition: Uri.java:160
final Encoded schemeSpecificPart
Encoded scheme-specific-part, never null.
Definition: Uri.java:1209
static final String HTTP_SCHEME
{@value}
Definition: Uri.java:285
static Uri valueOf(final File file)
Creates a new Uri instance using the given File instance.
Definition: Uri.java:1121
static final char JAR_SCHEME_SEPARATOR
A JAR sub-protocol is separated from the JAR entry w/ this separator {@value}.
Definition: Uri.java:291
final String toString()
Returns the encoded input as String, never null, same as getEncoded().
Definition: Uri.java:1301
final Uri getContainedUri()
If this instance's schemeSpecificPart contains a Uri itself, a sub-Uri, return schemeSpecificPart + #...
Definition: Uri.java:1437
static final String HTTPS_SCHEME
{@value}
Definition: Uri.java:287
static final String FILE_SCHEME
{@value}
Definition: Uri.java:283
static final String JAR_SCHEME
{@value}
Definition: Uri.java:289
static final char SCHEME_SEPARATOR
{@value}
Definition: Uri.java:277
final boolean isJarScheme()
Returns true, if this instance is a jar scheme, otherwise false.
Definition: Uri.java:1286
Uri getDirectory()
Returns this Uri's directory Uri.
Definition: Uri.java:1622
static Uri cast(final String encodedUri)
Casts the given encoded String to a new Encoded instance used to create the resulting Uri instance vi...
Definition: Uri.java:1068
final Encoded getEncoded()
Returns the encoded input, never null.
Definition: Uri.java:1293
Provides low-level, relatively platform-independent access to shared ("native") libraries.
static final String isValidNativeLibraryName(final String libName, final boolean isLowerCaseAlready)
Comparison of prefix and suffix of the given libName's basename is performed case insensitive
Utility class for querying platform specific properties.
Definition: Platform.java:58
static OSType getOSType()
Returns the OS type.
Definition: Platform.java:401
static URL getClassURL(final String clazzBinName, final ClassLoader cl)
Definition: IOUtil.java:474
static int copyStream2Stream(final InputStream in, final OutputStream out)
Copy the complete specified input stream to the specified output stream.
Definition: IOUtil.java:222
static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash)
Definition: IOUtil.java:389
static String getDirname(String fname)
Returns unified '/' dirname including the last '/'.
Definition: IOUtil.java:499
static final int extract(final File dest, final Map< String, String > nativeLibMap, final JarFile jarFile, final String nativeLibraryPath, final boolean extractNativeLibraries, final boolean extractClassFiles, final boolean extractOtherFiles)
Extract the files of the given jar file.
Definition: JarUtil.java:546
static Uri getJarEntryUri(final Uri jarFileUri, final Uri.Encoded jarEntry)
Definition: JarUtil.java:359
static Uri getJarUri(final String clazzBinName, final ClassLoader cl)
The Class's "com.jogamp.common.GlueGenVersion" Uri jar:sub_protocol:/some/path/gluegen-rt....
Definition: JarUtil.java:139
static boolean hasJarUri(final String clazzBinName, final ClassLoader cl)
Returns true if the Class's "com.jogamp.common.GlueGenVersion" is loaded from a JarFile and hence has...
Definition: JarUtil.java:116
static Uri.Encoded getJarBasename(final String clazzBinName, final ClassLoader cl)
The Class's com.jogamp.common.GlueGenVersion Uri jar:sub_protocol:/some/path/gluegen-rt....
Definition: JarUtil.java:245
static Uri.Encoded getJarBasename(final Uri classJarUri)
The Class's Jar Uri jar:sub_protocol:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion....
Definition: JarUtil.java:189
static Uri getJarFileUri(final Uri.Encoded jarSubUriS)
Definition: JarUtil.java:345
static Map< String, String > getNativeLibNames(final JarFile jarFile)
Return a map from native-lib-base-name to entry-name.
Definition: JarUtil.java:490
static Uri.Encoded getJarEntry(final Uri classJarUri)
The Class's Jar Uri jar:sub_protocol:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion....
Definition: JarUtil.java:258
static Uri getJarFileUri(final String clazzBinName, final ClassLoader cl)
The Class's "com.jogamp.common.GlueGenVersion" Uri jar:sub_protocol:/some/path/gluegen-rt....
Definition: JarUtil.java:300
static Uri getJarFileUri(final Uri jarSubUri)
Definition: JarUtil.java:332
static final void validateCertificates(final Certificate[] rootCerts, final JarFile jarFile)
Validate the certificates for each native Lib in the jar file.
Definition: JarUtil.java:693
static Uri getJarFileUri(final Uri baseUri, final Uri.Encoded jarFileName)
Definition: JarUtil.java:319
static JarFile getJarFile(final String clazzBinName, final ClassLoader cl)
Definition: JarUtil.java:375
static JarFile getJarFile(final Uri jarFileUri)
Definition: JarUtil.java:386
static Uri getRelativeOf(final Class<?> classFromJavaJar, final Uri.Encoded cutOffInclSubDir, final Uri.Encoded relResPath)
Locates the Jar file Uri of a given resource relative to a given class's Jar's Uri.
Definition: JarUtil.java:456
static void setResolver(final Resolver r)
Setting a custom Resolver instance.
Definition: JarUtil.java:86
Interface allowing users to provide an URL resolver that will convert custom classloader URLs like Ec...
Definition: JarUtil.java:71