/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.catalina.loader; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.security.Permission; import java.util.jar.JarFile; import org.apache.naming.resources.FileDirContext; import junit.framework.TestCase; public class TestWebappClassLoader extends TestCase { /** * A runnable thread that calls a method of WebappClassLoader. */ private static class FindClassThread extends Thread { private WebappClassLoader loader; private String className; public Class result; public Throwable exception; public FindClassThread(WebappClassLoader loader, String className) { this.loader = loader; this.className = className; } @Override public void run() { try { result = loader.findClass(className); } catch (Throwable th) { this.exception = th; } } } /** * Tests race condition in {@link WebappClassLoader#findClass()} method. * * @see bug 37458 */ public void testFindClassWithSecurityManager() throws Throwable { // Find, where the compiled classes are URL classesURL = ((URLClassLoader) WebappClassLoader.class .getClassLoader()).getURLs()[0]; File classesPath = new File(classesURL.getPath()); // The class that we will try loading is sealed.Sealed // from sealed.jar final String className = "sealed.Sealed"; // The sealed.jar is a resource that comes along with this class File sealedJarPath = new File(classesPath, this.getClass().getPackage() .getName().replace('.', '/') + "/sealed.jar"); boolean smWasSet = false; SecurityManager oldSm = System.getSecurityManager(); if (oldSm == null) { System.setSecurityManager(new SecurityManager() { @Override public void checkPermission(Permission perm, Object context) { } @Override public void checkPermission(Permission perm) { } }); smWasSet = true; } boolean throwableWasCaught = false; try { WebappClassLoader loader = new WebappClassLoader(); FileDirContext ctx = new FileDirContext(); ctx.setDocBase(sealedJarPath.getParent().toString()); loader.setResources(ctx); loader.addJar("sealed.jar", new JarFile(sealedJarPath), sealedJarPath); loader.start(); FindClassThread th1 = new FindClassThread(loader, className); FindClassThread th2 = new FindClassThread(loader, className); th1.start(); th2.start(); th1.join(); th2.join(); if (th1.exception != null) { throw new RuntimeException("Attempt 1 resulted in exception", th1.exception); } if (th2.exception != null) { throw new RuntimeException("Attempt 2 resulted in exception", th2.exception); } assertNotNull("Result 1 was null", th1.result); assertNotNull("Result 2 was null", th2.result); assertEquals(th1.result, th2.result); } catch (Throwable th) { throwableWasCaught = true; throw th; } finally { if (smWasSet) { try { System.setSecurityManager(oldSm); } catch (RuntimeException ex) { RuntimeException ex2 = new RuntimeException( "Failed to restore SecurityManager", ex); if (!throwableWasCaught) { throw ex2; } // something more serious happened, let's just print out ex2.printStackTrace(); } } } } /** * Iterate the test nnn times. * * @throws Throwable */ public void testFindClassWithSecurityManager100() throws Throwable { for (int i = 0; i < 100; i++) { try { testFindClassWithSecurityManager(); } catch (Throwable th) { System.err.println("Iteration " + i + " resulted in failure:"); th.printStackTrace(); throw th; } } } }