changeset 343:9b8837c53c09

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 aac62fe468b1
children 9c0e0aec8ac8
files ChangeLog NEWS netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java tests/netx/unit/net/sourceforge/jnlp/runtime/CodeBaseClassLoaderTest.java
diffstat 4 files changed, 205 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Mar 05 16:38:04 2012 -0500
+++ b/ChangeLog	Wed Mar 14 15:05:54 2012 -0400
@@ -1,3 +1,18 @@
+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.2.1
--- a/NEWS	Mon Mar 05 16:38:04 2012 -0500
+++ b/NEWS	Wed Mar 14 15:05:54 2012 -0400
@@ -9,6 +9,8 @@
 CVE-XXXX-YYYY: http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=XXXX-YYYY
 
 New in release 1.2.1 (2012-XX-XX):
+* Plugin
+  - PR895: IcedTea-Web searches for missing classes on each loadClass or findClass
 
 New in release 1.2 (2012-03-05):
 * Security updates:
--- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java	Mon Mar 05 16:38:04 2012 -0500
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java	Wed Mar 14 15:05:54 2012 -0400
@@ -37,6 +37,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;
@@ -47,9 +48,11 @@
 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;
+
 import net.sourceforge.jnlp.AppletDesc;
 import net.sourceforge.jnlp.ApplicationDesc;
 import net.sourceforge.jnlp.DownloadOptions;
@@ -1874,6 +1877,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;
@@ -1885,8 +1893,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;
+            }
         }
 
         /**
@@ -1913,17 +1931,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));
+    }
+
+}