diff options
Diffstat (limited to 'src/java/com/jogamp')
-rw-r--r-- | src/java/com/jogamp/common/jvm/JNILibLoaderBase.java | 140 | ||||
-rw-r--r-- | src/java/com/jogamp/common/net/URIQueryProps.java | 137 | ||||
-rw-r--r-- | src/java/com/jogamp/common/os/Platform.java | 14 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/IOUtil.java | 119 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/JarUtil.java | 83 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/PropertyAccess.java | 9 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/VersionUtil.java | 4 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/cache/TempJarCache.java | 101 |
8 files changed, 434 insertions, 173 deletions
diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java index 2023162..67cc05c 100644 --- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java +++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java @@ -44,6 +44,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; @@ -147,24 +148,56 @@ public class JNILibLoaderBase { loaderAction = action; } - /* pp */ static final boolean addNativeJarLibsImpl(Class<?> classFromJavaJar, URI classJarURI, String nativeJarBasename, StringBuilder msg) + /** + * + * @param classFromJavaJar + * @param classJarURI + * @param jarBasename jar basename w/ suffix + * @param nativeJarBasename native jar basename w/ suffix + * @param msg + * @return + * @throws IOException + * @throws SecurityException + * @throws URISyntaxException + */ + /* pp */ static final boolean addNativeJarLibsImpl(Class<?> classFromJavaJar, URI classJarURI, String jarBasename, String nativeJarBasename, StringBuilder msg) throws IOException, SecurityException, URISyntaxException { msg.setLength(0); // reset - msg.append("addNativeJarLibsImpl(classFromJavaJar ").append(classFromJavaJar).append(", classJarURI ").append(classJarURI).append(", nativeJarBaseName ").append(nativeJarBasename).append("): "); + msg.append("addNativeJarLibsImpl(classFromJavaJar ").append(classFromJavaJar).append(", classJarURI ").append(classJarURI) + .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 URI jarUriRoot = IOUtil.getDirname( JarUtil.getJarSubURI( classJarURI ) ); - msg.append(" + ").append(jarUriRoot); - final URI nativeJarURI = JarUtil.getJarFileURI(jarUriRoot, nativeJarName); - msg.append(" -> ").append(nativeJarURI); - if(DEBUG) { - System.err.println(msg.toString()); + if(TempJarCache.isInitialized()) { + final URI jarSubURI = JarUtil.getJarSubURI( classJarURI ); + if(null == jarSubURI) { + throw new IllegalArgumentException("JarSubURI is null of: "+classJarURI); + } + final String jarUriRoot_s = IOUtil.getURIDirname( jarSubURI.toString() ); + msg.append("[ ").append(jarSubURI.toString()).append(" -> ").append(jarUriRoot_s).append(" ] + "); + + final String nativeLibraryPath = "natives/"+PlatformPropsImpl.os_and_arch+"/"; + final ClassLoader cl = classFromJavaJar.getClassLoader(); + final URL nativeLibraryURI = cl.getResource(nativeLibraryPath); + if( null != nativeLibraryURI ) { + // We probably have one big-fat jar file, containing java classes + // and all native platform libraries under 'natives/os.and.arch'! + final URI nativeJarURI = JarUtil.getJarFileURI(jarUriRoot_s+jarBasename); + if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) { + ok = true; + msg.append(jarBasename).append(" -> fat: ").append(nativeJarURI); + } } - TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI); - ok = true; + if( !ok ) { + // We assume one slim native jar file per 'os.and.arch'! + final URI nativeJarURI = JarUtil.getJarFileURI(jarUriRoot_s+nativeJarBasename); + msg.append(nativeJarBasename).append(" -> slim: ").append(nativeJarURI); + ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, null /* nativeLibraryPath */); + } + } else { + msg.append("TempJarCache n/a"); + } + if(DEBUG) { + System.err.println(msg.toString()+" - OK "+ok); } return ok; } @@ -193,7 +226,8 @@ public class JNILibLoaderBase { final StringBuilder msg = new StringBuilder(); try { final URI classJarURI = JarUtil.getJarURI(classFromJavaJar.getName(), classFromJavaJar.getClassLoader()); - return addNativeJarLibsImpl(classFromJavaJar, classJarURI, nativeJarBasename, msg); + final String jarName = JarUtil.getJarBasename(classJarURI); + return addNativeJarLibsImpl(classFromJavaJar, classJarURI, jarName, nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar", msg); } catch (Exception e0) { // IllegalArgumentException, IOException System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while "+msg.toString()); @@ -208,6 +242,30 @@ public class JNILibLoaderBase { } /** + * Loads and adds a JAR file's native library to the TempJarCache, + * calling {@link JNILibLoaderBase#addNativeJarLibs(Class[], String, String[])} + * with default JOGL deployment configuration: + * <pre> + return JNILibLoaderBase.addNativeJarLibs(classesFromJavaJars, "-all", new String[] { "-noawt", "-mobile", "-core", "-android" } ); + * </pre> + * If <code>Class1.class</code> is contained in a JAR file which name includes <code>singleJarMarker</code> <i>-all</i>, + * implementation will attempt to resolve the native JAR file as follows: + * <ul> + * <li><i>ClassJar-all</i>[-noawt,-mobile,-android]?.jar to <i>ClassJar-all</i>-natives-<i>os.and.arch</i>.jar</li> + * </ul> + * Otherwise the native JAR files will be resolved for each class's JAR file: + * <ul> + * <li><i>ClassJar1</i>[-noawt,-mobile,-core,-android]?.jar to <i>ClassJar1</i>-natives-<i>os.and.arch</i>.jar</li> + * <li><i>ClassJar2</i>[-noawt,-mobile,-core,-android]?.jar to <i>ClassJar2</i>-natives-<i>os.and.arch</i>.jar</li> + * <li>..</li> + * </ul> + */ + public static final boolean addNativeJarLibsJoglCfg(final Class<?>[] classesFromJavaJars) { + return addNativeJarLibs(classesFromJavaJars, "-all", joglDeployCfg); + } + private static final String[] joglDeployCfg = new String[] { "-noawt", "-mobile", "-core", "-android" }; + + /** * Loads and adds a JAR file's native library to the TempJarCache.<br> * The native library JAR file's URI is derived as follows: * <ul> @@ -223,37 +281,59 @@ public class JNILibLoaderBase { * <li> [3] is it's <i>base URI</i></li> * <li> [4] is the derived native JAR filename</li> * </ul> - * - * Examples:<br> - * <br> + * <p> + * Generic description: + * <pre> + final ClassLoader cl = GLProfile.class.getClassLoader(); + final String newtFactoryClassName = "com.jogamp.newt.NewtFactory"; + final Class<?>[] classesFromJavaJars = new Class<?>[] { Class1.class, Class2.class }; + JNILibLoaderBase.addNativeJarLibs(classesFromJavaJars, "-all", new String[] { "-suff1", "-suff2" } ); + * </pre> + * If <code>Class1.class</code> is contained in a JAR file which name includes <code>singleJarMarker</code>, here <i>-all</i>, + * implementation will attempt to resolve the native JAR file as follows: + * <ul> + * <li><i>ClassJar-all</i>[-suff1,-suff2]?.jar to <i>ClassJar-all</i>-natives-<i>os.and.arch</i>.jar</li> + * </ul> + * Otherwise the native JAR files will be resolved for each class's JAR file: + * <ul> + * <li><i>Class1Jar</i>[-suff1,-suff2]?.jar to <i>Class1Jar</i>-natives-<i>os.and.arch</i>.jar</li> + * <li><i>Class2Jar</i>[-suff1,-suff2]?.jar to <i>Class2Jar</i>-natives-<i>os.and.arch</i>.jar</li> + * </ul> + * </p> + * <p> + * Examples: + * </p> + * <p> * JOCL: * <pre> - // only: jocl.jar -> jocl-natives-'os.and.arch'.jar + // only: jocl.jar -> jocl-natives-<i>os.and.arch</i>.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" } ); + // either: [jogl-all.jar, jogl-all-noawt.jar, jogl-all-mobile.jar, jogl-all-android.jar] -> jogl-all-natives-<i>os.and.arch</i>.jar + // or: nativewindow-core.jar -> nativewindow-natives-<i>os.and.arch</i>.jar, + // newt-core.jar -> newt-natives-<i>os.and.arch</i>.jar + JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { NWJNILibLoader.class, NEWTJNILibLoader.class }, "-all", new String[] { "-noawt", "-mobile", "-core", "-android" } ); * </pre> - * + * </p> + * <p> * 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) + // either: [jogl-all.jar, jogl-all-noawt.jar, jogl-all-mobile.jar, jogl-all-android.jar] -> jogl-all-natives-<i>os.and.arch</i>.jar + // or: nativewindow-core.jar -> nativewindow-natives-<i>os.and.arch</i>.jar, + // jogl-core.jar -> jogl-natives-<i>os.and.arch</i>.jar, + // (newt-core.jar -> newt-natives-<i>os.and.arch</i>.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" } ); + JNILibLoaderBase.addNativeJarLibs(classesFromJavaJars, "-all", new String[] { "-noawt", "-mobile", "-core", "-android" } ); * </pre> + * </p> * * @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 @@ -281,9 +361,9 @@ public class JNILibLoaderBase { 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); + final String nativeJarBasename = stripName(jarBasename, stripBasenameSuffixes)+"-natives-"+PlatformPropsImpl.os_and_arch+".jar"; done = null != singleJarMarker && jarBasename.indexOf(singleJarMarker) >= 0; // done if single-jar ('all' variant) - ok = JNILibLoaderBase.addNativeJarLibsImpl(classesFromJavaJars[i], classJarURI, nativeJarBasename, msg); + ok = JNILibLoaderBase.addNativeJarLibsImpl(classesFromJavaJars[i], classJarURI, jarName, nativeJarBasename, msg); if(ok) { count++; } if(DEBUG && done) { System.err.println("JNILibLoaderBase: addNativeJarLibs0: end after all-in-one JAR: "+jarBasename); diff --git a/src/java/com/jogamp/common/net/URIQueryProps.java b/src/java/com/jogamp/common/net/URIQueryProps.java new file mode 100644 index 0000000..fd91b9b --- /dev/null +++ b/src/java/com/jogamp/common/net/URIQueryProps.java @@ -0,0 +1,137 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.common.net; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Helper class to process URI's query, handled as properties. + * <p> + * The order of the URI segments (any properties) are <i>not</i> preserved. + * </p> + * <pre> + * URI: [scheme:][//authority][path][?query][#fragment] + * w/ authority: [user-info@]host[:port] + * Note: 'path' starts w/ fwd slash + * </pre> + */ +public class URIQueryProps { + private static final String QMARK = "?"; + private static final char ASSIG = '='; + private static final String EMPTY = ""; + private final String query_separator; + + private final HashMap<String, String> properties = new HashMap<String, String>(); + + private URIQueryProps(char querySeparator) { + query_separator = String.valueOf(querySeparator); + } + + public final Map<String, String> getProperties() { return properties; } + public final char getQuerySeparator() { return query_separator.charAt(0); } + + public final String appendQuery(String baseQuery) { + boolean needsSep = false; + final StringBuilder sb = new StringBuilder(); + if ( null != baseQuery ) { + if( !baseQuery.startsWith(QMARK) ) { + baseQuery = baseQuery.substring(1); + } + sb.append(baseQuery); + if( !baseQuery.endsWith(query_separator) ) { + needsSep = true; + } + } + Iterator<Entry<String, String>> entries = properties.entrySet().iterator(); + while(entries.hasNext()) { + if(needsSep) { + sb.append(query_separator); + } + final Entry<String, String> entry = entries.next(); + sb.append(entry.getKey()); + if( EMPTY != entry.getValue() ) { + sb.append(ASSIG).append(entry.getValue()); + } + needsSep = true; + } + return sb.toString(); + } + + public final URI appendQuery(URI base) throws URISyntaxException { + return new URI(base.getScheme(), + base.getRawUserInfo(), base.getHost(), base.getPort(), + base.getRawPath(), appendQuery(base.getRawQuery()), base.getRawFragment()); + } + + /** + * + * @param uri + * @param querySeparator should be either <i>;</i> or <i>&</i>, <i>;</i> is encouraged due to troubles of escaping <i>&</i>. + * @return + * @throws IllegalArgumentException if <code>querySeparator</code> is illegal, i.e. neither <i>;</i> nor <i>&</i> + */ + public static final URIQueryProps create(URI uri, char querySeparator) throws IllegalArgumentException { + if( ';' != querySeparator && '&' != querySeparator ) { + throw new IllegalArgumentException("querySeparator is invalid: "+querySeparator); + } + final URIQueryProps data = new URIQueryProps(querySeparator); + final String q = uri.getQuery(); + final int q_l = null != q ? q.length() : -1; + int q_e = -1; + while(q_e < q_l) { + int q_b = q_e + 1; // next term + q_e = q.indexOf(querySeparator, q_b); + if(0 == q_e) { + // single separator + continue; + } + if(0 > q_e) { + // end + q_e = q_l; + } + // n-part + final String part = q.substring(q_b, q_e); + final int assignment = part.indexOf(ASSIG); + if(0 < assignment) { + // assignment + final String k = part.substring(0, assignment); + final String v = part.substring(assignment+1); + data.properties.put(k, v); + } else { + // property key only + data.properties.put(part, EMPTY); + } + } + return data; + } +} diff --git a/src/java/com/jogamp/common/os/Platform.java b/src/java/com/jogamp/common/os/Platform.java index 32fc9f4..a01a4b4 100644 --- a/src/java/com/jogamp/common/os/Platform.java +++ b/src/java/com/jogamp/common/os/Platform.java @@ -33,7 +33,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.TimeUnit; -import com.jogamp.common.util.IOUtil; +import com.jogamp.common.jvm.JNILibLoaderBase; import com.jogamp.common.util.JarUtil; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.VersionNumber; @@ -192,19 +192,11 @@ public class Platform extends PlatformPropsImpl { // load GluegenRT native library if(_USE_TEMP_JAR_CACHE[0] && TempJarCache.initSingleton()) { - String nativeJarName = null; - URI jarUriRoot = null; - URI nativeJarURI = null; try { - final String jarName = JarUtil.getJarBasename( platformClassJarURI ); - final String nativeJarBasename = jarName.substring(0, jarName.indexOf(".jar")); // ".jar" already validated w/ JarUtil.getJarBasename(..) - nativeJarName = nativeJarBasename+"-natives-"+PlatformPropsImpl.os_and_arch+".jar"; - jarUriRoot = IOUtil.getDirname( JarUtil.getJarSubURI( platformClassJarURI ) ); - nativeJarURI = JarUtil.getJarFileURI(jarUriRoot, nativeJarName); - TempJarCache.bootstrapNativeLib(Platform.class, libBaseName, nativeJarURI); + JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { Platform.class }, null, null ); } catch (Exception e0) { // IllegalArgumentException, IOException - System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while TempJarCache.bootstrapNativeLib() of "+nativeJarURI+" ("+jarUriRoot+" + "+nativeJarName+")"); + System.err.println("Catched "+e0.getClass().getSimpleName()+": "+e0.getMessage()+", while JNILibLoaderBase.addNativeJarLibs(..)"); } } DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false, cl); diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index de5a8ea..242f300 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -43,6 +43,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.nio.ByteBuffer; +import java.util.regex.Pattern; import jogamp.common.Debug; import jogamp.common.os.AndroidUtils; @@ -169,7 +170,22 @@ public class IOUtil { * @throws IOException */ public static int copyStream2Stream(InputStream in, OutputStream out, int totalNumBytes) throws IOException { - final byte[] buf = new byte[Platform.getMachineDescription().pageSizeInBytes()]; + return copyStream2Stream(Platform.getMachineDescription().pageSizeInBytes(), in, out, totalNumBytes); + } + + /** + * Copy the specified input stream to the specified output stream. The total + * number of bytes written is returned. + * + * @param bufferSize the intermediate buffer size, should be {@link MachineDescription#pageSizeInBytes()} for best performance. + * @param in the source + * @param out the destination + * @param totalNumBytes informal number of expected bytes, maybe used for user feedback while processing. -1 if unknown + * @return + * @throws IOException + */ + public static int copyStream2Stream(int bufferSize, InputStream in, OutputStream out, int totalNumBytes) throws IOException { + final byte[] buf = new byte[bufferSize]; int numBytes = 0; while (true) { int count; @@ -181,7 +197,7 @@ public class IOUtil { } return numBytes; } - + /** * Copy the specified input stream to a byte array, which is being returned. */ @@ -281,7 +297,7 @@ public class IOUtil { * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code> */ public static String slashify(String path, boolean startWithSlash, boolean endWithSlash) throws URISyntaxException { - String p = path.replace('\\', '/'); // unify file seperator + String p = path.replace('\\', '/'); // unify file separator if (startWithSlash && !p.startsWith("/")) { p = "/" + p; } @@ -297,7 +313,7 @@ public class IOUtil { * @throws URISyntaxException if the resulting string does not comply w/ an RFC 2396 URI */ public static URI toURISimple(File file) throws URISyntaxException { - return new URI(FILE_SCHEME, null, slashify(file.getAbsolutePath(), true, file.isDirectory()), null); + return new URI(FILE_SCHEME, null, encodeToURI(slashify(file.getAbsolutePath(), true, file.isDirectory())), null); } /** @@ -306,7 +322,7 @@ public class IOUtil { * @throws URISyntaxException if the resulting string does not comply w/ an RFC 2396 URI */ public static URI toURISimple(String protocol, String file, boolean isDirectory) throws URISyntaxException { - return new URI(protocol, null, slashify(file, true, isDirectory), null); + return new URI(protocol, null, encodeToURI(slashify(file, true, isDirectory)), null); } /** @@ -429,7 +445,7 @@ public class IOUtil { * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null * @throws URISyntaxException */ - public static URI getDirname(URI uri) throws IllegalArgumentException, URISyntaxException { + public static URI getURIDirname(URI uri) throws IllegalArgumentException, URISyntaxException { if(null == uri) { throw new IllegalArgumentException("URI is null"); } @@ -437,6 +453,25 @@ public class IOUtil { if( DEBUG ) { System.out.println("getURIDirname "+uri+", extForm: "+uriS); } + return new URI( getURIDirname(uriS) ); + } + + /** + * The URI's <code><i>protocol</i>:/some/path/gluegen-rt.jar</code> + * parent dirname URI <code><i>protocol</i>:/some/path/</code> will be returned. + * <p> + * <i>protocol</i> may be "file", "http", etc.. + * </p> + * + * @param uri "<i>protocol</i>:/some/path/gluegen-rt.jar" (URI encoded) + * @return "<i>protocol</i>:/some/path/" + * @throws IllegalArgumentException if the URI doesn't match the expected formatting, or is null + * @throws URISyntaxException + */ + public static String getURIDirname(String uriS) throws IllegalArgumentException, URISyntaxException { + if(null == uriS) { + throw new IllegalArgumentException("uriS is null"); + } // from // file:/some/path/gluegen-rt.jar _or_ rsrc:gluegen-rt.jar // to @@ -446,7 +481,7 @@ public class IOUtil { // no abs-path, check for protocol terminator ':' idx = uriS.lastIndexOf(':'); if(0 > idx) { - throw new IllegalArgumentException("URI does not contain protocol terminator ':', in <"+uri+">"); + throw new IllegalArgumentException("URI does not contain protocol terminator ':', in <"+uriS+">"); } } uriS = uriS.substring(0, idx+1); // exclude jar name, include terminal '/' or ':' @@ -454,7 +489,7 @@ public class IOUtil { if( DEBUG ) { System.out.println("getJarURIDirname res: "+uriS); } - return new URI(uriS); + return uriS; } /** @@ -479,22 +514,40 @@ public class IOUtil { * @throws URISyntaxException */ public static URL toURL(URI uri) throws IOException, IllegalArgumentException, URISyntaxException { - final URL url; + URL url = null; final String uriSchema = uri.getScheme(); final boolean isJAR = IOUtil.JAR_SCHEME.equals(uriSchema); - final URI specificURI = isJAR ? JarUtil.getJarSubURI(uri) : uri; + final URI specificURI = isJAR ? JarUtil.getJarSubURI(uri) : uri; + int mode = 0; if( IOUtil.FILE_SCHEME.equals( specificURI.getScheme() ) ) { - final File f = new File(specificURI); - if( specificURI == uri ) { - url = new URL(IOUtil.FILE_SCHEME+IOUtil.SCHEME_SEPARATOR+f.getPath()); - // url = f.toURI().toURL(); // Doesn't work, since it uses encoded path! - } else { - final String post = isJAR ? IOUtil.JAR_SCHEME_SEPARATOR + JarUtil.getJarEntry(uri) : ""; - final String urlS = uriSchema+IOUtil.SCHEME_SEPARATOR+IOUtil.FILE_SCHEME+IOUtil.SCHEME_SEPARATOR+f.getPath()+post; - url = new URL(urlS); + File f; + try { + f = new File(specificURI); + } catch( IllegalArgumentException iae) { + if( DEBUG ) { + System.out.println("toURL: "+uri+" -> File("+specificURI+") failed: "+iae.getMessage()); + } + f = null; } - } else { + if( null != f ) { + if( specificURI == uri ) { + url = new URL(IOUtil.FILE_SCHEME+IOUtil.SCHEME_SEPARATOR+f.getPath()); + // url = f.toURI().toURL(); // Doesn't work, since it uses encoded path! + mode = 1; + } else { + final String post = isJAR ? IOUtil.JAR_SCHEME_SEPARATOR + JarUtil.getJarEntry(uri) : ""; + final String urlS = uriSchema+IOUtil.SCHEME_SEPARATOR+IOUtil.FILE_SCHEME+IOUtil.SCHEME_SEPARATOR+f.getPath()+post; + url = new URL(urlS); + mode = 2; + } + } + } + if( null == url ) { url = uri.toURL(); + mode = 3; + } + if( DEBUG ) { + System.err.println("IOUtil.toURL: "+uri+", isJar "+isJAR+": "+specificURI+" -> mode "+mode+", "+url); } return url; } @@ -675,7 +728,7 @@ public class IOUtil { * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code> */ public static URI getRelativeOf(URI baseURI, String relativePath) throws URISyntaxException { - return compose(baseURI.getScheme(), baseURI.getRawSchemeSpecificPart(), relativePath, baseURI.getRawFragment()); + return compose(baseURI.getScheme(), baseURI.getSchemeSpecificPart(), encodeToURI(relativePath), baseURI.getFragment()); } /** @@ -701,9 +754,11 @@ public class IOUtil { * </p> * * @param scheme scheme of the resulting URI - * @param schemeSpecificPart may include a query, which is separated while processing - * @param relativePath denotes a relative file to the baseLocation's parent directory + * @param schemeSpecificPart may include a query, which is separated while processing (URI encoded) + * @param relativePath denotes a relative file to the baseLocation's parent directory (URI encoded) + * @param fragment the URI fragment (URI encoded) * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code> + * @see #encodeToURI(String) */ public static URI compose(String scheme, String schemeSpecificPart, String relativePath, String fragment) throws URISyntaxException { // cut off optional query in scheme-specific-part @@ -725,6 +780,26 @@ public class IOUtil { return new URI(scheme, null == query ? schemeSpecificPart : schemeSpecificPart + "?" + query, fragment); } + private static final Pattern patternSpaceRaw = Pattern.compile(" "); + private static final Pattern patternSpaceEnc = Pattern.compile("%20"); + + /** + * Escapes characters not complying w/ RFC 2396 and the {@link URI#URI(String)} ctor. + * <ul> + * <li>SPACE -> %20</li> + * </ul> + */ + public static String encodeToURI(String s) { + return patternSpaceRaw.matcher(s).replaceAll("%20"); + } + + /** + * Reverses escaping of characters as performed via {@link #encodeToURI(String)}. + */ + public static String decodeFromURI(String s) { + return patternSpaceEnc.matcher(s).replaceAll(" "); + } + /** * Returns the connected URLConnection, or null if not url is not available */ diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java index 5604c3d..29e7dc7 100644 --- a/src/java/com/jogamp/common/util/JarUtil.java +++ b/src/java/com/jogamp/common/util/JarUtil.java @@ -53,6 +53,8 @@ import jogamp.common.Debug; public class JarUtil { private static final boolean DEBUG = Debug.debug("JarUtil"); + private static final int BUFFER_SIZE = 4096; + /** * Interface allowing users to provide an URL resolver that will convert custom classloader * URLs like Eclipse/OSGi <i>bundleresource:</i> URLs to normal <i>jar:</i> URLs. @@ -138,8 +140,9 @@ public class JarUtil { throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl); } final URI uri; + final URL url; { - final URL url = IOUtil.getClassURL(clazzBinName, cl); + url = IOUtil.getClassURL(clazzBinName, cl); final String scheme = url.getProtocol(); if( null != resolver && !scheme.equals( IOUtil.JAR_SCHEME ) && @@ -163,6 +166,9 @@ public class JarUtil { if( !uri.getScheme().equals( IOUtil.JAR_SCHEME ) ) { throw new IllegalArgumentException("URI is not using scheme "+IOUtil.JAR_SCHEME+": <"+uri+">"); } + if(DEBUG) { + System.out.println("getJarURI res: "+clazzBinName+" -> "+url+" -> "+uri); + } return uri; } @@ -187,7 +193,7 @@ public class JarUtil { if( !classJarURI.getScheme().equals(IOUtil.JAR_SCHEME) ) { throw new IllegalArgumentException("URI is not using scheme "+IOUtil.JAR_SCHEME+": <"+classJarURI+">"); } - String uriS = classJarURI.getRawSchemeSpecificPart(); + String uriS = classJarURI.getSchemeSpecificPart(); // from // file:/some/path/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class @@ -264,26 +270,27 @@ public class JarUtil { if( !classJarURI.getScheme().equals(IOUtil.JAR_SCHEME) ) { throw new IllegalArgumentException("URI is not a using scheme "+IOUtil.JAR_SCHEME+": <"+classJarURI+">"); } - String uriS = classJarURI.getRawSchemeSpecificPart(); // from // file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class // to // file:/some/path/gluegen-rt.jar - int idx = uriS.lastIndexOf('!'); + final String uriS0 = classJarURI.getSchemeSpecificPart(); + int idx = uriS0.lastIndexOf('!'); + final String uriS1; if (0 <= idx) { - uriS = uriS.substring(0, idx); // exclude '!/' + uriS1 = uriS0.substring(0, idx); // exclude '!/' } else { throw new IllegalArgumentException("JAR URI does not contain jar uri terminator '!', uri <"+classJarURI+">"); } - - if(0 >= uriS.lastIndexOf(".jar")) { + if(0 >= uriS1.lastIndexOf(".jar")) { throw new IllegalArgumentException("No Jar name in <"+classJarURI+">"); - } + } + final String uriS2 = IOUtil.encodeToURI(uriS1); if(DEBUG) { - System.out.println("getJarSubURI res: "+uriS); + System.out.println("getJarSubURI res: "+classJarURI+" -> "+uriS0+" -> "+uriS1+" -> "+uriS2); } - return new URI(uriS); + return new URI(uriS2); } /** @@ -302,7 +309,7 @@ public class JarUtil { if( !classJarURI.getScheme().equals(IOUtil.JAR_SCHEME) ) { throw new IllegalArgumentException("URI is not a using scheme "+IOUtil.JAR_SCHEME+": <"+classJarURI+">"); } - String uriS = classJarURI.getRawSchemeSpecificPart(); + String uriS = classJarURI.getSchemeSpecificPart(); // from // file:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class @@ -391,6 +398,19 @@ public class JarUtil { } /** + * @param jarSubUriS file:/some/path/gluegen-rt.jar + * @return jar:file:/some/path/gluegen-rt.jar!/ + * @throws IllegalArgumentException null arguments + * @throws URISyntaxException + */ + public static URI getJarFileURI(String jarSubUriS) throws IllegalArgumentException, URISyntaxException { + if(null == jarSubUriS) { + throw new IllegalArgumentException("jarSubURIS is null"); + } + return new URI(IOUtil.JAR_SCHEME, jarSubUriS+"!/", null); + } + + /** * @param jarFileURI 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 @@ -499,22 +519,23 @@ public class JarUtil { * @param dest * @param nativeLibMap * @param jarFile - * @param deepDirectoryTraversal + * @param nativeLibraryPath if not null, only extracts native libraries within this path. * @param extractNativeLibraries * @param extractClassFiles * @param extractOtherFiles + * @param deepDirectoryTraversal * @return * @throws IOException */ public static final int extract(File dest, Map<String, String> nativeLibMap, JarFile jarFile, + String nativeLibraryPath, boolean extractNativeLibraries, - boolean extractClassFiles, - boolean extractOtherFiles) throws IOException { + boolean extractClassFiles, boolean extractOtherFiles) throws IOException { if (DEBUG) { System.err.println("JarUtil: extract: "+jarFile.getName()+" -> "+dest+ - ", extractNativeLibraries "+extractNativeLibraries+ + ", extractNativeLibraries "+extractNativeLibraries+" ("+nativeLibraryPath+")"+ ", extractClassFiles "+extractClassFiles+ ", extractOtherFiles "+extractOtherFiles); } @@ -528,11 +549,29 @@ public class JarUtil { // Match entries with correct prefix and suffix (ignoring case) final String libBaseName = NativeLibrary.isValidNativeLibraryName(entryName, false); final boolean isNativeLib = null != libBaseName; - if(isNativeLib && !extractNativeLibraries) { - if (DEBUG) { - System.err.println("JarUtil: JarEntry : " + entryName + " native-lib skipped"); + if(isNativeLib) { + if(!extractNativeLibraries) { + if (DEBUG) { + System.err.println("JarUtil: JarEntry : " + entryName + " native-lib skipped, skip all native libs"); + } + continue; + } + if(null != nativeLibraryPath) { + final String nativeLibraryPathS; + final String dirnameS; + try { + nativeLibraryPathS = IOUtil.slashify(nativeLibraryPath, false /* startWithSlash */, true /* endWithSlash */); + dirnameS = IOUtil.getDirname(entryName); + } catch (URISyntaxException e) { + throw new IOException(e); + } + if( !nativeLibraryPathS.equals(dirnameS) ) { + if (DEBUG) { + System.err.println("JarUtil: JarEntry : " + entryName + " native-lib skipped, not in path: "+nativeLibraryPathS); + } + continue; + } } - continue; } final boolean isClassFile = entryName.endsWith(".class"); @@ -566,20 +605,20 @@ public class JarUtil { if (DEBUG) { System.err.println("JarUtil: MKDIR: " + entryName + " -> " + destFile ); } - destFile.mkdir(); + destFile.mkdirs(); } else { final File destFolder = new File(destFile.getParent()); if(!destFolder.exists()) { if (DEBUG) { System.err.println("JarUtil: MKDIR (parent): " + entryName + " -> " + destFolder ); } - destFolder.mkdir(); + destFolder.mkdirs(); } final InputStream in = new BufferedInputStream(jarFile.getInputStream(entry)); final OutputStream out = new BufferedOutputStream(new FileOutputStream(destFile)); int numBytes = -1; try { - numBytes = IOUtil.copyStream2Stream(in, out, -1); + numBytes = IOUtil.copyStream2Stream(BUFFER_SIZE, in, out, -1); } finally { in.close(); out.close(); diff --git a/src/java/com/jogamp/common/util/PropertyAccess.java b/src/java/com/jogamp/common/util/PropertyAccess.java index 1a4bc7e..fdb2665 100644 --- a/src/java/com/jogamp/common/util/PropertyAccess.java +++ b/src/java/com/jogamp/common/util/PropertyAccess.java @@ -40,12 +40,19 @@ public class PropertyAccess { public static final String javaws_prefix = "javaws."; static final HashSet<String> trustedPrefixes; + static final HashSet<String> trusted; static { trustedPrefixes = new HashSet<String>(); trustedPrefixes.add(javaws_prefix); trustedPrefixes.add(jnlp_prefix); // 'jogamp.' and maybe other trusted prefixes will be added later via 'addTrustedPrefix()' + + trusted = new HashSet<String>(); + trusted.add("sun.java2d.opengl"); + trusted.add("sun.java2d.noddraw"); + trusted.add("sun.java2d.d3d"); + trusted.add("sun.awt.noerasebackground"); } /** @@ -60,7 +67,7 @@ public class PropertyAccess { public static final boolean isTrusted(String propertyKey) { final int dot1 = propertyKey.indexOf('.'); if(0<=dot1) { - return trustedPrefixes.contains(propertyKey.substring(0, dot1+1)); + return trustedPrefixes.contains(propertyKey.substring(0, dot1+1)) || trusted.contains(propertyKey); } else { return false; } diff --git a/src/java/com/jogamp/common/util/VersionUtil.java b/src/java/com/jogamp/common/util/VersionUtil.java index 8d9e6a9..8030e94 100644 --- a/src/java/com/jogamp/common/util/VersionUtil.java +++ b/src/java/com/jogamp/common/util/VersionUtil.java @@ -55,7 +55,7 @@ public class VersionUtil { sb.append(SEPERATOR).append(Platform.getNewline()); // environment - sb.append("Platform: ").append(Platform.getOSType()).append(" / ").append(Platform.getOSName()).append(' ').append(Platform.getOSVersion()).append(" (os), "); + sb.append("Platform: ").append(Platform.getOSType()).append(" / ").append(Platform.getOSName()).append(' ').append(Platform.getOSVersion()).append(" (").append(Platform.getOSVersionNumber()).append("), "); sb.append(Platform.getArchName()).append(" (arch), ").append(Platform.getABIType()).append(", "); sb.append(Runtime.getRuntime().availableProcessors()).append(" cores"); sb.append(Platform.getNewline()); @@ -68,7 +68,7 @@ public class VersionUtil { Platform.getMachineDescription().toString(sb).append(Platform.getNewline()); // JVM/JRE - sb.append("Platform: Java Version: ").append(Platform.getJavaVersion()).append(", VM: ").append(Platform.getJavaVMName()); + sb.append("Platform: Java Version: ").append(Platform.getJavaVersion()).append(" (").append(Platform.getJavaVersionNumber()).append("u").append(Platform.JAVA_VERSION_UPDATE).append("), VM: ").append(Platform.getJavaVMName()); sb.append(", Runtime: ").append(Platform.getJavaRuntimeName()).append(Platform.getNewline()); sb.append("Platform: Java Vendor: ").append(Platform.getJavaVendor()).append(", ").append(Platform.getJavaVendorURL()); sb.append(", JavaSE: ").append(Platform.isJavaSE()); diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java index 30dea45..ecf6e56 100644 --- a/src/java/com/jogamp/common/util/cache/TempJarCache.java +++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java @@ -27,20 +27,13 @@ */ package com.jogamp.common.util.cache; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.security.cert.Certificate; -import java.util.Enumeration; import java.util.HashMap; import java.util.Map; -import java.util.jar.JarEntry; import java.util.jar.JarFile; import jogamp.common.Debug; @@ -204,25 +197,32 @@ public class TempJarCache { * * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarURI + * @param nativeLibraryPath if not null, only extracts native libraries within this path. + * @return true if native libraries were added or previously loaded from given jarURI, otherwise false * @throws IOException if the <code>jarURI</code> could not be loaded or a previous load attempt failed * @throws SecurityException * @throws URISyntaxException * @throws IllegalArgumentException */ - public synchronized static final void addNativeLibs(Class<?> certClass, URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException { + public synchronized static final boolean addNativeLibs(Class<?> certClass, URI jarURI, String nativeLibraryPath) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException { final LoadState nativeLibJarsLS = nativeLibJars.get(jarURI); if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) ) { nativeLibJars.put(jarURI, LoadState.LOOKED_UP); final JarFile jarFile = JarUtil.getJarFile(jarURI); if(DEBUG) { - System.err.println("TempJarCache: addNativeLibs: "+jarURI+": nativeJar "+jarFile.getName()); + System.err.println("TempJarCache: addNativeLibs: "+jarURI+": nativeJar "+jarFile.getName()+" (NEW)"); } validateCertificates(certClass, jarFile); - JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, true, false, false); + final int num = JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, nativeLibraryPath, true, false, false); nativeLibJars.put(jarURI, LoadState.LOADED); - } else if( !testLoadState(nativeLibJarsLS, LoadState.LOADED) ) { - throw new IOException("TempJarCache: addNativeLibs: "+jarURI+", previous load attempt failed"); + return num > 0; + } else if( testLoadState(nativeLibJarsLS, LoadState.LOADED) ) { + if(DEBUG) { + System.err.println("TempJarCache: addNativeLibs: "+jarURI+": nativeJar "+jarURI+" (REUSE)"); + } + return true; } + throw new IOException("TempJarCache: addNativeLibs: "+jarURI+", previous load attempt failed"); } /** @@ -248,7 +248,7 @@ public class TempJarCache { } validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile, - false, true, false); + null /* nativeLibraryPath */, false, true, false); classFileJars.put(jarURI, LoadState.LOADED); } else if( !testLoadState(classFileJarsLS, LoadState.LOADED) ) { throw new IOException("TempJarCache: addClasses: "+jarURI+", previous load attempt failed"); @@ -276,7 +276,7 @@ public class TempJarCache { } validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile, - false, false, true); + null /* nativeLibraryPath */, false, false, true); resourceFileJars.put(jarURI, LoadState.LOADED); } else if( !testLoadState(resourceFileJarsLS, LoadState.LOADED) ) { throw new IOException("TempJarCache: addResources: "+jarURI+", previous load attempt failed"); @@ -330,7 +330,7 @@ public class TempJarCache { } validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, - extractNativeLibraries, extractClassFiles, extractOtherFiles); + null /* nativeLibraryPath */, extractNativeLibraries, extractClassFiles, extractOtherFiles); // mark loaded (those were just loaded) if(extractNativeLibraries) { @@ -397,76 +397,7 @@ public class TempJarCache { } return null; } - - - /** - * Bootstrapping version extracting the JAR files root entry containing libBaseName, - * assuming it's a native library. This is used to get the 'gluegen-rt' - * native library, hence bootstrapping. - * - * @param certClass if class is certified, the JarFile entries needs to have the same certificate - * - * @throws IOException - * @throws SecurityException - * @throws URISyntaxException - * @throws IllegalArgumentException - */ - public synchronized static final void bootstrapNativeLib(Class<?> certClass, String libBaseName, URI jarURI) - throws IOException, SecurityException, IllegalArgumentException, URISyntaxException { - checkInitialized(); - boolean ok = false; - int countEntries = 0; - final LoadState nativeLibJarsLS = nativeLibJars.get(jarURI); - if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) && !nativeLibMap.containsKey(libBaseName) ) { - if(DEBUG) { - System.err.println("TempJarCache: bootstrapNativeLib(certClass: "+certClass+", libBaseName "+libBaseName+", jarURI "+jarURI+")"); - } - nativeLibJars.put(jarURI, LoadState.LOOKED_UP); - final JarFile jarFile = JarUtil.getJarFile(jarURI); - if(DEBUG) { - System.err.println("TempJarCache: bootstrapNativeLib: nativeJar "+jarFile.getName()); - } - validateCertificates(certClass, jarFile); - final Enumeration<JarEntry> entries = jarFile.entries(); - while (entries.hasMoreElements()) { - final JarEntry entry = entries.nextElement(); - final String entryName = entry.getName(); - - if( entryName.indexOf('/') == -1 && - entryName.indexOf(File.separatorChar) == -1 && - entryName.indexOf(libBaseName) >= 0 ) - { - final File destFile = new File(tmpFileCache.getTempDir(), entryName); - final InputStream in = new BufferedInputStream(jarFile.getInputStream(entry)); - final OutputStream out = new BufferedOutputStream(new FileOutputStream(destFile)); - int numBytes = 0; - try { - final byte[] buf = new byte[ 2048 ]; - while (true) { - 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.put(jarURI, LoadState.LOADED); - ok = true; - countEntries++; - } - } - } - } else if( testLoadState(nativeLibJarsLS, LoadState.LOADED) ) { - ok = true; // already loaded - } else { - throw new IOException("TempJarCache: bootstrapNativeLib: "+jarURI+", previous load attempt failed"); - } - if(DEBUG) { - System.err.println("TempJarCache: bootstrapNativeLib() done, count "+countEntries+", ok "+ok); - } - } - + private static void validateCertificates(Class<?> certClass, JarFile jarFile) throws IOException, SecurityException { if(null == certClass) { throw new IllegalArgumentException("certClass is null"); |