GlueGen v2.6.0-rc-20250712
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(0 >= ssp.lastIndexOf(".jar")) {
224 throw new IllegalArgumentException("No Jar name in <"+classJarUri+">");
225 }
226 if(DEBUG) {
227 System.err.println("getJarName res: "+ssp);
228 }
229 return ssp;
230 }
231
232 /**
233 * The Class's <code>com.jogamp.common.GlueGenVersion</code>
234 * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
235 * Jar basename <code>gluegen-rt.jar</code> will be returned.
236 * <p>
237 * <i>sub_protocol</i> may be "file", "http", etc..
238 * </p>
239 *
240 * @param clazzBinName <code>com.jogamp.common.GlueGenVersion</code>
241 * @param cl
242 * @return <code>gluegen-rt.jar</code>
243 * @throws IllegalArgumentException if the Uri doesn't match the expected formatting
244 * @throws IOException if the class's Jar file could not been found by the ClassLoader.
245 * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
246 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
247 */
248 public static Uri.Encoded getJarBasename(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
249 return getJarBasename( getJarUri(clazzBinName, cl) );
250 }
251
252 /**
253 * The Class's Jar Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
254 * Jar file's entry <code>/com/jogamp/common/GlueGenVersion.class</code> will be returned.
255 *
256 * @param classJarUri as retrieved w/ {@link #getJarUri(String, ClassLoader) getJarUri("com.jogamp.common.GlueGenVersion", cl)},
257 * i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
258 * @return <code>/com/jogamp/common/GlueGenVersion.class</code>
259 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
260 */
261 public static Uri.Encoded getJarEntry(final Uri classJarUri) {
262 if(null == classJarUri) {
263 throw new IllegalArgumentException("Uri is null");
264 }
265 if( !classJarUri.isJarScheme() ) {
266 throw new IllegalArgumentException("Uri is not a using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">");
267 }
268 final Uri.Encoded uriSSP = classJarUri.schemeSpecificPart;
269
270 // from
271 // file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
272 // to
273 // /com/jogamp/common/GlueGenVersion.class
274 final int idx = uriSSP.lastIndexOf(Uri.JAR_SCHEME_SEPARATOR);
275 if (0 <= idx) {
276 final Uri.Encoded res = uriSSP.substring(idx+1); // right of '!'
277 // Uri TODO ? final String res = Uri.decode(uriSSP.substring(idx+1)); // right of '!'
278 if(DEBUG) {
279 System.err.println("getJarEntry res: "+classJarUri+" -> "+uriSSP+" -> "+idx+" -> "+res);
280 }
281 return res;
282 } else {
283 throw new IllegalArgumentException("JAR Uri does not contain jar uri terminator '!', uri <"+classJarUri+">");
284 }
285 }
286
287 /**
288 * The Class's <code>"com.jogamp.common.GlueGenVersion"</code>
289 * Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>
290 * Jar file Uri <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/</code> will be returned.
291 * <p>
292 * <i>sub_protocol</i> may be "file", "http", etc..
293 * </p>
294 *
295 * @param clazzBinName "com.jogamp.common.GlueGenVersion"
296 * @param cl
297 * @return "jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/"
298 * @throws IllegalArgumentException if the Uri doesn't match the expected formatting or null arguments
299 * @throws IOException if the class's Jar file could not been found by the ClassLoader
300 * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
301 * @see {@link IOUtil#getClassURL(String, ClassLoader)}
302 */
303 public static Uri getJarFileUri(final String clazzBinName, final ClassLoader cl) throws IllegalArgumentException, IOException, URISyntaxException {
304 if(null == clazzBinName || null == cl) {
305 throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl);
306 }
307 final Uri jarSubUri = getJarUri(clazzBinName, cl).getContainedUri();
308 final Uri uri = Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUri.toString()+"!/");
309 if(DEBUG) {
310 System.err.println("getJarFileUri res: "+uri);
311 }
312 return uri;
313 }
314
315 /**
316 * @param baseUri file:/some/path/
317 * @param jarFileName gluegen-rt.jar (Uri encoded)
318 * @return jar:file:/some/path/gluegen-rt.jar!/
319 * @throws URISyntaxException
320 * @throws IllegalArgumentException null arguments
321 */
322 public static Uri getJarFileUri(final Uri baseUri, final Uri.Encoded jarFileName) throws IllegalArgumentException, URISyntaxException {
323 if(null == baseUri || null == jarFileName) {
324 throw new IllegalArgumentException("null arguments: baseUri "+baseUri+", jarFileName "+jarFileName);
325 }
326 return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+baseUri.toString()+jarFileName+"!/");
327 }
328
329 /**
330 * @param jarSubUri file:/some/path/gluegen-rt.jar
331 * @return jar:file:/some/path/gluegen-rt.jar!/
332 * @throws IllegalArgumentException null arguments
333 * @throws URISyntaxException
334 */
335 public static Uri getJarFileUri(final Uri jarSubUri) throws IllegalArgumentException, URISyntaxException {
336 if(null == jarSubUri) {
337 throw new IllegalArgumentException("jarSubUri is null");
338 }
339 return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUri.toString()+"!/");
340 }
341
342 /**
343 * @param jarSubUriS file:/some/path/gluegen-rt.jar (Uri encoded)
344 * @return jar:file:/some/path/gluegen-rt.jar!/
345 * @throws IllegalArgumentException null arguments
346 * @throws URISyntaxException
347 */
348 public static Uri getJarFileUri(final Uri.Encoded jarSubUriS) throws IllegalArgumentException, URISyntaxException {
349 if(null == jarSubUriS) {
350 throw new IllegalArgumentException("jarSubUriS is null");
351 }
352 return Uri.cast(Uri.JAR_SCHEME+Uri.SCHEME_SEPARATOR+jarSubUriS+"!/");
353 }
354
355 /**
356 * @param jarFileUri jar:file:/some/path/gluegen-rt.jar!/
357 * @param jarEntry com/jogamp/common/GlueGenVersion.class
358 * @return jar:file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class
359 * @throws IllegalArgumentException null arguments
360 * @throws URISyntaxException
361 */
362 public static Uri getJarEntryUri(final Uri jarFileUri, final Uri.Encoded jarEntry) throws IllegalArgumentException, URISyntaxException {
363 if(null == jarEntry) {
364 throw new IllegalArgumentException("jarEntry is null");
365 }
366 return Uri.cast(jarFileUri.toString()+jarEntry);
367 }
368
369 /**
370 * @param clazzBinName com.jogamp.common.util.cache.TempJarCache
371 * @param cl domain
372 * @return JarFile containing the named class within the given ClassLoader
373 * @throws IOException if the class's Jar file could not been found by the ClassLoader
374 * @throws IllegalArgumentException null arguments
375 * @throws URISyntaxException if the Uri could not be translated into a RFC 2396 Uri
376 * @see {@link #getJarFileUri(String, ClassLoader)}
377 */
378 public static JarFile getJarFile(final String clazzBinName, final ClassLoader cl) throws IOException, IllegalArgumentException, URISyntaxException {
379 return getJarFile( getJarFileUri(clazzBinName, cl) );
380 }
381
382 /**
383 * @param jarFileUri jar:file:/some/path/gluegen-rt.jar!/
384 * @return JarFile as named by Uri within the given ClassLoader
385 * @throws IllegalArgumentException null arguments
386 * @throws IOException if the Jar file could not been found
387 * @throws URISyntaxException
388 */
389 public static JarFile getJarFile(final Uri jarFileUri) throws IOException, IllegalArgumentException, URISyntaxException {
390 if(null == jarFileUri) {
391 throw new IllegalArgumentException("null jarFileUri");
392 }
393 if(DEBUG) {
394 System.err.println("getJarFile.0: "+jarFileUri.toString());
395 }
396 final URL jarFileURL = jarFileUri.toURL();
397 if(DEBUG) {
398 System.err.println("getJarFile.1: "+jarFileURL.toString());
399 }
400 final URLConnection urlc = jarFileURL.openConnection();
401 if(urlc instanceof JarURLConnection) {
402 final JarURLConnection jarConnection = (JarURLConnection)jarFileURL.openConnection();
403 final JarFile jarFile = jarConnection.getJarFile();
404 if(DEBUG) {
405 System.err.println("getJarFile res: "+jarFile.getName());
406 }
407 return jarFile;
408 }
409 if(DEBUG) {
410 System.err.println("getJarFile res: NULL");
411 }
412 return null;
413 }
414
415 /**
416 * Locates the {@link JarUtil#getJarFileUri(Uri) Jar file Uri} of a given resource
417 * relative to a given class's Jar's Uri.
418 * <pre>
419 * class's jar url path + cutOffInclSubDir + relResPath,
420 * </pre>
421 * or with <code>cutOffInclSubDir</code> being <code>null</code> just
422 * <pre>
423 * class's jar url path + relResPath,
424 * </pre>
425 * Example #1
426 * <pre>
427 * classFromJavaJar = com.lighting.Test (in: file:/storage/TestLighting.jar)
428 * cutOffInclSubDir = null
429 * relResPath = LightAssets.jar
430 * Result : file:/storage/LightAssets.jar
431 * </pre>
432 * Example #2
433 * <pre>
434 * classFromJavaJar = com.lighting.Test (in: file:/storage/TestLighting.jar)
435 * cutOffInclSubDir = lights/
436 * relResPath = LightAssets.jar
437 * Result : file:/storage/lights/LightAssets.jar
438 * </pre>
439 * Example #3
440 * <pre>
441 * classFromJavaJar = com.lighting.Test (in: file:/storage/lights/TestLighting.jar)
442 * cutOffInclSubDir = lights/
443 * relResPath = LightAssets.jar
444 * Result : file:/storage/lights/LightAssets.jar
445 * </pre>
446 *
447 * TODO: Enhance documentation!
448 *
449 * @param classFromJavaJar Used to get the root Uri for the class's Jar Uri.
450 * @param cutOffInclSubDir Optional <i>cut off</i> included sub-directory prepending the relative resource path.
451 * If the root Uri includes cutOffInclSubDir, it is no more added to the result.
452 * Parameter will be ignored if <code>null</code>.
453 * @param relResPath The relative resource path. (Uri encoded)
454 * @return The resulting resource Uri, which is not tested.
455 * @throws IllegalArgumentException
456 * @throws IOException
457 * @throws URISyntaxException
458 */
459 public static Uri getRelativeOf(final Class<?> classFromJavaJar, final Uri.Encoded cutOffInclSubDir, final Uri.Encoded relResPath) throws IllegalArgumentException, IOException, URISyntaxException {
460 final ClassLoader cl = classFromJavaJar.getClassLoader();
461 final Uri classJarUri = JarUtil.getJarUri(classFromJavaJar.getName(), cl);
462 if( DEBUG ) {
463 System.err.println("JarUtil.getRelativeOf: "+"(classFromJavaJar "+classFromJavaJar+", classJarUri "+classJarUri+
464 ", cutOffInclSubDir "+cutOffInclSubDir+", relResPath "+relResPath+"): ");
465 }
466 final Uri jarSubUri = classJarUri.getContainedUri();
467 if(null == jarSubUri) {
468 throw new IllegalArgumentException("JarSubUri is null of: "+classJarUri);
469 }
470 final Uri.Encoded jarUriRoot = jarSubUri.getDirectory().getEncoded();
471 if( DEBUG ) {
472 System.err.println("JarUtil.getRelativeOf: "+"uri "+jarSubUri.toString()+" -> "+jarUriRoot);
473 }
474 final Uri.Encoded resUri;
475 if( null == cutOffInclSubDir || jarUriRoot.endsWith(cutOffInclSubDir.get()) ) {
476 resUri = jarUriRoot.concat(relResPath);
477 } else {
478 resUri = jarUriRoot.concat(cutOffInclSubDir).concat(relResPath);
479 }
480 if( DEBUG ) {
481 System.err.println("JarUtil.getRelativeOf: "+"... -> "+resUri);
482 }
483 final Uri resJarUri = JarUtil.getJarFileUri(resUri);
484 if( DEBUG ) {
485 System.err.println("JarUtil.getRelativeOf: "+"fin "+resJarUri);
486 }
487 return resJarUri;
488 }
489
490 /**
491 * Return a map from native-lib-base-name to entry-name.
492 */
493 public static Map<String, String> getNativeLibNames(final JarFile jarFile) {
494 if (DEBUG) {
495 System.err.println("JarUtil: getNativeLibNames: "+jarFile);
496 }
497
498 final Map<String,String> nameMap = new HashMap<String, String>();
499 final Enumeration<JarEntry> entries = jarFile.entries();
500
501 while (entries.hasMoreElements()) {
502 final JarEntry entry = entries.nextElement();
503 final String entryName = entry.getName();
504 final String baseName = NativeLibrary.isValidNativeLibraryName(entryName, false);
505
506 if(null != baseName) {
507 nameMap.put(baseName, entryName);
508 }
509 }
510
511 return nameMap;
512 }
513
514 /**
515 * Extract the files of the given jar file.
516 * <p>
517 * If <code>extractNativeLibraries</code> is true,
518 * native libraries are added to the given <code>nativeLibMap</code>
519 * with the base name to temp file location.<br>
520 * A file is identified as a native library,
521 * if it's name complies with the running platform's native library naming scheme.<br>
522 * Root entries are favored over non root entries in case of naming collisions.<br>
523 * Example on a Unix like machine:<br>
524 * <pre>
525 * mylib.jar!/sub1/libsour.so -> sour (mapped, unique name)
526 * mylib.jar!/sub1/libsweet.so (dropped, root entry favored)
527 * mylib.jar!/libsweet.so -> sweet (mapped, root entry favored)
528 * mylib.jar!/sweet.dll -> (dropped, not a unix library name)
529 * </pre>
530 * </p>
531 * <p>
532 * In order to be compatible with Java Web Start, we need
533 * to extract all root entries from the jar file.<br>
534 * In this case, set all flags to true <code>extractNativeLibraries </code>.
535 * <code>extractClassFiles</code>, <code>extractOtherFiles</code>.
536 * </p>
537 *
538 * @param dest
539 * @param nativeLibMap
540 * @param jarFile
541 * @param nativeLibraryPath if not null, only extracts native libraries within this path.
542 * @param extractNativeLibraries
543 * @param extractClassFiles
544 * @param extractOtherFiles
545 * @param deepDirectoryTraversal
546 * @return
547 * @throws IOException
548 */
549 public static final int extract(final File dest, final Map<String, String> nativeLibMap,
550 final JarFile jarFile,
551 final String nativeLibraryPath,
552 final boolean extractNativeLibraries,
553 final boolean extractClassFiles, final boolean extractOtherFiles) throws IOException {
554
555 if (DEBUG) {
556 System.err.println("JarUtil: extract: "+jarFile.getName()+" -> "+dest+
557 ", extractNativeLibraries "+extractNativeLibraries+" ("+nativeLibraryPath+")"+
558 ", extractClassFiles "+extractClassFiles+
559 ", extractOtherFiles "+extractOtherFiles);
560 }
561 int num = 0;
562
563 final Enumeration<JarEntry> entries = jarFile.entries();
564 while (entries.hasMoreElements()) {
565 final JarEntry entry = entries.nextElement();
566 final String entryName = entry.getName();
567
568 // Match entries with correct prefix and suffix (ignoring case)
569 final String libBaseName = NativeLibrary.isValidNativeLibraryName(entryName, false);
570 final boolean isNativeLib = null != libBaseName;
571 if(isNativeLib) {
572 if(!extractNativeLibraries) {
573 if (DEBUG) {
574 System.err.println("JarUtil: JarEntry : " + entryName + " native-lib skipped, skip all native libs");
575 }
576 continue;
577 }
578 if(null != nativeLibraryPath) {
579 final String nativeLibraryPathS;
580 final String dirnameS;
581 try {
582 nativeLibraryPathS = IOUtil.slashify(nativeLibraryPath, false /* startWithSlash */, true /* endWithSlash */);
583 dirnameS = IOUtil.getDirname(entryName);
584 } catch (final URISyntaxException e) {
585 throw new IOException(e);
586 }
587 if( !nativeLibraryPathS.equals(dirnameS) ) {
588 if (DEBUG) {
589 System.err.println("JarUtil: JarEntry : " + entryName + " native-lib skipped, not in path: "+nativeLibraryPathS);
590 }
591 continue;
592 }
593 }
594 }
595
596 final boolean isClassFile = entryName.endsWith(".class");
597 if(isClassFile && !extractClassFiles) {
598 if (DEBUG) {
599 System.err.println("JarUtil: JarEntry : " + entryName + " class-file skipped");
600 }
601 continue;
602 }
603
604 if(!isNativeLib && !isClassFile && !extractOtherFiles) {
605 if (DEBUG) {
606 System.err.println("JarUtil: JarEntry : " + entryName + " other-file skipped");
607 }
608 continue;
609 }
610
611 final boolean isDir = entryName.endsWith("/");
612
613 final boolean isRootEntry = entryName.indexOf('/') == -1 &&
614 entryName.indexOf(File.separatorChar) == -1;
615
616 if (DEBUG) {
617 System.err.println("JarUtil: JarEntry : isNativeLib " + isNativeLib +
618 ", isClassFile " + isClassFile + ", isDir " + isDir +
619 ", isRootEntry " + isRootEntry );
620 }
621
622 final File destFile = new File(dest, entryName);
623 if(isDir) {
624 if (DEBUG) {
625 System.err.println("JarUtil: MKDIR: " + entryName + " -> " + destFile );
626 }
627 destFile.mkdirs();
628 } else {
629 final File destFolder = new File(destFile.getParent());
630 if(!destFolder.exists()) {
631 if (DEBUG) {
632 System.err.println("JarUtil: MKDIR (parent): " + entryName + " -> " + destFolder );
633 }
634 destFolder.mkdirs();
635 }
636 final InputStream in = new BufferedInputStream(jarFile.getInputStream(entry));
637 final OutputStream out = new BufferedOutputStream(new FileOutputStream(destFile));
638 int numBytes = -1;
639 try {
640 numBytes = IOUtil.copyStream2Stream(BUFFER_SIZE, in, out);
641 } finally {
642 in.close();
643 out.close();
644 }
645 boolean addedAsNativeLib = false;
646 if (numBytes>0) {
647 num++;
648 if (isNativeLib && ( isRootEntry || !nativeLibMap.containsKey(libBaseName) ) ) {
649 nativeLibMap.put(libBaseName, destFile.getAbsolutePath());
650 addedAsNativeLib = true;
651 fixNativeLibAttribs(destFile);
652 }
653 }
654 if (DEBUG) {
655 System.err.println("JarUtil: EXTRACT["+num+"]: [" + libBaseName + " -> ] " + entryName + " -> " + destFile + ": "+numBytes+" bytes, addedAsNativeLib: "+addedAsNativeLib);
656 }
657 }
658 }
659 return num;
660 }
661
662 /**
663 * Mitigate file permission issues of native library files, i.e.:
664 * <ul>
665 * <li>Bug 865: Safari >= 6.1 [OSX]: May employ xattr on 'com.apple.quarantine' on 'PluginProcess.app'</li>
666 * </ul>
667 */
668 private final static void fixNativeLibAttribs(final File file) {
669 // We tolerate UnsatisfiedLinkError (and derived) to solve the chicken and egg problem
670 // of loading gluegen's native library.
671 // On Safari(OSX), Bug 865, we simply hope the destination folder is executable.
673 final String fileAbsPath = file.getAbsolutePath();
674 try {
675 fixNativeLibAttribs(fileAbsPath);
676 if( DEBUG ) {
677 System.err.println("JarUtil.fixNativeLibAttribs: "+fileAbsPath+" - OK");
678 }
679 } catch (final Throwable t) {
680 if( DEBUG ) {
681 System.err.println("JarUtil.fixNativeLibAttribs: "+fileAbsPath+" - "+t.getClass().getSimpleName()+": "+t.getMessage());
682 }
683 }
684 }
685 }
686 private native static boolean fixNativeLibAttribs(String fname);
687
688 /**
689 * Validate the certificates for each native Lib in the jar file.
690 * Throws an IOException if any certificate is not valid.
691 * <pre>
692 Certificate[] rootCerts = Something.class.getProtectionDomain().
693 getCodeSource().getCertificates();
694 </pre>
695 */
696 public static final void validateCertificates(final Certificate[] rootCerts, final JarFile jarFile)
697 throws IOException, SecurityException {
698
699 if (DEBUG) {
700 System.err.println("JarUtil: validateCertificates: "+jarFile.getName());
701 }
702
703 if (rootCerts == null || rootCerts.length == 0) {
704 throw new IllegalArgumentException("Null certificates passed");
705 }
706
707 final byte[] buf = new byte[1024];
708 final Enumeration<JarEntry> entries = jarFile.entries();
709 while (entries.hasMoreElements()) {
710 final JarEntry entry = entries.nextElement();
711 if( ! entry.isDirectory() && ! entry.getName().startsWith("META-INF/") ) {
712 // only validate non META-INF and non directories
713 validateCertificate(rootCerts, jarFile, entry, buf);
714 }
715 }
716 }
717
718 /**
719 * Check the certificates with the ones in the jar file
720 * (all must match).
721 */
722 private static final void validateCertificate(final Certificate[] rootCerts,
723 final JarFile jar, final JarEntry entry, final byte[] buf) throws IOException, SecurityException {
724
725 if (DEBUG) {
726 System.err.println("JarUtil: validate JarEntry : " + entry.getName());
727 }
728
729 // API states that we must read all of the data from the entry's
730 // InputStream in order to be able to get its certificates
731
732 final InputStream is = jar.getInputStream(entry);
733 try {
734 while (is.read(buf) > 0) { }
735 } finally {
736 is.close();
737 }
738
739 // Get the certificates for the JAR entry
740 final Certificate[] nativeCerts = entry.getCertificates();
741 if (nativeCerts == null || nativeCerts.length == 0) {
742 throw new SecurityException("no certificate for " + entry.getName() + " in " + jar.getName());
743 }
744
745 if( !SecurityUtil.equals(rootCerts, nativeCerts) ) {
746 throw new SecurityException("certificates not equal for " + entry.getName() + " in " + jar.getName());
747 }
748 }
749}
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:549
static Uri getJarEntryUri(final Uri jarFileUri, final Uri.Encoded jarEntry)
Definition: JarUtil.java:362
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:248
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:348
static Map< String, String > getNativeLibNames(final JarFile jarFile)
Return a map from native-lib-base-name to entry-name.
Definition: JarUtil.java:493
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:261
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:303
static Uri getJarFileUri(final Uri jarSubUri)
Definition: JarUtil.java:335
static final void validateCertificates(final Certificate[] rootCerts, final JarFile jarFile)
Validate the certificates for each native Lib in the jar file.
Definition: JarUtil.java:696
static Uri getJarFileUri(final Uri baseUri, final Uri.Encoded jarFileName)
Definition: JarUtil.java:322
static JarFile getJarFile(final String clazzBinName, final ClassLoader cl)
Definition: JarUtil.java:378
static JarFile getJarFile(final Uri jarFileUri)
Definition: JarUtil.java:389
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:459
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