summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-02-21 13:32:52 +0100
committerSven Gothel <[email protected]>2013-02-21 13:32:52 +0100
commit40cbd174b1c78261e3e7fcbac82e96ec37876e74 (patch)
tree315c9d8b4ca4247ba03afb1fc585b348263dc602 /src
parentfd475cc0f42eed11f908da4c725e3f53610ed156 (diff)
parent45a84db7739aba2ab4526d7ef87850b9eb824740 (diff)
Merge remote-tracking branch 'wwalker/bug_687_jar_resolver'
Diffstat (limited to 'src')
-rw-r--r--src/java/com/jogamp/common/util/JarUtil.java46
-rw-r--r--src/junit/com/jogamp/common/util/TestJarUtil.java77
2 files changed, 122 insertions, 1 deletions
diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java
index 84ec59d..7fa5dd0 100644
--- a/src/java/com/jogamp/common/util/JarUtil.java
+++ b/src/java/com/jogamp/common/util/JarUtil.java
@@ -53,6 +53,43 @@ public class JarUtil {
private static final boolean DEBUG = Debug.debug("JarUtil");
/**
+ * This interface allows users to provide an URL resolver that will convert custom classloader
+ * URLs like Eclipse/OSGi "bundleresource:" URLs to normal "jar:" URLs. This is needed when
+ * the classloader has been customized.
+ */
+ public interface Resolver {
+ URL resolve(URL url);
+ }
+
+ /** If non-null, we use this to resolve class file URLs after querying them from the classloader.
+ * The resolver won't be used on an URL if it's already of a common type like file, jar, or http[s].*/
+ private static Resolver resolver;
+
+ /**
+ * Setter.
+ * @param r Resolver to use after querying class file URLs from the classloader.
+ * @throws Error if the resolver has already been set.
+ * @throws SecurityException if the security manager doesn't have the setFactory
+ * permission
+ */
+ public static void setResolver(Resolver r) {
+ if(r == null) {
+ return;
+ }
+
+ if(resolver != null) {
+ throw new Error("Resolver already set!");
+ }
+
+ SecurityManager security = System.getSecurityManager();
+ if(security != null) {
+ security.checkSetFactory();
+ }
+
+ resolver = r;
+ }
+
+ /**
* Returns <code>true</code> if the Class's <code>"com.jogamp.common.GlueGenVersion"</code>
* is loaded from a JarFile and hence has a Jar URL like
* URL <code>jar:<i>sub_protocol</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class"</code>.
@@ -92,7 +129,14 @@ public class JarUtil {
if(null == clazzBinName || null == cl) {
throw new IllegalArgumentException("null arguments: clazzBinName "+clazzBinName+", cl "+cl);
}
- final URL url = IOUtil.getClassURL(clazzBinName, cl);
+ URL url = IOUtil.getClassURL(clazzBinName, cl);
+ if( resolver != null
+ && !url.toString().startsWith("jar:")
+ && !url.toString().startsWith("file:")
+ && !url.toString().startsWith("http:")
+ && !url.toString().startsWith("https:")) {
+ url = resolver.resolve(url);
+ }
// test name ..
final String urlS = url.toExternalForm();
if(DEBUG) {
diff --git a/src/junit/com/jogamp/common/util/TestJarUtil.java b/src/junit/com/jogamp/common/util/TestJarUtil.java
index cb1cc45..ace2d7b 100644
--- a/src/junit/com/jogamp/common/util/TestJarUtil.java
+++ b/src/junit/com/jogamp/common/util/TestJarUtil.java
@@ -29,10 +29,12 @@
package com.jogamp.common.util;
import java.io.IOException;
+import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.net.JarURLConnection;
+import java.net.URLStreamHandler;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -168,6 +170,81 @@ public class TestJarUtil extends JunitTracer {
System.err.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXX");
}
+ /**
+ * Tests JarUtil's ability to resolve non-JAR URLs with a custom resolver. Meant to be used
+ * in cases like an OSGi plugin, where all classes are loaded with custom classloaders and
+ * therefore return URLs that don't start with "jar:". Adapted from test 02 above.
+ */
+ @Test
+ public void testJarUtilJarInJar03() throws IOException, ClassNotFoundException {
+ System.err.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+
+ Assert.assertTrue(TempJarCache.initSingleton());
+ Assert.assertTrue(TempCacheReg.isTempJarCacheUsed());
+ Assert.assertTrue(TempJarCache.isInitialized());
+
+ /** This classloader mimics what OSGi's does -- it takes jar: URLs and makes them into bundleresource: URLs
+ * where the JAR is not directly accessible anymore. Here I leave the JAR name at the end of the URL so I can
+ * retrieve it later in the resolver, but OSGi obscures it completely and returns URLs like
+ * "bundleresource:4.fwk1990213994:1/Something.class" where the JAR name not present. */
+ class CustomClassLoader extends ClassLoader {
+ CustomClassLoader() {
+ super(TestJarUtil.this.getClass().getClassLoader());
+ }
+
+ /** Override normal method to return un-resolvable URL. */
+ public URL getResource(String name) {
+ URL url = super.getResource(name);
+ if(url == null)
+ return(null);
+ URL urlReturn = null;
+ try {
+ // numbers to mimic OSGi -- can be anything
+ urlReturn = new URL("bundleresource", "4.fwk1990213994", 1, url.getFile(),
+ new URLStreamHandler() {
+ @Override
+ protected URLConnection openConnection(URL u) throws IOException {
+ return null;
+ }
+ });
+ } catch(MalformedURLException e) {
+ // shouldn't happen, since I create the URL correctly above
+ Assert.assertTrue(false);
+ }
+ return(urlReturn);
+ }
+ };
+
+ /* This resolver converts bundleresource: URLs back into jar: URLs. OSGi does this by consulting
+ * opaque bundle data inside its custom classloader to find the stored JAR path; we do it here
+ * by simply retrieving the JAR name from where we left it at the end of the URL. */
+ JarUtil.setResolver( new JarUtil.Resolver() {
+ public URL resolve( URL url ) {
+ if(url.toString().startsWith("bundleresource")) {
+ try {
+ return(new URL("jar", "", url.getFile()));
+ } catch(IOException e) {
+ return(url);
+ }
+ }
+ else
+ return(url);
+ }
+ } );
+
+ final ClassLoader rootCL = new CustomClassLoader();
+
+ // Get containing JAR file "TestJarsInJar.jar" and add it to the TempJarCache
+ 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"));
+ final ClassLoader cl = new URLClassLoader(new URL[] { ClassInJar2_jarFileURL }, rootCL);
+ Assert.assertNotNull(cl);
+ validateJarUtil("ClassInJar2.jar", "ClassInJar2", cl);
+ System.err.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+ }
+
public static void main(String args[]) throws IOException {
String tstname = TestJarUtil.class.getName();
org.junit.runner.JUnitCore.main(tstname);