/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * Licensed 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.jboss.shrinkwrap.impl.base.classloader; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.logging.Logger; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.GenericArchive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.Asset; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.classloader.ShrinkWrapClassLoader; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.impl.base.io.IOUtil; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * Ensures the {@link ShrinkWrapClassLoader} is working as contracted * * @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a> * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a> * @version $Revision: $ */ public class ShrinkWrapClassLoaderTestCase { // -------------------------------------------------------------------------------------|| // Class Members ----------------------------------------------------------------------|| // -------------------------------------------------------------------------------------|| /** * Logger */ private static final Logger log = Logger.getLogger(ShrinkWrapClassLoaderTestCase.class.getName()); /** * Class to be accessed via a ShrinkWrap ClassLoader */ private static final Class<?> applicationClassLoaderClass = LoadedTestClass.class; /** * Archive to be read via a {@link ShrinkWrapClassLoaderTestCase#shrinkWrapClassLoader} */ private static final JavaArchive archive = ShrinkWrap.create(JavaArchive.class).addClass( applicationClassLoaderClass); // -------------------------------------------------------------------------------------|| // Instance Members -------------------------------------------------------------------|| // -------------------------------------------------------------------------------------|| /** * ClassLoader used to load {@link ShrinkWrapClassLoaderTestCase#applicationClassLoaderClass} */ private ClassLoader shrinkWrapClassLoader; // -------------------------------------------------------------------------------------|| // Lifecycle --------------------------------------------------------------------------|| // -------------------------------------------------------------------------------------|| /** * Creates the {@link ShrinkWrapClassLoaderTestCase#shrinkWrapClassLoader} used to load classes from an * {@link Archive}. The {@link ClassLoader} will be isolated from the application classpath by specifying a null * parent explicitly. */ @Before public void createClassLoader() { shrinkWrapClassLoader = new ShrinkWrapClassLoader((ClassLoader) null, archive); } /** * Closes resources associated with the {@link ShrinkWrapClassLoaderTestCase#shrinkWrapClassLoader} */ @After public void closeClassLoader() { if (shrinkWrapClassLoader instanceof Closeable) { try { ((Closeable) shrinkWrapClassLoader).close(); } catch (final IOException e) { log.warning("Could not close the " + shrinkWrapClassLoader + ": " + e); } } } // -------------------------------------------------------------------------------------|| // Tests ------------------------------------------------------------------------------|| // -------------------------------------------------------------------------------------|| /** * Ensures we can load a Class instance from the {@link ShrinkWrapClassLoader} */ @Test public void shouldBeAbleToLoadClassFromArchive() throws ClassNotFoundException { // Load the test class from the CL final Class<?> loadedTestClass = Class.forName(applicationClassLoaderClass.getName(), false, shrinkWrapClassLoader); final ClassLoader loadedTestClassClassLoader = loadedTestClass.getClassLoader(); log.info("Got " + loadedTestClass + " from " + loadedTestClassClassLoader); // Assertions Assert.assertNotNull("Test class could not be found via the ClassLoader", loadedTestClass); Assert.assertSame("Test class should have been loaded via the archive ClassLoader", shrinkWrapClassLoader, loadedTestClassClassLoader); Assert.assertNotSame("Class Loaded from the CL should not be the same as the one on the appCL", loadedTestClass, applicationClassLoaderClass); } /** * Ensures that we can open up directory content as obtained via a {@link URL} from the * {@link ShrinkWrapClassLoader} (ie. should return null, not throw an exception) * * SHRINKWRAP-306 */ @Test public void shouldBeAbleToOpenStreamOnDirectoryUrl() throws IOException { // Make a new Archive with some content in a directory final String nestedResourceName = "nested/test"; final Asset testAsset = new StringAsset("testContent"); final GenericArchive archive = ShrinkWrap.create(GenericArchive.class).add(testAsset, nestedResourceName); // Make a CL to load the content final ClassLoader swCl = new ShrinkWrapClassLoader(archive); // Get the URL to the parent directory final URL nestedResourceUrl = swCl.getResource(nestedResourceName); final URL nestedResourceUpALevelUrl = new URL(nestedResourceUrl, "../"); // openStream on the URL to the parent directory; should return null, not throw an exception final InputStream in = nestedResourceUpALevelUrl.openStream(); Assert.assertNull("URLs pointing to a directory should openStream as null", in); } /** * Ensures that we can open up an asset that doesn't exist via a {@link URL} from the {@link ShrinkWrapClassLoader} * (ie. should throw {@link FileNotFoundException} * * SHRINKWRAP-308 */ @Test(expected = FileNotFoundException.class) public void shouldNotBeAbleToOpenStreamOnNonexistantAsset() throws IOException { // Make a new Archive with some content in a directory final String nestedResourceName = "nested/test"; final Asset testAsset = new StringAsset("testContent"); final GenericArchive archive = ShrinkWrap.create(GenericArchive.class).add(testAsset, nestedResourceName); // Make a CL to load the content final ClassLoader swCl = new ShrinkWrapClassLoader(archive); // Get the URL to something that doesn't exist final URL nestedResourceUrl = swCl.getResource(nestedResourceName); final URL nestedResourceThatDoesntExistUrl = new URL(nestedResourceUrl, "../fake"); // openStream on the URL that doesn't exist should throw FNFE nestedResourceThatDoesntExistUrl.openStream(); } /** * Ensures we can load a resource by name from the {@link ShrinkWrapClassLoader} */ @Test public void shouldBeAbleToLoadResourceFromArchive() { // Load the class as a resource final URL resource = shrinkWrapClassLoader.getResource(getResourceNameOfClass(applicationClassLoaderClass)); // Assertions Assert.assertNotNull(resource); } /** * SHRINKWRAP-237: Reading the same resource multiple times cause IOException */ @Test public void shouldBeAbleToLoadAResourceFromArchiveMultipleTimes() throws Exception { String resourceName = getResourceNameOfClass(applicationClassLoaderClass); // Load the class as a resource URL resource = shrinkWrapClassLoader.getResource(resourceName); // Assertions Assert.assertNotNull(resource); // Read the stream until EOF IOUtil.copyWithClose(resource.openStream(), new ByteArrayOutputStream()); // Load the class as a resource for the second time resource = shrinkWrapClassLoader.getResource(resourceName); // Assertions Assert.assertNotNull(resource); // SHRINKWRAP-237: This throws IOException: Stream closed IOUtil.copyWithClose(resource.openStream(), new ByteArrayOutputStream()); } // -------------------------------------------------------------------------------------|| // Internal Helper Methods ------------------------------------------------------------|| // -------------------------------------------------------------------------------------|| /** * Obtains the resource name for a given class */ private static String getResourceNameOfClass(final Class<?> clazz) { assert clazz != null : "clazz must be specified"; final StringBuilder sb = new StringBuilder(); final String className = clazz.getName().replace('.', '/'); sb.append(className); sb.append(".class"); return sb.toString(); } }