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

GlueGen v2.6.0 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(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.
669 if( Platform.OSType.MACOS == Platform.getOSType() || Platform.OSType.IOS == Platform.getOSType() ) {
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}
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:546
com.jogamp.common.util.JarUtil.getJarEntryUri
static Uri getJarEntryUri(final Uri jarFileUri, final Uri.Encoded jarEntry)
Definition: JarUtil.java:359
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:245
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:345
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:490
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:258
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:300
com.jogamp.common.util.JarUtil.getJarFileUri
static Uri getJarFileUri(final Uri jarSubUri)
Definition: JarUtil.java:332
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:693
com.jogamp.common.util.JarUtil.getJarFileUri
static Uri getJarFileUri(final Uri baseUri, final Uri.Encoded jarFileName)
Definition: JarUtil.java:319
com.jogamp.common.util.JarUtil.getJarFile
static JarFile getJarFile(final String clazzBinName, final ClassLoader cl)
Definition: JarUtil.java:375
com.jogamp.common.util.JarUtil.getJarFile
static JarFile getJarFile(final Uri jarFileUri)
Definition: JarUtil.java:386
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:456
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)