diff options
-rw-r--r-- | make/build-test.xml | 2 | ||||
-rw-r--r-- | make/build.xml | 20 | ||||
-rwxr-xr-x | make/scripts/adb-install-all-armv7.sh | 2 | ||||
-rw-r--r-- | src/java/com/jogamp/common/jvm/JNILibLoaderBase.java | 182 | ||||
-rw-r--r-- | src/java/com/jogamp/common/os/Platform.java | 43 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/IOUtil.java | 12 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/JarUtil.java | 287 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/cache/TempJarCache.java | 174 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestJarUtil.java | 4 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestTempJarCache.java | 4 |
10 files changed, 474 insertions, 256 deletions
diff --git a/make/build-test.xml b/make/build-test.xml index 7540c1c..a5417b6 100644 --- a/make/build-test.xml +++ b/make/build-test.xml @@ -86,7 +86,7 @@ </path> <property name="junit.run.jars" value="${junit.jar}${path.separator}${ant.jar}${path.separator}${TestJarsInJar.jar}${path.separator}${gluegen.root}/${rootrel.build}/gluegen.jar${path.separator}${build_t}/gluegen-test.jar"/> <property name="junit.run.remote.jars" value="${junit.jar}${path.separator}${env.TARGET_ANT_HOME}/lib/ant.jar${path.separator}${env.TARGET_ANT_HOME}/lib/ant-junit.jar${path.separator}${gluegen.root}/make/lib/TestJarsInJar.jar${path.separator}${gluegen.root}/${rootrel.build}/gluegen.jar${path.separator}${build_t}/gluegen-test.jar"/> - <property name="junit.run.remote.apks" value="${gluegen.root}/${rootrel.build}/jogamp.android-launcher.apk${path.separator}${ant-junit-all.apk}${path.separator}${gluegen.root}/${rootrel.build}/gluegen-rt-android-${android.abi}.apk${path.separator}${build_t}/gluegen-test.apk${path.separator}${gluegen.root}/make/lib/TestJarsInJar.apk"/> + <property name="junit.run.remote.apks" value="${gluegen.root}/${rootrel.build}/jogamp-android-launcher.apk${path.separator}${ant-junit-all.apk}${path.separator}${gluegen.root}/${rootrel.build}/gluegen-rt-android-${android.abi}.apk${path.separator}${build_t}/gluegen-test.apk${path.separator}${gluegen.root}/make/lib/TestJarsInJar.apk"/> <property name="stub.includes.dir" value="stub_includes" /> <!-- NOTE: this MUST be relative for FileSet --> diff --git a/make/build.xml b/make/build.xml index 9b4e316..671e3c3 100644 --- a/make/build.xml +++ b/make/build.xml @@ -141,7 +141,7 @@ <property name="archive" value="${build}/${archive.name}" /> <!-- the launcher is compiled and packaged explicit - if android --> - <property name="jogamp.android-launcher.classes" value="jogamp/android/launcher/**"/> + <property name="jogamp-android-launcher.classes" value="jogamp/android/launcher/**"/> <!-- optional android classes - if android --> <property name="java.part.android" value="jogamp/common/os/android/**"/> @@ -150,7 +150,7 @@ <property name="gluegen-rt.classes" value="com/jogamp/gluegen/runtime/**"/> <property name="jogamp.common.classes" value="com/jogamp/common/** jogamp/common/**"/> - <property name="gluegen.excludes.all" value="${gluegen.excludes.nsig} ${jogamp.android-launcher.classes}" /> + <property name="gluegen.excludes.all" value="${gluegen.excludes.nsig} ${jogamp-android-launcher.classes}" /> </target> @@ -709,7 +709,7 @@ <jar destfile="${build}/gluegen.jar" manifest="${build}/Manifest.temp"> <fileset dir="${classes}"> <include name="**/*.class" /> - <exclude name="${jogamp.android-launcher.classes}" /> + <exclude name="${jogamp-android-launcher.classes}" /> <exclude name="${java.part.android}" /> </fileset> <fileset dir="resources/assets"> @@ -734,7 +734,7 @@ <include name="com/jogamp/gluegen/runtime/*.class" /> <include name="com/jogamp/common/**" /> <include name="jogamp/common/**" /> - <exclude name="${jogamp.android-launcher.classes}" /> + <exclude name="${jogamp-android-launcher.classes}" /> <exclude name="${java.part.android}" /> </fileset> <fileset dir="resources/assets"> @@ -793,7 +793,7 @@ <include name="com/jogamp/gluegen/runtime/*.class" /> <include name="com/jogamp/common/**" /> <include name="jogamp/common/**" /> - <exclude name="${jogamp.android-launcher.classes}" /> + <exclude name="${jogamp-android-launcher.classes}" /> </fileset> <fileset dir="resources/assets"> <include name="**" /> @@ -812,14 +812,14 @@ <srcfiles dir= "." includes="*.xml"/> <srcfiles dir= "resources/android" includes="**/*.xml"/> <srcfiles dir= "${src.java}/jogamp/android/launcher" includes="**"/> - <mapper type="merge" to="${build}/jogamp.android-launcher.jar"/> + <mapper type="merge" to="${build}/jogamp-android-launcher.jar"/> </uptodate> </target> <target name="android-launcher.build" depends="gluegen.cpptasks.detect.os,gluegen.build.check.android-launcher" if="isAndroid" unless="gluegen.build.skip.android-launcher"> <javac destdir="${classes}" includeAntRuntime="false" - includes="${jogamp.android-launcher.classes}" + includes="${jogamp-android-launcher.classes}" excludes="${jogamp.common.classes}" memoryMaximumSize="${javac.memorymax}" encoding="UTF-8" @@ -842,16 +842,16 @@ </filterset> </copy> - <jar destfile="${build}/jogamp.android-launcher.jar" manifest="${build}/Manifest-android-launcher.temp"> + <jar destfile="${build}/jogamp-android-launcher.jar" manifest="${build}/Manifest-android-launcher.temp"> <fileset dir="${classes}"> - <include name="${jogamp.android-launcher.classes}" /> + <include name="${jogamp-android-launcher.classes}" /> </fileset> </jar> <aapt.signed assetsdir="resources/assets-launcher" jarbuilddir="${build}" - jarbasename="jogamp.android-launcher" + jarbasename="jogamp-android-launcher" nativebuilddir="${build}" nativebasename="non-existing" androidmanifest.path="resources/android/AndroidManifest-Launcher.xml" diff --git a/make/scripts/adb-install-all-armv7.sh b/make/scripts/adb-install-all-armv7.sh index 6f271eb..9a0e4f7 100755 --- a/make/scripts/adb-install-all-armv7.sh +++ b/make/scripts/adb-install-all-armv7.sh @@ -1,2 +1,2 @@ -adb $* install ../build-android-armv7/jogamp.android-launcher.apk +adb $* install ../build-android-armv7/jogamp-android-launcher.apk adb $* install ../build-android-armv7/gluegen-rt-android-armeabi-v7a.apk diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java index 60f3060..b09a2f1 100644 --- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java +++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java @@ -39,11 +39,13 @@ package com.jogamp.common.jvm; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Arrays; import java.util.HashSet; import com.jogamp.common.os.NativeLibrary; @@ -141,76 +143,176 @@ public class JNILibLoaderBase { loaderAction = action; } + /* pp */ static final boolean addNativeJarLibsImpl(Class<?> classFromJavaJar, URL classJarURL, String nativeJarBasename, StringBuilder msg) + throws IOException, IllegalArgumentException, SecurityException + { + msg.setLength(0); // reset + msg.append("addNativeJarLibsImpl(classFromJavaJar ").append(classFromJavaJar).append(", classJarURL ").append(classJarURL).append(", nativeJarBaseName ").append(nativeJarBasename).append("): "); + boolean ok = false; + if(TempJarCache.isInitialized()) { + final String nativeJarName = nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar"; + msg.append(nativeJarName); + final URL jarUrlRoot = JarUtil.getURLDirname( JarUtil.getJarSubURL( classJarURL ) ); + msg.append(" + ").append(jarUrlRoot); + final URL nativeJarURL = JarUtil.getJarFileURL(jarUrlRoot, nativeJarName); + msg.append(" -> ").append(nativeJarURL); + if(DEBUG) { + System.err.println(msg.toString()); + } + TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURL); + ok = true; + } + return ok; + } + /** + * Loads and adds a JAR file's native library to the TempJarCache.<br> + * The native library JAR file's URL is derived as follows: + * <ul> + * <li> [1] <code>GLProfile.class</code> -> </li> + * <li> [2] <code>http://lala/</code> -> </li> + * <li> [4] <code>http://lala/'nativeJarBaseName'-'os.and.arch'.jar</code> </li> + * </ul> + * Where: + * <ul> + * <li> [1] is the <code>classFromJavaJar</code></li> + * <li> [2] is it's <i>URL path</i></li> + * <li> [4] is the derived native JAR filename</li> + * </ul> * * @param classFromJavaJar GLProfile - * @param nativeJarBaseName jogl-all + * @param nativeJarBasename jogl-all * @return true if the native JAR file loaded successful or were loaded already, false in case of an error */ - public static final boolean addNativeJarLibs(Class<?> classFromJavaJar, String nativeJarBaseName) { + public static final boolean addNativeJarLibs(Class<?> classFromJavaJar, String nativeJarBasename) { if(TempJarCache.isInitialized()) { - final String nativeJarName = nativeJarBaseName+"-natives-"+PlatformPropsImpl.os_and_arch+".jar"; - final ClassLoader cl = classFromJavaJar.getClassLoader(); + final StringBuilder msg = new StringBuilder(); try { - URL jarUrlRoot = JarUtil.getURLDirname( JarUtil.getJarSubURL( classFromJavaJar.getName(), cl ) ); - if(DEBUG) { - System.err.println("JNILibLoaderBase: addNativeJarLibs: "+nativeJarBaseName+": url-root "+jarUrlRoot); - } - URL nativeJarURL = JarUtil.getJarFileURL(jarUrlRoot, nativeJarName); - if(DEBUG) { - System.err.println("JNILibLoaderBase: addNativeJarLibs: "+nativeJarBaseName+": nativeJarURL "+nativeJarURL); - } - TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURL, cl); - return true; + final URL classJarURL = JarUtil.getJarURL(classFromJavaJar.getName(), classFromJavaJar.getClassLoader()); + return addNativeJarLibsImpl(classFromJavaJar, classJarURL, nativeJarBasename, msg); } catch (Exception e0) { // IllegalArgumentException, IOException - System.err.println("Catched: "+e0.getMessage()); + System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while "+msg.toString()); if(DEBUG) { e0.printStackTrace(); } - } + } + } else if(DEBUG) { + System.err.println("JNILibLoaderBase: addNativeJarLibs1: disabled due to uninitialized TempJarCache"); } return false; } /** - * @param classFromJavaJar A class file to determine the base URL of the native JAR files, eg.: GLProfile.class - * @param allNativeJarBaseName Attempt to use the 'all' native JAR variant first, if exists. Eg. "jogl-all" - * @param atomicNativeJarBaseNames Fallback to use all the atomic native JAR files, eg. [ "nativewindow", "jogl", "newt" ] - * @return true if either the 'all' native JAR or all of the atomic native JARs loaded successful or were loaded already, + * Loads and adds a JAR file's native library to the TempJarCache.<br> + * The native library JAR file's URL is derived as follows: + * <ul> + * <li> [1] <code>GLProfile.class</code> -> </li> + * <li> [2] <code>http://lala/gluegen-rt.jar</code> -> </li> + * <li> [3] <code>http://lala/gluegen-rt</code> -> </li> + * <li> [4] <code>http://lala/gluegen-rt-natives-'os.and.arch'.jar</code> </li> + * </ul> + * Where: + * <ul> + * <li> [1] is one of <code>classesFromJavaJars</code></li> + * <li> [2] is it's complete URL</li> + * <li> [3] is it's <i>base URL</i></li> + * <li> [4] is the derived native JAR filename</li> + * </ul> + * + * Examples:<br> + * <br> + * JOCL: + * <pre> + // only: jocl.jar -> jocl-natives-'os.and.arch'.jar + addNativeJarLibs(new Class<?>[] { JOCLJNILibLoader.class }, null, null ); + * </pre> + * + * Newt Only: + * <pre> + // either: [jogl-all.jar, jogl-all-noawt.jar, jogl-all-mobile.jar] -> jogl-all-natives-<os.and.arch>.jar + // or: nativewindow-core.jar -> nativewindow-natives-<os.and.arch>.jar, + // newt-core.jar -> newt-natives-<os.and.arch>.jar + JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { NWJNILibLoader.class, NEWTJNILibLoader.class }, "-all", new String[] { "-noawt", "-mobile", "-core" } ); + * </pre> + * + * JOGL: + * <pre> + final ClassLoader cl = GLProfile.class.getClassLoader(); + // either: [jogl-all.jar, jogl-all-noawt.jar, jogl-all-mobile.jar] -> jogl-all-natives-<os.and.arch>.jar + // or: nativewindow-core.jar -> nativewindow-natives-<os.and.arch>.jar, + // jogl-core.jar -> jogl-natives-<os.and.arch>.jar, + // (newt-core.jar -> newt-natives-<os.and.arch>.jar)? (if available) + final String newtFactoryClassName = "com.jogamp.newt.NewtFactory"; + final Class<?>[] classesFromJavaJars = new Class<?>[] { NWJNILibLoader.class, GLProfile.class, null }; + if( ReflectionUtil.isClassAvailable(newtFactoryClassName, cl) ) { + classesFromJavaJars[2] = ReflectionUtil.getClass(newtFactoryClassName, false, cl); + } + JNILibLoaderBase.addNativeJarLibs(classesFromJavaJars, "-all", new String[] { "-noawt", "-mobile", "-core" } ); + * </pre> + * + * @param classesFromJavaJars For each given Class, load the native library JAR. + * @param singleJarMarker Optional string marker like "-all" to identify the single 'all-in-one' JAR file + * after which processing of the class array shall stop. + * @param stripBasenameSuffixes Optional substrings to be stripped of the <i>base URL</i> + * + * @return true if either the 'all-in-one' native JAR or all native JARs loaded successful or were loaded already, * false in case of an error */ - public static boolean addNativeJarLibs(Class<?> classFromJavaJar, String allNativeJarBaseName, String[] atomicNativeJarBaseNames) { - boolean res = false; + public static boolean addNativeJarLibs(Class<?>[] classesFromJavaJars, String singleJarMarker, String[] stripBasenameSuffixes) { + if(DEBUG) { + System.err.println("JNILibLoaderBase: addNativeJarLibs0(classesFromJavaJars "+Arrays.asList(classesFromJavaJars)+", singleJarMarker "+singleJarMarker+", stripBasenameSuffixes "+Arrays.asList(stripBasenameSuffixes)); + } + boolean ok = false; if(TempJarCache.isInitialized()) { - final ClassLoader cl = classFromJavaJar.getClassLoader(); - try { - final String jarName = JarUtil.getJarBasename(classFromJavaJar.getName(), cl); - if(jarName!=null) { - if(!res && null != allNativeJarBaseName) { - // all-in-one variant 1st - res = JNILibLoaderBase.addNativeJarLibs(classFromJavaJar, allNativeJarBaseName); - } - if(!res && null != atomicNativeJarBaseNames) { - // atomic variant - res = true; - for(int i=0; res && i<atomicNativeJarBaseNames.length; i++) { - final String atomicNativeJarBaseName = atomicNativeJarBaseNames[i]; - if(null != atomicNativeJarBaseName && atomicNativeJarBaseName.length()>0) { - res = JNILibLoaderBase.addNativeJarLibs(classFromJavaJar, atomicNativeJarBaseName); - } + final StringBuilder msg = new StringBuilder(); + int count = 0; + try { + boolean done = false; + ok = true; + for(int i=0; !done && ok && i<classesFromJavaJars.length && null!=classesFromJavaJars[i]; i++) { + final ClassLoader cl = classesFromJavaJars[i].getClassLoader(); + final URL classJarURL = JarUtil.getJarURL(classesFromJavaJars[i].getName(), cl); + final String jarName = JarUtil.getJarBasename(classJarURL); + ok = null != jarName; + if(ok) { + final String jarBasename = jarName.substring(0, jarName.indexOf(".jar")); // ".jar" already validated w/ JarUtil.getJarBasename(..) + final String nativeJarBasename = stripName(jarBasename, stripBasenameSuffixes); + done = null != singleJarMarker && jarBasename.indexOf(singleJarMarker) >= 0; // done if single-jar ('all' variant) + ok = JNILibLoaderBase.addNativeJarLibsImpl(classesFromJavaJars[i], classJarURL, nativeJarBasename, msg); + if(ok) { count++; } + if(DEBUG && done) { + System.err.println("JNILibLoaderBase: addNativeJarLibs0: end after all-in-one JAR: "+jarBasename); } } } } catch (Exception e0) { // IllegalArgumentException, IOException - System.err.println("Catched: "+e0.getMessage()); + System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while "+msg.toString()); if(DEBUG) { e0.printStackTrace(); } + ok = false; } + if(DEBUG) { + System.err.println("JNILibLoaderBase: addNativeJarLibs0(..) done, count "+count+", ok "+ok); + } + } else if(DEBUG) { + System.err.println("JNILibLoaderBase: addNativeJarLibs0: disabled due to uninitialized TempJarCache"); } - return res; + return ok; + } + + private static final String stripName(String name, String[] suffixes) { + if(null != suffixes) { + for(int i=0; i<suffixes.length && null != suffixes[i]; i++) { + int idx = name.indexOf(suffixes[i]); + if(0 < idx) { + return name.substring(0, idx); + } + } + } + return name; } /** diff --git a/src/java/com/jogamp/common/os/Platform.java b/src/java/com/jogamp/common/os/Platform.java index a42ab25..ba89474 100644 --- a/src/java/com/jogamp/common/os/Platform.java +++ b/src/java/com/jogamp/common/os/Platform.java @@ -162,9 +162,21 @@ public class Platform extends PlatformPropsImpl { /** <code>true</code> if AWT is available and not in headless mode, otherwise <code>false</code>. */ public static final boolean AWT_AVAILABLE; + private static final URL platformClassJarURL; + static { PlatformPropsImpl.initSingleton(); // just documenting the order of static initialization - + + { + URL _platformClassJarURL; + try { + _platformClassJarURL = JarUtil.getJarURL(Platform.class.getName(), Platform.class.getClassLoader()); + } catch (Exception e) { + _platformClassJarURL = null; + } + platformClassJarURL = _platformClassJarURL; + } + USE_TEMP_JAR_CACHE = (OS_TYPE != OSType.ANDROID) && isRunningFromJarURL() && Debug.getBooleanProperty(useTempJarCachePropName, true, true); @@ -209,35 +221,32 @@ public class Platform extends PlatformPropsImpl { private Platform() {} /** - * Preemptively avoids initializing and using {@link TempJarCache} in case we are <b>not</b> running - * from a Jar URL, ie. plain class files. Used to set {@link USE_TEMP_JAR_CACHE}. - * <p> - * Impact: Less overhead and more robustness. - * </p> - * * @return true if we're running from a Jar URL, otherwise false */ - private static final boolean isRunningFromJarURL() { - return JarUtil.hasJarURL(Platform.class.getName(), Platform.class.getClassLoader()); + public static final boolean isRunningFromJarURL() { + return null != platformClassJarURL; } private static final void loadGlueGenRTImpl() { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { - final ClassLoader cl = Platform.class.getClassLoader(); if(USE_TEMP_JAR_CACHE && TempJarCache.initSingleton()) { - final String nativeJarName = libBaseName+"-natives-"+os_and_arch+".jar"; + String nativeJarName = null; + URL jarUrlRoot = null; + URL nativeJarURL = null; try { - final URL jarUrlRoot = JarUtil.getURLDirname( - JarUtil.getJarSubURL(Platform.class.getName(), cl) ); - final URL nativeJarURL = JarUtil.getJarFileURL(jarUrlRoot, nativeJarName); - TempJarCache.bootstrapNativeLib(Platform.class, libBaseName, nativeJarURL, cl); + final String jarName = JarUtil.getJarBasename(platformClassJarURL); + final String nativeJarBasename = jarName.substring(0, jarName.indexOf(".jar")); // ".jar" already validated w/ JarUtil.getJarBasename(..) + nativeJarName = nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar"; + jarUrlRoot = JarUtil.getURLDirname( JarUtil.getJarSubURL(platformClassJarURL) ); + nativeJarURL = JarUtil.getJarFileURL(jarUrlRoot, nativeJarName); + TempJarCache.bootstrapNativeLib(Platform.class, libBaseName, nativeJarURL); } catch (Exception e0) { // IllegalArgumentException, IOException - System.err.println("Catched: "+e0.getMessage()); + System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while TempJarCache.bootstrapNativeLib() of "+nativeJarURL+" ("+jarUrlRoot+" + "+nativeJarName+")"); } } - DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, cl); + DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, Platform.class.getClassLoader()); return null; } }); diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index 8c2a0c1..ffea7cb 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -338,19 +338,23 @@ public class IOUtil { } } - public static String getClassFileName(String clazzBinName) throws IOException { + public static String getClassFileName(String clazzBinName) { // or return clazzBinName.replace('.', File.separatorChar) + ".class"; ? return clazzBinName.replace('.', '/') + ".class"; } /** * @param clazzBinName com.jogamp.common.util.cache.TempJarCache - * @param cl + * @param cl ClassLoader to locate the JarFile * @return jar:file:/usr/local/projects/JOGL/gluegen/build-x86_64/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class - * @throws IOException + * @throws IOException if the jar file could not been found by the ClassLoader */ public static URL getClassURL(String clazzBinName, ClassLoader cl) throws IOException { - return cl.getResource(getClassFileName(clazzBinName)); + final URL url = cl.getResource(getClassFileName(clazzBinName)); + if(null == url) { + throw new IOException("Cannot not find: "+clazzBinName); + } + return url; } /** diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java index 85a10ce..84ec59d 100644 --- a/src/java/com/jogamp/common/util/JarUtil.java +++ b/src/java/com/jogamp/common/util/JarUtil.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.JarURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.security.cert.Certificate; @@ -81,125 +82,161 @@ public class JarUtil { * </p> * * @param clazzBinName "com.jogamp.common.GlueGenVersion" - * @param cl + * @param cl ClassLoader to locate the JarFile * @return "jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class" - * @throws IllegalArgumentException if the URL doesn't match the expected formatting - * @throws IOException + * @throws IllegalArgumentException if the URL doesn't match the expected formatting or null arguments + * @throws IOException if the class's Jar file could not been found by the ClassLoader * @see {@link IOUtil#getClassURL(String, ClassLoader)} */ public static URL getJarURL(String clazzBinName, ClassLoader cl) throws IllegalArgumentException, IOException { - URL url = IOUtil.getClassURL(clazzBinName, cl); - if(null != url) { - String urlS = url.toExternalForm(); - if(DEBUG) { - System.out.println("getJarURL "+url+", extForm: "+urlS); - } - if(!urlS.startsWith("jar:")) { - throw new IllegalArgumentException("JAR URL doesn't start with 'jar:', got <"+urlS+">"); - } + if(null == clazzBinName || null == cl) { + throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl); + } + final URL url = IOUtil.getClassURL(clazzBinName, cl); + // test name .. + final String urlS = url.toExternalForm(); + if(DEBUG) { + System.out.println("getJarURL "+url+", extForm: "+urlS); + } + if(!urlS.startsWith("jar:")) { + throw new IllegalArgumentException("JAR URL doesn't start with 'jar:', got <"+urlS+">"); } return url; } /** - * The Class's <code>"com.jogamp.common.GlueGenVersion"</code> - * URL <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code> + * The Class's Jar URL <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code> * Jar basename <code>gluegen-rt.jar</code> will be returned. * <p> * <i>sub_protocol</i> may be "file", "http", etc.. * </p> * - * @param clazzBinName "com.jogamp.common.GlueGenVersion" + * @param classJarURL as retrieved w/ {@link #getJarURL(String, ClassLoader) getJarURL("com.jogamp.common.GlueGenVersion", cl)}, + * i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code> + * @return <code>gluegen-rt.jar</code> + * @throws IllegalArgumentException if the URL doesn't match the expected formatting or is null + * @see {@link IOUtil#getClassURL(String, ClassLoader)} + */ + public static String getJarBasename(URL classJarURL) throws IllegalArgumentException { + if(null == classJarURL) { + throw new IllegalArgumentException("URL is null"); + } + String urlS = classJarURL.toExternalForm(); + urlS = urlS.substring(4, urlS.length()); // exclude 'jar:' + + // from + // file:/some/path/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class + // to + // file:/some/path/gluegen-rt.jar + int idx = urlS.lastIndexOf('!'); + if (0 <= idx) { + urlS = urlS.substring(0, idx); // exclude '!/' + } else { + throw new IllegalArgumentException("JAR URL does not contain jar url terminator '!', in <"+classJarURL.toExternalForm()+">, got <"+urlS+">"); + } + + // from + // file:/some/path/gluegen-rt.jar + // to + // gluegen-rt.jar + idx = urlS.lastIndexOf('/'); + if(0 > idx) { + // no abs-path, check for protocol terminator ':' + idx = urlS.lastIndexOf(':'); + if(0 > idx) { + throw new IllegalArgumentException("JAR URL does not contain protocol terminator ':', in <"+classJarURL.toExternalForm()+">, got <"+urlS+">"); + } + } + urlS = urlS.substring(idx+1); // just the jar name + + if(0 >= urlS.lastIndexOf(".jar")) { + throw new IllegalArgumentException("No Jar name in <"+classJarURL.toExternalForm()+">, got <"+urlS+">"); + } + if(DEBUG) { + System.out.println("getJarName res: "+urlS); + } + return urlS; + } + + /** + * The Class's <code>com.jogamp.common.GlueGenVersion</code> + * URL <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code> + * Jar basename <code>gluegen-rt.jar</code> will be returned. + * <p> + * <i>sub_protocol</i> may be "file", "http", etc.. + * </p> + * + * @param clazzBinName <code>com.jogamp.common.GlueGenVersion</code> * @param cl - * @return "gluegen-rt.jar" + * @return <code>gluegen-rt.jar</code> * @throws IllegalArgumentException if the URL doesn't match the expected formatting - * @throws IOException + * @throws IOException if the class's Jar file could not been found by the ClassLoader * @see {@link IOUtil#getClassURL(String, ClassLoader)} */ public static String getJarBasename(String clazzBinName, ClassLoader cl) throws IllegalArgumentException, IOException { - URL url = getJarURL(clazzBinName, cl); - if(null != url) { - String urlS = url.toExternalForm(); - urlS = urlS.substring(4, urlS.length()); // exclude 'jar:' - - // from - // file:/some/path/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class - // to - // file:/some/path/gluegen-rt.jar - int idx = urlS.lastIndexOf('!'); - if (0 <= idx) { - urlS = urlS.substring(0, idx); // exclude '!/' - } else { - throw new IllegalArgumentException("JAR URL does not contain jar url terminator '!', in <"+url.toExternalForm()+">, got <"+urlS+">"); - } - - // from - // file:/some/path/gluegen-rt.jar - // to - // gluegen-rt.jar - idx = urlS.lastIndexOf('/'); - if(0 > idx) { - // no abs-path, check for protocol terminator ':' - idx = urlS.lastIndexOf(':'); - if(0 > idx) { - throw new IllegalArgumentException("JAR URL does not contain protocol terminator ':', in <"+url.toExternalForm()+">, got <"+urlS+">"); - } - } - urlS = urlS.substring(idx+1); // just the jar name - - if(0 >= urlS.lastIndexOf(".jar")) { - throw new IllegalArgumentException("No Jar name in <"+url.toExternalForm()+">, got <"+urlS+">"); - } - if(DEBUG) { - System.out.println("getJarName res: "+urlS); - } - return urlS; + return getJarBasename(getJarURL(clazzBinName, cl)); + } + + /** + * The Class's Jar URL <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code> + * Jar file's sub URL <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code> will be returned. + * <p> + * <i>sub_protocol</i> may be "file", "http", etc.. + * </p> + * + * @param classJarURL as retrieved w/ {@link #getJarURL(String, ClassLoader) getJarURL("com.jogamp.common.GlueGenVersion", cl)}, + * i.e. <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code> + * @param cl + * @return <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code> + * @throws IllegalArgumentException if the URL doesn't match the expected formatting or is null + * @throws MalformedURLException if the computed URL specifies an unknown protocol + * @see {@link IOUtil#getClassURL(String, ClassLoader)} + */ + public static URL getJarSubURL(URL classJarURL) throws IllegalArgumentException, MalformedURLException { + if(null == classJarURL) { + throw new IllegalArgumentException("URL is null"); } - return null; + String urlS = classJarURL.toExternalForm(); + urlS = urlS.substring(4, urlS.length()); // exclude 'jar:' + + // from + // file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class + // to + // file:/some/path/gluegen-rt.jar + int idx = urlS.lastIndexOf('!'); + if (0 <= idx) { + urlS = urlS.substring(0, idx); // exclude '!/' + } else { + throw new IllegalArgumentException("JAR URL does not contain jar url terminator '!', url <"+urlS+">"); + } + + if(0 >= urlS.lastIndexOf(".jar")) { + throw new IllegalArgumentException("No Jar name in <"+classJarURL.toExternalForm()+">, got <"+urlS+">"); + } + if(DEBUG) { + System.out.println("getJarSubURL res: "+urlS); + } + return new URL(urlS); } /** - * The Class's <code>"com.jogamp.common.GlueGenVersion"</code> - * URL <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code> + * The Class's <code>com.jogamp.common.GlueGenVersion</code> + * URL <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code> * Jar file's sub URL <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code> will be returned. * <p> * <i>sub_protocol</i> may be "file", "http", etc.. * </p> * - * @param clazzBinName "com.jogamp.common.GlueGenVersion" + * @param clazzBinName <code>com.jogamp.common.GlueGenVersion</code> * @param cl - * @return "<i>sub_protocol</i>:/some/path/gluegen-rt.jar" + * @return <code><i>sub_protocol</i>:/some/path/gluegen-rt.jar</code> * @throws IllegalArgumentException if the URL doesn't match the expected formatting - * @throws IOException + * @throws IOException if the class's Jar file could not been found by the ClassLoader * @see {@link IOUtil#getClassURL(String, ClassLoader)} */ public static URL getJarSubURL(String clazzBinName, ClassLoader cl) throws IllegalArgumentException, IOException { - URL url = getJarURL(clazzBinName, cl); - if(null != url) { - String urlS = url.toExternalForm(); - urlS = urlS.substring(4, urlS.length()); // exclude 'jar:' - - // from - // file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class - // to - // file:/some/path/gluegen-rt.jar - int idx = urlS.lastIndexOf('!'); - if (0 <= idx) { - urlS = urlS.substring(0, idx); // exclude '!/' - } else { - throw new IllegalArgumentException("JAR URL does not contain jar url terminator '!', url <"+urlS+">"); - } - - if(0 >= urlS.lastIndexOf(".jar")) { - throw new IllegalArgumentException("No Jar name in <"+url.toExternalForm()+">, got <"+urlS+">"); - } - if(DEBUG) { - System.out.println("getJarSubURL res: "+urlS); - } - return new URL(urlS); - } - return null; + return getJarSubURL(getJarURL(clazzBinName, cl)); } /** @@ -213,20 +250,20 @@ public class JarUtil { * @param clazzBinName "com.jogamp.common.GlueGenVersion" * @param cl * @return "jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/" - * @throws IllegalArgumentException if the URL doesn't match the expected formatting - * @throws IOException + * @throws IllegalArgumentException if the URL doesn't match the expected formatting or null arguments + * @throws IOException if the class's Jar file could not been found by the ClassLoader * @see {@link IOUtil#getClassURL(String, ClassLoader)} */ public static URL getJarFileURL(String clazzBinName, ClassLoader cl) throws IllegalArgumentException, IOException { + if(null == clazzBinName || null == cl) { + throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl); + } URL url = getJarSubURL(clazzBinName, cl); - if(null != url) { - url = new URL("jar:"+url.toExternalForm()+"!/"); - if(DEBUG) { - System.out.println("getJarFileURL res: "+url); - } - return url; + url = new URL("jar:"+url.toExternalForm()+"!/"); + if(DEBUG) { + System.out.println("getJarFileURL res: "+url); } - return null; + return url; } /** @@ -238,10 +275,13 @@ public class JarUtil { * * @param aURL "<i>protocol</i>:/some/path/gluegen-rt.jar" * @return "<i>protocol</i>:/some/path/" - * @throws IllegalArgumentException if the URL doesn't match the expected formatting - * @throws IOException + * @throws IllegalArgumentException if the URL doesn't match the expected formatting, or is null + * @throws MalformedURLException */ - public static URL getURLDirname(URL aURL) throws IllegalArgumentException, IOException { + public static URL getURLDirname(URL aURL) throws IllegalArgumentException, MalformedURLException { + if(null == aURL) { + throw new IllegalArgumentException("URL is null"); + } String urlS = aURL.toExternalForm(); if(DEBUG) { System.out.println("getURLDirname "+aURL+", extForm: "+urlS); @@ -270,11 +310,12 @@ public class JarUtil { * @param baseUrl file:/some/path/ * @param jarFileName gluegen-rt.jar * @return jar:file:/some/path/gluegen-rt.jar!/ - * @throws IOException + * @throws MalformedURLException + * @throws IllegalArgumentException null arguments */ - public static URL getJarFileURL(URL baseUrl, String jarFileName) throws IOException { - if(null == jarFileName) { - throw new IllegalArgumentException("jarFileName is null"); + public static URL getJarFileURL(URL baseUrl, String jarFileName) throws IOException, MalformedURLException { + if(null == baseUrl || null == jarFileName) { + throw new IllegalArgumentException("null arguments: baseUrl "+baseUrl+", jarFileName "+jarFileName); } return new URL("jar:"+baseUrl.toExternalForm()+jarFileName+"!/"); } @@ -282,9 +323,10 @@ public class JarUtil { /** * @param jarSubUrl file:/some/path/gluegen-rt.jar * @return jar:file:/some/path/gluegen-rt.jar!/ - * @throws IOException + * @throws MalformedURLException + * @throws IllegalArgumentException null arguments */ - public static URL getJarFileURL(URL jarSubUrl) throws IOException { + public static URL getJarFileURL(URL jarSubUrl) throws MalformedURLException, IllegalArgumentException { if(null == jarSubUrl) { throw new IllegalArgumentException("jarSubUrl is null"); } @@ -295,9 +337,10 @@ public class JarUtil { * @param jarFileURL jar:file:/some/path/gluegen-rt.jar!/ * @param jarEntry com/jogamp/common/GlueGenVersion.class * @return jar:file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class - * @throws IOException + * @throws MalformedURLException + * @throws IllegalArgumentException null arguments */ - public static URL getJarEntryURL(URL jarFileURL, String jarEntry) throws IOException { + public static URL getJarEntryURL(URL jarFileURL, String jarEntry) throws MalformedURLException, IllegalArgumentException { if(null == jarEntry) { throw new IllegalArgumentException("jarEntry is null"); } @@ -308,33 +351,35 @@ public class JarUtil { * @param clazzBinName com.jogamp.common.util.cache.TempJarCache * @param cl domain * @return JarFile containing the named class within the given ClassLoader - * @throws IOException + * @throws IOException if the class's Jar file could not been found by the ClassLoader + * @throws IllegalArgumentException null arguments * @see {@link #getJarFileURL(String, ClassLoader)} */ - public static JarFile getJarFile(String clazzBinName, ClassLoader cl) throws IOException { - return getJarFile(getJarFileURL(clazzBinName, cl), cl); + public static JarFile getJarFile(String clazzBinName, ClassLoader cl) throws IOException, IllegalArgumentException { + return getJarFile(getJarFileURL(clazzBinName, cl)); } /** * @param jarFileURL jar:file:/some/path/gluegen-rt.jar!/ - * @param cl domain * @return JarFile as named by URL within the given ClassLoader - * @throws IOException + * @throws IllegalArgumentException null arguments + * @throws IOException if the Jar file could not been found */ - public static JarFile getJarFile(URL jarFileUrl, ClassLoader cl) throws IOException { + public static JarFile getJarFile(URL jarFileUrl) throws IOException, IllegalArgumentException { + if(null == jarFileUrl) { + throw new IllegalArgumentException("null jarFileUrl"); + } if(DEBUG) { System.out.println("getJarFile: "+jarFileUrl); } - if(null != jarFileUrl) { - URLConnection urlc = jarFileUrl.openConnection(); - if(urlc instanceof JarURLConnection) { - JarURLConnection jarConnection = (JarURLConnection)jarFileUrl.openConnection(); - JarFile jarFile = jarConnection.getJarFile(); - if(DEBUG) { - System.out.println("getJarFile res: "+jarFile.getName()); - } - return jarFile; - } + URLConnection urlc = jarFileUrl.openConnection(); + if(urlc instanceof JarURLConnection) { + JarURLConnection jarConnection = (JarURLConnection)jarFileUrl.openConnection(); + JarFile jarFile = jarConnection.getJarFile(); + if(DEBUG) { + System.out.println("getJarFile res: "+jarFile.getName()); + } + return jarFile; } if(DEBUG) { System.out.println("getJarFile res: NULL"); diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java index e4a77fe..6f24c68 100644 --- a/src/java/com/jogamp/common/util/cache/TempJarCache.java +++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java @@ -39,9 +39,7 @@ import java.net.URL; import java.security.cert.Certificate; import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -62,10 +60,24 @@ public class TempJarCache { // unpacked library file in nativeTmpDir. private static Map<String, String> nativeLibMap; - // Set of native jar files added - private static Set<URL> nativeLibJars; - private static Set<URL> classFileJars; - private static Set<URL> resourceFileJars; + public enum LoadState { + LOOKED_UP, LOADED; + + public boolean compliesWith(LoadState o2) { + return null != o2 ? compareTo(o2) >= 0 : false; + } + } + private static boolean testLoadState(LoadState has, LoadState exp) { + if(null == has) { + return null == exp; + } + return has.compliesWith(exp); + } + + // Set of jar files added + private static Map<URL, LoadState> nativeLibJars; + private static Map<URL, LoadState> classFileJars; + private static Map<URL, LoadState> resourceFileJars; private static TempFileCache tmpFileCache; @@ -92,9 +104,9 @@ public class TempJarCache { if(!staticInitError) { // Initialize the collections of resources nativeLibMap = new HashMap<String, String>(); - nativeLibJars = new HashSet<URL>(); - classFileJars = new HashSet<URL>(); - resourceFileJars = new HashSet<URL>(); + nativeLibJars = new HashMap<URL, LoadState>(); + classFileJars = new HashMap<URL, LoadState>(); + resourceFileJars = new HashMap<URL, LoadState>(); } if(DEBUG) { System.err.println("TempJarCache.initSingleton(): ok "+(false==staticInitError)+", "+ tmpFileCache.getTempDir()); @@ -163,28 +175,28 @@ public class TempJarCache { return tmpFileCache; } - public static boolean containsNativeLibs(URL jarURL) throws IOException { + public synchronized static boolean checkNativeLibs(URL jarURL, LoadState exp) throws IOException { checkInitialized(); if(null == jarURL) { throw new IllegalArgumentException("jarURL is null"); } - return nativeLibJars.contains(jarURL); + return testLoadState(nativeLibJars.get(jarURL), exp); } - public static boolean containsClasses(URL jarURL) throws IOException { + public synchronized static boolean checkClasses(URL jarURL, LoadState exp) throws IOException { checkInitialized(); if(null == jarURL) { throw new IllegalArgumentException("jarURL is null"); } - return classFileJars.contains(jarURL); + return testLoadState(classFileJars.get(jarURL), exp); } - public static boolean containsResources(URL jarURL) throws IOException { + public synchronized static boolean checkResources(URL jarURL, LoadState exp) throws IOException { checkInitialized(); if(null == jarURL) { throw new IllegalArgumentException("jarURL is null"); } - return resourceFileJars.contains(jarURL); + return testLoadState(resourceFileJars.get(jarURL), exp); } /** @@ -192,20 +204,23 @@ public class TempJarCache { * * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarURL - * - * @throws IOException + * @throws IOException if the <code>jarURL</code> could not be loaded or a previous load attempt failed * @throws SecurityException */ - public static final void addNativeLibs(Class<?> certClass, URL jarURL, ClassLoader cl) throws IOException, SecurityException { - if(!containsNativeLibs(jarURL)) { - final JarFile jarFile = JarUtil.getJarFile(jarURL, cl); + public synchronized static final void addNativeLibs(Class<?> certClass, URL jarURL) throws IOException, SecurityException { + final LoadState nativeLibJarsLS = nativeLibJars.get(jarURL); + if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) ) { + nativeLibJars.put(jarURL, LoadState.LOOKED_UP); + final JarFile jarFile = JarUtil.getJarFile(jarURL); if(DEBUG) { System.err.println("TempJarCache: addNativeLibs: "+jarURL+": nativeJar "+jarFile.getName()); } validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, true, false, false); - nativeLibJars.add(jarURL); + nativeLibJars.put(jarURL, LoadState.LOADED); + } else if( !testLoadState(nativeLibJarsLS, LoadState.LOADED) ) { + throw new IOException("TempJarCache: addNativeLibs: "+jarURL+", previous load attempt failed"); } } @@ -217,20 +232,23 @@ public class TempJarCache { * * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarFile - * - * @throws IOException + * @throws IOException if the <code>jarURL</code> could not be loaded or a previous load attempt failed * @throws SecurityException */ - public static final void addClasses(Class<?> certClass, URL jarURL, ClassLoader cl) throws IOException, SecurityException { - if(!containsClasses(jarURL)) { - final JarFile jarFile = JarUtil.getJarFile(jarURL, cl); + public synchronized static final void addClasses(Class<?> certClass, URL jarURL) throws IOException, SecurityException { + final LoadState classFileJarsLS = classFileJars.get(jarURL); + if( !testLoadState(classFileJarsLS, LoadState.LOOKED_UP) ) { + classFileJars.put(jarURL, LoadState.LOOKED_UP); + final JarFile jarFile = JarUtil.getJarFile(jarURL); if(DEBUG) { System.err.println("TempJarCache: addClasses: "+jarURL+": nativeJar "+jarFile.getName()); } validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile, false, true, false); - classFileJars.add(jarURL); + classFileJars.put(jarURL, LoadState.LOADED); + } else if( !testLoadState(classFileJarsLS, LoadState.LOADED) ) { + throw new IOException("TempJarCache: addClasses: "+jarURL+", previous load attempt failed"); } } @@ -239,21 +257,24 @@ public class TempJarCache { * * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarFile - * * @return - * @throws IOException + * @throws IOException if the <code>jarURL</code> could not be loaded or a previous load attempt failed * @throws SecurityException */ - public static final void addResources(Class<?> certClass, URL jarURL, ClassLoader cl) throws IOException, SecurityException { - if(!containsResources(jarURL)) { - final JarFile jarFile = JarUtil.getJarFile(jarURL, cl); + public synchronized static final void addResources(Class<?> certClass, URL jarURL) throws IOException, SecurityException { + final LoadState resourceFileJarsLS = resourceFileJars.get(jarURL); + if( !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP) ) { + resourceFileJars.put(jarURL, LoadState.LOOKED_UP); + final JarFile jarFile = JarUtil.getJarFile(jarURL); if(DEBUG) { System.err.println("TempJarCache: addResources: "+jarURL+": nativeJar "+jarFile.getName()); } validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile, false, false, true); - resourceFileJars.add(jarURL); + resourceFileJars.put(jarURL, LoadState.LOADED); + } else if( !testLoadState(resourceFileJarsLS, LoadState.LOADED) ) { + throw new IOException("TempJarCache: addResources: "+jarURL+", previous load attempt failed"); } } @@ -266,41 +287,62 @@ public class TempJarCache { * * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarFile - * - * @throws IOException + * @throws IOException if the <code>jarURL</code> could not be loaded or a previous load attempt failed * @throws SecurityException */ - public static final void addAll(Class<?> certClass, URL jarURL, ClassLoader cl) throws IOException, SecurityException { + public synchronized static final void addAll(Class<?> certClass, URL jarURL) throws IOException, SecurityException { checkInitialized(); if(null == jarURL) { throw new IllegalArgumentException("jarURL is null"); } - if(!nativeLibJars.contains(jarURL) || - !classFileJars.contains(jarURL) || - !resourceFileJars.contains(jarURL)) { - final JarFile jarFile = JarUtil.getJarFile(jarURL, cl); + final LoadState nativeLibJarsLS = nativeLibJars.get(jarURL); + final LoadState classFileJarsLS = classFileJars.get(jarURL); + final LoadState resourceFileJarsLS = resourceFileJars.get(jarURL); + if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) || + !testLoadState(classFileJarsLS, LoadState.LOOKED_UP) || + !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP) ) { + + final boolean extractNativeLibraries = !testLoadState(nativeLibJarsLS, LoadState.LOADED); + final boolean extractClassFiles = !testLoadState(classFileJarsLS, LoadState.LOADED); + final boolean extractOtherFiles = !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP); + + // mark looked-up (those who are not loaded) + if(extractNativeLibraries) { + nativeLibJars.put(jarURL, LoadState.LOOKED_UP); + } + if(extractClassFiles) { + classFileJars.put(jarURL, LoadState.LOOKED_UP); + } + if(extractOtherFiles) { + resourceFileJars.put(jarURL, LoadState.LOOKED_UP); + } + + final JarFile jarFile = JarUtil.getJarFile(jarURL); if(DEBUG) { System.err.println("TempJarCache: addAll: "+jarURL+": nativeJar "+jarFile.getName()); } - final boolean extractNativeLibraries = !nativeLibJars.contains(jarURL); - final boolean extractClassFiles = !classFileJars.contains(jarURL); - final boolean extractOtherFiles = !resourceFileJars.contains(jarURL); validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, extractNativeLibraries, extractClassFiles, extractOtherFiles); + + // mark loaded (those were just loaded) if(extractNativeLibraries) { - nativeLibJars.add(jarURL); + nativeLibJars.put(jarURL, LoadState.LOADED); } if(extractClassFiles) { - classFileJars.add(jarURL); + classFileJars.put(jarURL, LoadState.LOADED); } if(extractOtherFiles) { - resourceFileJars.add(jarURL); + resourceFileJars.put(jarURL, LoadState.LOADED); } + } else if( !testLoadState(nativeLibJarsLS, LoadState.LOADED) || + !testLoadState(classFileJarsLS, LoadState.LOADED) || + !testLoadState(resourceFileJarsLS, LoadState.LOADED) ) { + throw new IOException("TempJarCache: addAll: "+jarURL+", previous load attempt failed"); } } - public static final String findLibrary(String libName) { + public synchronized static final String findLibrary(String libName) { checkInitialized(); // try with mapped library basename first String path = nativeLibMap.get(libName); @@ -331,7 +373,7 @@ public class TempJarCache { return null; } */ - public static final String findResource(String name) { + public synchronized static final String findResource(String name) { checkInitialized(); final File f = new File(tmpFileCache.getTempDir(), name); if(f.exists()) { @@ -340,7 +382,7 @@ public class TempJarCache { return null; } - public static final URL getResource(String name) throws MalformedURLException { + public synchronized static final URL getResource(String name) throws MalformedURLException { checkInitialized(); final File f = new File(tmpFileCache.getTempDir(), name); if(f.exists()) { @@ -360,13 +402,20 @@ public class TempJarCache { * @throws IOException * @throws SecurityException */ - public static final void bootstrapNativeLib(Class<?> certClass, String libBaseName, URL jarURL, ClassLoader cl) + public synchronized static final void bootstrapNativeLib(Class<?> certClass, String libBaseName, URL jarURL) throws IOException, SecurityException { checkInitialized(); - if(!nativeLibJars.contains(jarURL) && !nativeLibMap.containsKey(libBaseName) ) { - final JarFile jarFile = JarUtil.getJarFile(jarURL, cl); + boolean ok = false; + int countEntries = 0; + final LoadState nativeLibJarsLS = nativeLibJars.get(jarURL); + if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) && !nativeLibMap.containsKey(libBaseName) ) { if(DEBUG) { - System.err.println("TempJarCache: bootstrapNativeLib: "+jarURL+": nativeJar "+jarFile.getName()+" - libBaseName: "+libBaseName); + System.err.println("TempJarCache: bootstrapNativeLib(certClass: "+certClass+", libBaseName "+libBaseName+", jarURL "+jarURL+")"); + } + nativeLibJars.put(jarURL, LoadState.LOOKED_UP); + final JarFile jarFile = JarUtil.getJarFile(jarURL); + if(DEBUG) { + System.err.println("TempJarCache: bootstrapNativeLib: nativeJar "+jarFile.getName()); } validateCertificates(certClass, jarFile); final Enumeration<JarEntry> entries = jarFile.entries(); @@ -385,18 +434,27 @@ public class TempJarCache { try { final byte[] buf = new byte[ 2048 ]; while (true) { - int count; - if ((count = in.read(buf)) == -1) { break; } - out.write(buf, 0, count); - numBytes += count; + int countBytes; + if ((countBytes = in.read(buf)) == -1) { break; } + out.write(buf, 0, countBytes); + numBytes += countBytes; } } finally { in.close(); out.close(); } if (numBytes>0) { nativeLibMap.put(libBaseName, destFile.getAbsolutePath()); - nativeLibJars.add(jarURL); + nativeLibJars.put(jarURL, LoadState.LOADED); + ok = true; + countEntries++; } } } + } else if( testLoadState(nativeLibJarsLS, LoadState.LOADED) ) { + ok = true; // already loaded + } else { + throw new IOException("TempJarCache: bootstrapNativeLib: "+jarURL+", previous load attempt failed"); + } + if(DEBUG) { + System.err.println("TempJarCache: bootstrapNativeLib() done, count "+countEntries+", ok "+ok); } } diff --git a/src/junit/com/jogamp/common/util/TestJarUtil.java b/src/junit/com/jogamp/common/util/TestJarUtil.java index ea6cebf..cb1cc45 100644 --- a/src/junit/com/jogamp/common/util/TestJarUtil.java +++ b/src/junit/com/jogamp/common/util/TestJarUtil.java @@ -137,7 +137,7 @@ public class TestJarUtil extends JunitTracer { final ClassLoader rootCL = this.getClass().getClassLoader(); // Get containing JAR file "TestJarsInJar.jar" and add it to the TempJarCache - TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURL("ClassInJar0", rootCL), rootCL); + TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURL("ClassInJar0", rootCL)); // Fetch and load the contained "ClassInJar1.jar" final URL ClassInJar1_jarFileURL = JarUtil.getJarFileURL(TempJarCache.getResource("ClassInJar1.jar")); @@ -158,7 +158,7 @@ public class TestJarUtil extends JunitTracer { final ClassLoader rootCL = this.getClass().getClassLoader(); // Get containing JAR file "TestJarsInJar.jar" and add it to the TempJarCache - TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURL("ClassInJar0", rootCL), rootCL); + TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURL("ClassInJar0", rootCL)); // Fetch and load the contained "ClassInJar1.jar" final URL ClassInJar2_jarFileURL = JarUtil.getJarFileURL(TempJarCache.getResource("sub/ClassInJar2.jar")); diff --git a/src/junit/com/jogamp/common/util/TestTempJarCache.java b/src/junit/com/jogamp/common/util/TestTempJarCache.java index d97894c..5467e4c 100644 --- a/src/junit/com/jogamp/common/util/TestTempJarCache.java +++ b/src/junit/com/jogamp/common/util/TestTempJarCache.java @@ -172,7 +172,7 @@ public class TestTempJarCache extends JunitTracer { if(AndroidVersion.isAvailable) { System.err.println("n/a on Android"); return; } final ClassLoader cl = getClass().getClassLoader(); - TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURL(GlueGenVersion.class.getName(), cl), cl); + TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFileURL(GlueGenVersion.class.getName(), cl)); File f0 = new File(TempJarCache.getTempFileCache().getTempDir(), "META-INF/MANIFEST.MF"); Assert.assertTrue(f0.exists()); @@ -201,7 +201,7 @@ public class TestTempJarCache extends JunitTracer { URL nativeJarURL = JarUtil.getJarFileURL(jarUrlRoot, nativeJarName); - TempJarCache.addNativeLibs(TempJarCache.class, nativeJarURL, cl); + TempJarCache.addNativeLibs(TempJarCache.class, nativeJarURL); String libFullPath = TempJarCache.findLibrary(libBaseName); Assert.assertNotNull(libFullPath); Assert.assertEquals(libBaseName, NativeLibrary.isValidNativeLibraryName(libFullPath, true)); |