changeset 289:d4a6d82ed0ae

PR895: IcedTea-Web searches for missing classes on each loadClass or findClass
author Deepak Bhole <dbhole@redhat.com>
date Wed, 14 Mar 2012 15:05:54 -0400
parents 25af765297c6
children 4672053d61e0
files ChangeLog NEWS netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java tests/netx/unit/net/sourceforge/jnlp/runtime/CodeBaseClassLoaderTest.java
diffstat 4 files changed, 203 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Mar 05 16:39:41 2012 -0500
+++ b/ChangeLog	Wed Mar 14 15:05:54 2012 -0400
@@ -3,6 +3,21 @@
 	* configure.ac: Prepare for 1.1.6
 	* NEWS: Same.
 
+2012-03-14  Deepak Bhole <dbhole@redhat.com>
+			Omair Majid <omajid@redhat.com>
+
+	PR895: IcedTea-Web searches for missing classes on each loadClass or
+	findClass
+	* netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java
+	(CodeBaseClassLoader): Added new map to track resources that are not
+	found.
+	(findClass): If resource was not found before, return immediately. If
+	resource was not found for the first time, record it in the new map.
+	(findResouces): Same.
+	(findResource): Same.
+	* tests/netx/unit/net/sourceforge/jnlp/runtime/CodeBaseClassLoaderTest.java:
+	Test case for PR895 from Omair Majid.
+
 2012-03-05  Deepak Bhole <dbhole@redhat.com>
 
 	* configure.ac: Prepare for 1.1.5
--- a/NEWS	Mon Mar 05 16:39:41 2012 -0500
+++ b/NEWS	Wed Mar 14 15:05:54 2012 -0400
@@ -15,6 +15,7 @@
   - RH586194: Unable to connect to connect with Juniper VPN client
   - PR820: IcedTea-Web 1.1.3 crashing Firefox when loading Citrix XenApp
   - PR838: IcedTea plugin crashes with chrome browser when javascript is executed
+  - PR895: IcedTea-Web searches for missing classes on each loadClass or findClass
 
 New in release 1.1.4 (2011-11-08):
 * Security updates:
--- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java	Mon Mar 05 16:39:41 2012 -0500
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java	Wed Mar 14 15:05:54 2012 -0400
@@ -34,6 +34,7 @@
 import java.security.PrivilegedAction;
 import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -44,6 +45,7 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
@@ -1481,6 +1483,11 @@
 
         JNLPClassLoader parentJNLPClassLoader;
         
+        /**
+         * Classes that are not found, so that findClass can skip them next time
+         */
+        ConcurrentHashMap<String, URL[]> notFoundResources = new ConcurrentHashMap<String, URL[]>();
+
         public CodeBaseClassLoader(URL[] urls, JNLPClassLoader cl) {
             super(urls);
             parentJNLPClassLoader = cl;
@@ -1492,8 +1499,18 @@
         }
 
         @Override
-        public Class<?> findClass(String name) throws ClassNotFoundException { 
-            return super.findClass(name); 
+        public Class<?> findClass(String name) throws ClassNotFoundException {
+
+            // If we have searched this path before, don't try again
+            if (Arrays.equals(super.getURLs(), notFoundResources.get(name)))
+                throw new ClassNotFoundException(name);
+
+            try {
+                return super.findClass(name);
+            } catch (ClassNotFoundException cnfe) {
+                notFoundResources.put(name, super.getURLs());
+                throw cnfe;
+            }
         }
 
         /**
@@ -1520,17 +1537,41 @@
 
         @Override
         public Enumeration<URL> findResources(String name) throws IOException {
+
+            // If we have searched this path before, don't try again
+            if (Arrays.equals(super.getURLs(), notFoundResources.get(name)))
+                return (new Vector<URL>(0)).elements();
+
             if (!name.startsWith("META-INF")) {
-                return super.findResources(name);
+                Enumeration<URL> urls = super.findResources(name);
+
+                if (!urls.hasMoreElements()) {
+                    notFoundResources.put(name, super.getURLs());
+                }
+
+                return urls;
             }
+
             return (new Vector<URL>(0)).elements();
         }
 
         @Override
         public URL findResource(String name) {
+
+            // If we have searched this path before, don't try again
+            if (Arrays.equals(super.getURLs(), notFoundResources.get(name)))
+                return null;
+
             if (!name.startsWith("META-INF")) {
-                return super.findResource(name);
+                URL url = super.findResource(name);
+
+                if (url == null) {
+                    notFoundResources.put(name, super.getURLs());
+                }
+
+                return url;
             }
+
             return null;
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/netx/unit/net/sourceforge/jnlp/runtime/CodeBaseClassLoaderTest.java	Wed Mar 14 15:05:54 2012 -0400
@@ -0,0 +1,142 @@
+/* CodeBaseClassLoaderTest.java
+   Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version.
+*/
+
+package net.sourceforge.jnlp.runtime;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Locale;
+
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.ParseException;
+import net.sourceforge.jnlp.ResourcesDesc;
+import net.sourceforge.jnlp.SecurityDesc;
+import net.sourceforge.jnlp.runtime.JNLPClassLoader;
+import net.sourceforge.jnlp.runtime.JNLPClassLoader.CodeBaseClassLoader;
+
+import org.junit.Test;
+
+public class CodeBaseClassLoaderTest {
+
+    @Test
+    public void testResourceLoadSuccessCaching() throws LaunchException, ClassNotFoundException, IOException, ParseException {
+        final URL JAR_URL = new URL("http://icedtea.classpath.org/netx/about.jar");
+        final URL CODEBASE_URL = new URL("http://icedtea.classpath.org/netx/");
+
+        JNLPFile dummyJnlpFile = new JNLPFile() {
+            @Override
+            public ResourcesDesc getResources() {
+                return new ResourcesDesc(null, new Locale[0], new String[0], new String[0]);
+            }
+
+            @Override
+            public URL getCodeBase() {
+                return CODEBASE_URL;
+            }
+
+            @Override
+            public SecurityDesc getSecurity() {
+                return new SecurityDesc(null, SecurityDesc.SANDBOX_PERMISSIONS, null);
+            }
+        };
+
+        JNLPClassLoader parent = new JNLPClassLoader(dummyJnlpFile, null);
+        CodeBaseClassLoader classLoader = new CodeBaseClassLoader(new URL[] { JAR_URL, CODEBASE_URL }, parent);
+
+        long startTime, stopTime;
+
+        startTime = System.nanoTime();
+        classLoader.findResource("net/sourceforge/jnlp/about/Main.class");
+        stopTime = System.nanoTime();
+        long timeOnFirstTry = stopTime - startTime;
+        System.out.println(timeOnFirstTry);
+
+        startTime = System.nanoTime();
+        classLoader.findResource("net/sourceforge/jnlp/about/Main.class");
+        stopTime = System.nanoTime();
+        long timeOnSecondTry = stopTime - startTime;
+        System.out.println(timeOnSecondTry);
+
+        assertTrue(timeOnSecondTry < (timeOnFirstTry / 10));
+    }
+
+    @Test
+    public void testResourceLoadFailureCaching() throws LaunchException, ClassNotFoundException, IOException, ParseException {
+        final URL JAR_URL = new URL("http://icedtea.classpath.org/netx/about.jar");
+        final URL CODEBASE_URL = new URL("http://icedtea.classpath.org/netx/");
+
+        JNLPFile dummyJnlpFile = new JNLPFile() {
+            @Override
+            public ResourcesDesc getResources() {
+                return new ResourcesDesc(null, new Locale[0], new String[0], new String[0]);
+            }
+
+            @Override
+            public URL getCodeBase() {
+                return CODEBASE_URL;
+            }
+
+            @Override
+            public SecurityDesc getSecurity() {
+                return new SecurityDesc(null, SecurityDesc.SANDBOX_PERMISSIONS, null);
+            }
+        };
+
+        JNLPClassLoader parent = new JNLPClassLoader(dummyJnlpFile, null);
+        CodeBaseClassLoader classLoader = new CodeBaseClassLoader(new URL[] { JAR_URL, CODEBASE_URL }, parent);
+
+        long startTime, stopTime;
+
+        startTime = System.nanoTime();
+        classLoader.findResource("net/sourceforge/jnlp/about/Main_FOO_.class");
+        stopTime = System.nanoTime();
+        long timeOnFirstTry = stopTime - startTime;
+        System.out.println(timeOnFirstTry);
+
+        startTime = System.nanoTime();
+        classLoader.findResource("net/sourceforge/jnlp/about/Main_FOO_.class");
+        stopTime = System.nanoTime();
+        long timeOnSecondTry = stopTime - startTime;
+        System.out.println(timeOnSecondTry);
+
+        assertTrue(timeOnSecondTry < (timeOnFirstTry / 10));
+    }
+
+}