GlueGen: src/java/com/jogamp/common/util/JarUtil.java Source File

GlueGen v2.6.0-rc-20250722 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
54 public 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.
672 if( Platform.OSType.MACOS == Platform.getOSType() || Platform.OSType.IOS == Platform.getOSType() ) {
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}
com.jogamp.common.net.Uri.Encoded
Immutable RFC3986 encoded string.
Definition: Uri.java:296
com.jogamp.common.net.Uri.Encoded.lastIndexOf
final int lastIndexOf(final int ch)
See String#lastIndexOf(int).
Definition: Uri.java:430
com.jogamp.common.net.Uri.Encoded.concat
Encoded concat(final Encoded encoded)
See String#concat(String).
Definition: Uri.java:413
com.jogamp.common.net.Uri
This class implements an immutable Uri as defined by RFC 2396.
Definition: Uri.java:160
com.jogamp.common.net.Uri.schemeSpecificPart
final Encoded schemeSpecificPart
Encoded scheme-specific-part, never null.
Definition: Uri.java:1209
com.jogamp.common.net.Uri.HTTP_SCHEME
static final String HTTP_SCHEME
{@value}
Definition: Uri.java:285
com.jogamp.common.net.Uri.valueOf
static Uri valueOf(final File file)
Creates a new Uri instance using the given File instance.
Definition: Uri.java:1121
com.jogamp.common.net.Uri.JAR_SCHEME_SEPARATOR
static final char JAR_SCHEME_SEPARATOR
A JAR sub-protocol is separated from the JAR entry w/ this separator {@value}.
Definition: Uri.java:291
com.jogamp.common.net.Uri.toString
final String toString()
Returns the encoded input as String, never null, same as getEncoded().
Definition: Uri.java:1301
com.jogamp.common.net.Uri.getContainedUri
final Uri getContainedUri()
If this instance's schemeSpecificPart contains a Uri itself, a sub-Uri, return schemeSpecificPart + #...
Definition: Uri.java:1437
com.jogamp.common.net.Uri.HTTPS_SCHEME
static final String HTTPS_SCHEME
{@value}
Definition: Uri.java:287
com.jogamp.common.net.Uri.FILE_SCHEME
static final String FILE_SCHEME
{@value}
Definition: Uri.java:283
com.jogamp.common.net.Uri.JAR_SCHEME
static final String JAR_SCHEME
{@value}
Definition: Uri.java:289
com.jogamp.common.net.Uri.SCHEME_SEPARATOR
static final char SCHEME_SEPARATOR
{@value}
Definition: Uri.java:277
com.jogamp.common.net.Uri.isJarScheme
final boolean isJarScheme()
Returns true, if this instance is a jar scheme, otherwise false.
Definition: Uri.java:1286
com.jogamp.common.net.Uri.getDirectory
Uri getDirectory()
Returns this Uri's directory Uri.
Definition: Uri.java:1622
com.jogamp.common.net.Uri.cast
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
com.jogamp.common.net.Uri.getEncoded
final Encoded getEncoded()
Returns the encoded input, never null.
Definition: Uri.java:1293
com.jogamp.common.os.NativeLibrary
Provides low-level, relatively platform-independent access to shared ("native") libraries.
Definition: NativeLibrary.java:112
com.jogamp.common.os.NativeLibrary.isValidNativeLibraryName
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
Definition: NativeLibrary.java:599
com.jogamp.common.os.Platform
Utility class for querying platform specific properties.
Definition: Platform.java:58
com.jogamp.common.os.Platform.getOSType
static OSType getOSType()
Returns the OS type.
Definition: Platform.java:401
com.jogamp.common.util.IOUtil
Definition: IOUtil.java:69
com.jogamp.common.util.IOUtil.getClassURL
static URL getClassURL(final String clazzBinName, final ClassLoader cl)
Definition: IOUtil.java:474
com.jogamp.common.util.IOUtil.copyStream2Stream
static int copyStream2Stream(final InputStream in, final OutputStream out)
Copy the complete specified input stream to the specified output stream.
Definition: IOUtil.java:222
com.jogamp.common.util.IOUtil.slashify
static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash)
Definition: IOUtil.java:389
com.jogamp.common.util.IOUtil.getDirname
static String getDirname(String fname)
Returns unified '/' dirname including the last '/'.
Definition: IOUtil.java:499
com.jogamp.common.util.JarUtil
Definition: JarUtil.java:54
com.jogamp.common.util.JarUtil.extract
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
com.jogamp.common.util.JarUtil.getJarEntryUri
static Uri getJarEntryUri(final Uri jarFileUri, final Uri.Encoded jarEntry)
Definition: JarUtil.java:362
com.jogamp.common.util.JarUtil.getJarUri
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
com.jogamp.common.util.JarUtil.hasJarUri
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
com.jogamp.common.util.JarUtil.getJarBasename
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
com.jogamp.common.util.JarUtil.getJarBasename
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
com.jogamp.common.util.JarUtil.getJarFileUri
static Uri getJarFileUri(final Uri.Encoded jarSubUriS)
Definition: JarUtil.java:348
com.jogamp.common.util.JarUtil.getNativeLibNames
static Map< String, String > getNativeLibNames(final JarFile jarFile)
Return a map from native-lib-base-name to entry-name.
Definition: JarUtil.java:493
com.jogamp.common.util.JarUtil.getJarEntry
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
com.jogamp.common.util.JarUtil.getJarFileUri
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
com.jogamp.common.util.JarUtil.getJarFileUri
static Uri getJarFileUri(final Uri jarSubUri)
Definition: JarUtil.java:335
com.jogamp.common.util.JarUtil.validateCertificates
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
com.jogamp.common.util.JarUtil.getJarFileUri
static Uri getJarFileUri(final Uri baseUri, final Uri.Encoded jarFileName)
Definition: JarUtil.java:322
com.jogamp.common.util.JarUtil.getJarFile
static JarFile getJarFile(final String clazzBinName, final ClassLoader cl)
Definition: JarUtil.java:378
com.jogamp.common.util.JarUtil.getJarFile
static JarFile getJarFile(final Uri jarFileUri)
Definition: JarUtil.java:389
com.jogamp.common.util.JarUtil.getRelativeOf
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
com.jogamp.common.util.JarUtil.setResolver
static void setResolver(final Resolver r)
Setting a custom Resolver instance.
Definition: JarUtil.java:86
com.jogamp.common.os.Platform.OSType
Definition: Platform.java:60
com.jogamp.common.os.Platform.OSType.IOS
IOS
Definition: Platform.java:61
com.jogamp.common.os.Platform.OSType.MACOS
MACOS
Definition: Platform.java:61
com.jogamp.common.util.JarUtil.Resolver
Interface allowing users to provide an URL resolver that will convert custom classloader URLs like Ec...
Definition: JarUtil.java:71
com.jogamp.common.util.JarUtil.Resolver.resolve
URL resolve(URL url)