All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.ClassPathLoaderJUnitTest Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.internal;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Vector;

import junit.framework.TestCase;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ClassGen;

import util.ClassBuilder;

/**
 * Test the {@link ClassPathLoader}.
 * 
 * @author Kirk Lund
 * @since 6.5.1.4
 */
public class ClassPathLoaderJUnitTest extends TestCase {

  private static final int TEMP_FILE_BYTES_COUNT = 256;
  private static final int GENERATED_CLASS_BYTES_COUNT = 362;

  private static final String ORIGINAL_ENABLE_TRACE_VALUE;
  private static final String ORIGINAL_EXCLUDE_TCCL_VALUE;
  
  private boolean deleteExtDir = false;

  static {
    ORIGINAL_ENABLE_TRACE_VALUE = System.getProperty(ClassPathLoader.ENABLE_TRACE_PROPERTY,
        ClassPathLoader.ENABLE_TRACE_DEFAULT_VALUE);
    ORIGINAL_EXCLUDE_TCCL_VALUE = System.getProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, Boolean
        .toString(ClassPathLoader.EXCLUDE_TCCL_DEFAULT_VALUE));

    exploreClassLoaders(); // optional output for developer's convenience
  }

  private volatile File tempFile;

  public ClassPathLoaderJUnitTest(String name) {
    super(name);
  }

  @Override
  public void setUp() throws Exception {
    System.setProperty(ClassPathLoader.ENABLE_TRACE_PROPERTY, "true");
    System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");

    File workingDir = new File(System.getProperty("user.dir")).getAbsoluteFile();
    this.tempFile = File.createTempFile("ClassPathLoaderJUnitTest.", null, workingDir);
    FileOutputStream fos = new FileOutputStream(this.tempFile);
    fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
    fos.close();
  }

  @Override
  public void tearDown() throws Exception {
    assertTrue(this.tempFile.delete());
  }

  /**
   * Verifies that {@link ClassPathLoader#getLatest()} is always initialized and returns a ClassPathLoader
   * instance.
   */
  public void testLatestExists() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testLatestExists");

    assertNotNull(ClassPathLoader.getLatest());
  }

  /**
   * Verifies that {@link ClassPathLoader#getLatest()} throws ClassNotFoundException when class does not exist.
   */
  public void testForNameThrowsClassNotFoundException() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testForNameThrowsClassNotFoundException");

    try {
      String classToLoad = "com.nowhere.DoesNotExist";
      ClassPathLoader.getLatest().forName(classToLoad);
      fail();
    } catch (ClassNotFoundException expected) {
      // Expected
    }
  }

  /**
   * Verifies that {@link ClassPathLoader#getLatest()} finds and loads class via
   * Class.forName(String, boolean, ClassLoader) when class does exist.
   */
  public void testForName() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testForName");

    String classToLoad = "com.gemstone.gemfire.internal.classpathloaderjunittest.DoesExist";
    Class clazz = ClassPathLoader.getLatest().forName(classToLoad);
    assertNotNull(clazz);
  }

  /**
   * Verifies that {@link ClassPathLoader#getLatest()} can actually getResource when it exists.
   */
  public void testGetResource() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResource");

    String resourceToGet = "com/gemstone/gemfire/internal/classpathloaderjunittest/DoesExist.class";
    URL url = ClassPathLoader.getLatest().getResource(resourceToGet);
    assertNotNull(url);

    InputStream is = url != null ? url.openStream() : null;
    assertNotNull(is);

    int totalBytesRead = 0;
    byte[] input = new byte[256];

    BufferedInputStream bis = new BufferedInputStream(is);
    for (int bytesRead = bis.read(input); bytesRead > -1;) {
      totalBytesRead += bytesRead;
      bytesRead = bis.read(input);
    }
    bis.close();

    // if the following fails then maybe javac changed and DoesExist.class
    // contains other than 374 bytes of data... consider updating this test
    assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
  }

  /**
   * Verifies that {@link ClassPathLoader#getLatest()} can actually getResources when it exists.
   */
  public void testGetResources() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResources");

    String resourceToGet = "com/gemstone/gemfire/internal/classpathloaderjunittest/DoesExist.class";
    Enumeration urls = ClassPathLoader.getLatest().getResources(resourceToGet);
    assertNotNull(urls);
    assertTrue(urls.hasMoreElements());

    URL url = urls.nextElement();
    InputStream is = url != null ? url.openStream() : null;
    assertNotNull(is);

    int totalBytesRead = 0;
    byte[] input = new byte[256];

    BufferedInputStream bis = new BufferedInputStream(is);
    for (int bytesRead = bis.read(input); bytesRead > -1;) {
      totalBytesRead += bytesRead;
      bytesRead = bis.read(input);
    }
    bis.close();

    // if the following fails then maybe javac changed and DoesExist.class
    // contains other than 374 bytes of data... consider updating this test
    assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
  }

  /**
   * Verifies that {@link ClassPathLoader#getLatest()} can actually getResourceAsStream when it exists.
   */
  public void testGetResourceAsStream() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStream");

    String resourceToGet = "com/gemstone/gemfire/internal/classpathloaderjunittest/DoesExist.class";
    InputStream is = ClassPathLoader.getLatest().getResourceAsStream(resourceToGet);
    assertNotNull(is);

    int totalBytesRead = 0;
    byte[] input = new byte[256];

    BufferedInputStream bis = new BufferedInputStream(is);
    for (int bytesRead = bis.read(input); bytesRead > -1;) {
      totalBytesRead += bytesRead;
      bytesRead = bis.read(input);
    }
    bis.close();

    // if the following fails then maybe javac changed and DoesExist.class
    // contains other than 374 bytes of data... consider updating this test
    assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
  }

  /**
   * Verifies that the {@link GeneratingClassLoader} works and always generates the named class. This is a control which
   * ensures that tests depending on GeneratingClassLoader are valid.
   */
  public void testGeneratingClassLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGeneratingClassLoader");

    ClassLoader gcl = new GeneratingClassLoader();
    String classToLoad = "com.nowhere.TestGeneratingClassLoader";

    Class clazz = gcl.loadClass(classToLoad);
    assertNotNull(clazz);
    assertEquals(classToLoad, clazz.getName());

    Object obj = clazz.newInstance();
    assertEquals(clazz.getName(), obj.getClass().getName());

    try {
      Class.forName(classToLoad);
      fail("Should have thrown ClassNotFoundException");
    } catch (ClassNotFoundException expected) {
      // Expected
    }

    Class clazzForName = Class.forName(classToLoad, true, gcl);
    assertNotNull(clazzForName);
    assertEquals(clazz, clazzForName);

    Object objForName = clazzForName.newInstance();
    assertEquals(classToLoad, objForName.getClass().getName());
  }

  /**
   * Verifies that custom loader is used to load class.
   */
  public void testForNameWithCustomLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testForNameWithCustomLoader");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());

    String classToLoad = "com.nowhere.TestForNameWithCustomLoader";
    Class clazz = dcl.forName(classToLoad);
    assertNotNull(clazz);
    assertEquals(classToLoad, clazz.getName());

    Object obj = clazz.newInstance();
    assertEquals(classToLoad, obj.getClass().getName());
  }

  /**
   * Verifies that {@link Class#forName(String, boolean, ClassLoader)} used with {@link ClassPathLoader} works as
   * expected with named object arrays, while {@link ClassLoader#loadClass(String)} throws ClassNotFoundException for
   * named object arrays.
   */
  public void testForNameWithObjectArray() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testForNameWithObjectArray");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);

    String classToLoad = "[Ljava.lang.String;";
    Class clazz = null;
    clazz = dcl.forName(classToLoad);
    assertEquals(classToLoad, clazz.getName());
  }

  /**
   * Verifies that TCCL finds the class when {@link Class#forName(String, boolean, ClassLoader)} uses
   * {@link ClassPathLoader}.
   */
  public void testForNameWithTCCL() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testForNameWithTCCL");

    final ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    final String classToLoad = "com.nowhere.TestForNameWithTCCL";

    try {
      dcl.forName(classToLoad);
      fail("Should have thrown ClassNotFoundException");
    } catch (ClassNotFoundException expected) {
      // Expected
    }

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // ensure that TCCL is only CL that can find this class
      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
      Class clazz = dcl.forName(classToLoad);
      assertNotNull(clazz);
      Object instance = clazz.newInstance();
      assertNotNull(instance);
      assertEquals(classToLoad, instance.getClass().getName());
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }

    try {
      dcl.forName(classToLoad);
      fail("Should have thrown ClassNotFoundException");
    } catch (ClassNotFoundException expected) {
      // Expected
    }

  }

  /**
   * Verifies that the {@link NullClassLoader} works and never finds the named class. This is a control which ensures
   * that tests depending on NullClassLoader are valid.
   */
  public void testNullClassLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testNullClassLoader");

    ClassLoader cl = new NullClassLoader();
    String classToLoad = "java.lang.String";

    try {
      Class.forName(classToLoad, true, cl);
      fail();
    } catch (ClassNotFoundException expected) {
      // Expected
    }

    String resourceToGet = "java/lang/String.class";

    URL url = cl.getResource(resourceToGet);
    assertNull(url);

    InputStream is = cl.getResourceAsStream(resourceToGet);
    assertNull(is);
  }

  /**
   * Verifies that the {@link SimpleClassLoader} works and finds classes that the parent can find. This is a control
   * which ensures that tests depending on SimpleClassLoader are valid.
   */
  public void testSimpleClassLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testSimpleClassLoader");

    ClassLoader cl = new SimpleClassLoader(getClass().getClassLoader());
    String classToLoad = "java.lang.String";

    Class clazz = Class.forName(classToLoad, true, cl);
    assertNotNull(clazz);

    String resourceToGet = "java/lang/String.class";

    URL url = cl.getResource(resourceToGet);
    assertNotNull(url);

    InputStream is = cl.getResourceAsStream(resourceToGet);
    assertNotNull(is);
  }

  /**
   * Verifies that the {@link BrokenClassLoader} is broken and always throws errors. This is a control which ensures
   * that tests depending on BrokenClassLoader are valid.
   */
  public void testBrokenClassLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testBrokenClassLoader");

    ClassLoader cl = new BrokenClassLoader();

    String classToLoad = "java.lang.String";
    try {
      Class.forName(classToLoad, true, cl);
      fail();
    } catch (ClassNotFoundException e) {
      throw e;
    } catch (BrokenError expected) {
      // Expected
    }

    String resourceToGet = "java/lang/String.class";
    try {
      cl.getResource(resourceToGet);
      fail();
    } catch (BrokenError expected) {
      // Expected
    }
    try {
      cl.getResourceAsStream(resourceToGet);
      fail();
    } catch (BrokenError expected) {
      // Expected
    }
  }

  /**
   * Verifies that the {@link BrokenClassLoader} is broken and always throws errors even when used as a TCCL from
   * {@link ClassPathLoader}. This is primarily a control which ensures that tests depending on
   * BrokenClassLoader are valid, but it also verifies that TCCL is included by default by
   * ClassPathLoader.
   */
  public void testBrokenTCCLThrowsErrors() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testBrokenTCCLThrowsErrors");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl.addOrReplace(new NullClassLoader());

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // set the TCCL to throw errors
      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());

      String classToLoad = "java.lang.String";
      try {
        dcl.forName(classToLoad);
        fail();
      } catch (ClassNotFoundException e) {
        throw e;
      } catch (BrokenError expected) {
        // Expected
      }

      String resourceToGet = "java/lang/String.class";
      try {
        dcl.getResource(resourceToGet);
        fail();
      } catch (BrokenError expected) {
        // Expected
      }

      try {
        dcl.getResourceAsStream(resourceToGet);
        fail();
      } catch (BrokenError expected) {
        // Expected
      }
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that the class classloader or system classloader will find the class or resource. Parent is a
   * {@link NullClassLoader} while the TCCL is an excluded {@link BrokenClassLoader}.
   */
  public void testEverythingWithDefaultLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testEverythingWithDefaultLoader");

    // create DCL such that parent cannot find anything
    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
    dcl.addOrReplace(new NullClassLoader());

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // set the TCCL to never find anything
      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());

      String classToLoad = "java.lang.String";
      Class clazz = dcl.forName(classToLoad);
      assertNotNull(clazz);

      String resourceToGet = "java/lang/String.class";
      URL url = dcl.getResource(resourceToGet);
      assertNotNull(url);
      InputStream is = dcl.getResourceAsStream(resourceToGet);
      assertNotNull(is);
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that the 3rd custom loader will find the class. Parent cannot find it and TCCL is broken. This verifies
   * that all custom loaders are checked and that the custom loaders are all checked before TCCL.
   */
  public void testForNameWithMultipleCustomLoaders() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testForNameWithMultipleCustomLoaders");

    // create DCL such that the 3rd loader should find the class
    // first custom loader becomes parent which won't find anything
    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());
    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
    dcl = dcl.addOrReplace(new NullClassLoader());

    String classToLoad = "com.nowhere.TestForNameWithMultipleCustomLoaders";

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // set TCCL to throw errors which makes sure we find before checking TCCL
      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());

      Class clazz = dcl.forName(classToLoad);
      assertNotNull(clazz);
      assertEquals(classToLoad, clazz.getName());

      Object obj = clazz.newInstance();
      assertEquals(classToLoad, obj.getClass().getName());
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that the 3rd custom loader will get the resource. Parent cannot find it and TCCL is broken. This verifies
   * that all custom loaders are checked and that the custom loaders are all checked before TCCL.
   */
  public void testGetResourceWithMultipleCustomLoaders() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithMultipleCustomLoaders");

    // create DCL such that the 3rd loader should find the resource
    // first custom loader becomes parent which won't find anything
    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());
    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
    dcl = dcl.addOrReplace(new NullClassLoader());

    String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // set TCCL to throw errors which makes sure we find before checking TCCL
      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());

      URL url = dcl.getResource(resourceToGet);
      assertNotNull(url);
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that the 3rd custom loader will get the resources. Parent cannot find it and TCCL is broken. This verifies
   * that all custom loaders are checked and that the custom loaders are all checked before TCCL.
   */
  public void testGetResourcesWithMultipleCustomLoaders() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithMultipleCustomLoaders");

    // create DCL such that the 3rd loader should find the resource
    // first custom loader becomes parent which won't find anything
    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());
    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
    dcl = dcl.addOrReplace(new NullClassLoader());

    String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // set TCCL to throw errors which makes sure we find before checking TCCL
      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());

      Enumeration urls = dcl.getResources(resourceToGet);
      assertNotNull(urls);
      assertTrue(urls.hasMoreElements());
      
      URL url = urls.nextElement();
      assertNotNull(url);
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that the 3rd custom loader will get the resource. Parent cannot find it and TCCL is broken. This verifies
   * that all custom loaders are checked and that the custom loaders are all checked before TCCL.
   */
  public void testGetResourceAsStreamWithMultipleCustomLoaders() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStreamWithMultipleCustomLoaders");

    // create DCL such that the 3rd loader should find the resource
    // first custom loader becomes parent which won't find anything
    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());
    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
    dcl = dcl.addOrReplace(new NullClassLoader());

    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithMultipleCustomLoaders.rsc";

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // set TCCL to throw errors which makes sure we find before checking TCCL
      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());

      InputStream is = dcl.getResourceAsStream(resourceToGet);
      assertNotNull(is);
      is.close();
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that setting excludeThreadContextClassLoader to true will indeed exclude the TCCL.
   */
  public void testExcludeTCCL() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testExcludeTCCL");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);

    String classToLoad = "com.nowhere.TestExcludeTCCL";

    try {
      dcl.forName(classToLoad);
      fail("Should have thrown ClassNotFoundException");
    } catch (ClassNotFoundException expected) {
      // Expected
    }

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // ensure that TCCL is only CL that can find this class
      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
      dcl.forName(classToLoad);
      fail("Should have thrown ClassNotFoundException");
    } catch (ClassNotFoundException expected) {
      // Expected
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }

  }

  /**
   * Verifies that getResource works with custom loader from {@link ClassPathLoader}.
   */
  public void testGetResourceWithCustomLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithCustomLoader");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());

    String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
    URL url = dcl.getResource(resourceToGet);
    assertNotNull(url);

    InputStream is = url != null ? url.openStream() : null;
    assertNotNull(is);

    int totalBytesRead = 0;
    byte[] input = new byte[128];

    BufferedInputStream bis = new BufferedInputStream(is);
    for (int bytesRead = bis.read(input); bytesRead > -1;) {
      totalBytesRead += bytesRead;
      bytesRead = bis.read(input);
    }
    bis.close();

    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
  }

  /**
   * Verifies that getResources works with custom loader from {@link ClassPathLoader}.
   */
  public void testGetResourcesWithCustomLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithCustomLoader");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());

    String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
    Enumeration urls = dcl.getResources(resourceToGet);
    assertNotNull(urls);
    assertTrue(urls.hasMoreElements());

    URL url = urls.nextElement();
    InputStream is = url != null ? url.openStream() : null;
    assertNotNull(is);

    int totalBytesRead = 0;
    byte[] input = new byte[128];

    BufferedInputStream bis = new BufferedInputStream(is);
    for (int bytesRead = bis.read(input); bytesRead > -1;) {
      totalBytesRead += bytesRead;
      bytesRead = bis.read(input);
    }
    bis.close();

    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
  }

  /**
   * Verifies that getResourceAsStream works with custom loader from {@link ClassPathLoader}.
   */
  public void testGetResourceAsStreamWithCustomLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStreamWithCustomLoader");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());

    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithCustomLoader.rsc";
    InputStream is = dcl.getResourceAsStream(resourceToGet);
    assertNotNull(is);

    int totalBytesRead = 0;
    byte[] input = new byte[128];

    BufferedInputStream bis = new BufferedInputStream(is);
    for (int bytesRead = bis.read(input); bytesRead > -1;) {
      totalBytesRead += bytesRead;
      bytesRead = bis.read(input);
    }
    bis.close();

    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
  }

  /**
   * Verifies that getResource works with TCCL from {@link ClassPathLoader}.
   */
  public void testGetResourceWithTCCL() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithTCCL");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);

    String resourceToGet = "com/nowhere/testGetResourceWithTCCL.rsc";
    assertNull(dcl.getResource(resourceToGet));

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
      URL url = dcl.getResource(resourceToGet);
      assertNotNull(url);

      InputStream is = url.openStream();
      assertNotNull(is);

      int totalBytesRead = 0;
      byte[] input = new byte[128];

      BufferedInputStream bis = new BufferedInputStream(is);
      for (int bytesRead = bis.read(input); bytesRead > -1;) {
        totalBytesRead += bytesRead;
        bytesRead = bis.read(input);
      }
      bis.close();

      assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that getResources works with TCCL from {@link ClassPathLoader}.
   */
  public void testGetResourcesWithTCCL() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithTCCL");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);

    String resourceToGet = "com/nowhere/testGetResourceWithTCCL.rsc";
    Enumeration urls = dcl.getResources(resourceToGet);
    assertNotNull(urls);
    assertFalse(urls.hasMoreElements());

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
      urls = dcl.getResources(resourceToGet);
      assertNotNull(urls);

      URL url = urls.nextElement();
      InputStream is = url.openStream();
      assertNotNull(is);

      int totalBytesRead = 0;
      byte[] input = new byte[128];

      BufferedInputStream bis = new BufferedInputStream(is);
      for (int bytesRead = bis.read(input); bytesRead > -1;) {
        totalBytesRead += bytesRead;
        bytesRead = bis.read(input);
      }
      bis.close();

      assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that getResourceAsStream works with TCCL from {@link ClassPathLoader}.
   */
  public void testGetResourceAsStreamWithTCCL() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStreamWithTCCL");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);

    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithTCCL.rsc";
    assertNull(dcl.getResourceAsStream(resourceToGet));

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // ensure that TCCL is only CL that can find this resource
      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
      InputStream is = dcl.getResourceAsStream(resourceToGet);
      assertNotNull(is);

      int totalBytesRead = 0;
      byte[] input = new byte[128];

      BufferedInputStream bis = new BufferedInputStream(is);
      for (int bytesRead = bis.read(input); bytesRead > -1;) {
        totalBytesRead += bytesRead;
        bytesRead = bis.read(input);
      }
      bis.close();

      assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that getResource will skip TCCL if excludeThreadContextClassLoader has been set to true.
   */
  public void testGetResourceExcludeTCCL() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceExcludeTCCL");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);

    String resourceToGet = "com/nowhere/testGetResourceExcludeTCCL.rsc";
    assertNull(dcl.getResource(resourceToGet));

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // ensure that TCCL is only CL that can find this resource
      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
      assertNull(dcl.getResource(resourceToGet));
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  /**
   * Verifies that getResourceAsStream will skip TCCL if excludeThreadContextClassLoader has been set
   * to true.
   */
  public void testGetResourceAsStreamExcludeTCCL() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStreamExcludeTCCL");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);

    String resourceToGet = "com/nowhere/testGetResourceAsStreamExcludeTCCL.rsc";
    assertNull(dcl.getResourceAsStream(resourceToGet));

    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      // ensure that TCCL is only CL that can find this resource
      Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
      assertNull(dcl.getResourceAsStream(resourceToGet));
    } finally {
      Thread.currentThread().setContextClassLoader(cl);
    }
  }

  public void testAddFindsLatestClassLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testAddFindsLatestClassLoader");

    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
    dcl = dcl.addOrReplace(new GeneratingClassLoader());

    String classToLoad = "com.nowhere.TestAddFindsLatestClassLoader";
    Class clazz = dcl.forName(classToLoad);
    assertNotNull(clazz);

    dcl = dcl.addOrReplace(new BrokenClassLoader());

    try {
      dcl.forName(classToLoad);
      fail();
    } catch (BrokenError expected) {
      // Expected
    }
  }

  /**
   * Verifies removing a ClassLoader.
   */
  public void testRemoveClassLoader() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testRemoveClassLoader");

    GeneratingClassLoader genClassLoader = new GeneratingClassLoader();
    ClassPathLoader cpl = ClassPathLoader.createWithDefaults(false);
    cpl = cpl.addOrReplace(genClassLoader);

    String classToLoad = "com.nowhere.TestRemoveClassLoader";
    Class clazz = cpl.forName(classToLoad);
    assertNotNull(clazz);

    cpl = cpl.remove(genClassLoader);

    try {
      clazz = cpl.forName(classToLoad);
      fail();
    } catch (ClassNotFoundException expected) {
      // Expected
    }
  }

  /**
   * Verifies that a ClassLoader will be replaced when added more than once.
   */
  public void testClassLoaderReplace() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testClassLoaderReplace");

    String class1ToLoad = "ClassA";
    String class2ToLoad = "ClassB";

    ClassPathLoader cpl = ClassPathLoader.createWithDefaults(false);
    cpl = cpl.addOrReplace(new OneClassClassLoader(class1ToLoad));

    try {
      @SuppressWarnings("unused")
      Class clazz = cpl.forName(class1ToLoad);
    } catch (ClassNotFoundException unexpected) {
      fail();
    }

    try {
      @SuppressWarnings("unused")
      Class clazz = cpl.forName(class2ToLoad);
      fail();
    } catch (ClassNotFoundException expected) {
      // Expected
    }

    cpl = cpl.addOrReplace(new OneClassClassLoader(class2ToLoad));
    try {
      @SuppressWarnings("unused")
      Class clazz = cpl.forName(class2ToLoad);
    } catch (ClassNotFoundException unexpected) {
      fail();
    }

    try {
      @SuppressWarnings("unused")
      Class clazz = cpl.forName(class1ToLoad);
      fail();
    } catch (ClassNotFoundException expected) {
      // Expected
    }
  }

  /**
   * Verifies that JAR files found in the extlib directory will be correctly
   * added to the {@link ClassPathLoader}.
   */
  public void testJarsInExtLib() throws Exception {
    System.out.println("\nStarting ClassPathLoaderJUnitTest#testJarsInExtLib");

    this.deleteExtDir = ClassPathLoader.EXT_LIB_DIR.mkdirs();
    
    File subdir = new File(ClassPathLoader.EXT_LIB_DIR, "cplju");
    subdir.mkdir();
    
    final ClassBuilder classBuilder = new ClassBuilder();

    File jar1 = new File(ClassPathLoader.EXT_LIB_DIR, "ClassPathLoaderJUnit1.jar");
    writeJarBytesToFile(jar1, classBuilder.createJarFromClassContent("com/cpljunit1/ClassPathLoaderJUnit1",
        "package com.cpljunit1; public class ClassPathLoaderJUnit1 {}"));

    File jar2 = new File(subdir, "ClassPathLoaderJUnit2.jar");
    writeJarBytesToFile(jar2, classBuilder.createJarFromClassContent("com/cpljunit2/ClassPathLoaderJUnit2",
        "package com.cpljunit2; public class ClassPathLoaderJUnit2 {}"));

    try {
      ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
      try {
        classPathLoader.forName("com.cpljunit1.ClassPathLoaderJUnit1");
      } catch (ClassNotFoundException cnfex) {
        fail("JAR file not correctly added to Classpath");
      }

      try {
        classPathLoader.forName("com.cpljunit2.ClassPathLoaderJUnit2");
      } catch (ClassNotFoundException cnfex) {
        fail("JAR file not correctly added to Classpath");
      }

      assertNotNull(classPathLoader.getResource("com/cpljunit2/ClassPathLoaderJUnit2.class"));

      Enumeration urls = classPathLoader.getResources("com/cpljunit1");
      if (!urls.hasMoreElements()) {
        fail("Resources should return one element");
      }
      System.setProperty(ClassPathLoader.ENABLE_TRACE_PROPERTY, ORIGINAL_ENABLE_TRACE_VALUE);
      System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, ORIGINAL_EXCLUDE_TCCL_VALUE);

    } finally {
      if (this.deleteExtDir) {
        ClassPathLoader.EXT_LIB_DIR.deleteOnExit();
      }
      subdir.deleteOnExit();      
      jar1.deleteOnExit();
      jar2.deleteOnExit();
    }
  }
  
  private void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
    final OutputStream outStream = new FileOutputStream(jarFile);
    outStream.write(jarBytes);
    outStream.close();
  }
  
  private static void exploreClassLoaders() {
    System.out.println("Thread.currentThread().getContextClassLoader()...");
    exploreClassLoader(Thread.currentThread().getContextClassLoader(), 1);

    System.out.println("class.getClassLoader()...");
    exploreClassLoader(ClassPathLoaderJUnitTest.class.getClassLoader(), 1);

    System.out.println("ClassLoader.getSystemClassLoader()...");
    exploreClassLoader(ClassLoader.getSystemClassLoader(), 1);
  }

  private static void exploreClassLoader(ClassLoader cl, int indent) {
    String prefix = "";
    for (int i = 0; i < indent; i++) {
      prefix += "\t";
    }
    System.out.println(prefix + "ClassLoader toString() = " + cl);

    Class clazz = cl.getClass();
    System.out.println(prefix + "ClassLoader getClass().getName() = " + clazz.getName());
    exploreClassLoaderSuperClass(prefix, clazz);

    try {
      URL[] urls = ((URLClassLoader) cl).getURLs();
      StringBuilder sb = new StringBuilder(prefix).append("ClassLoader getURLs = [");
      for (int i = 0; i < urls.length; i++) {
        if (i > 0)
          sb.append(", ");
        sb.append(urls[i].toString());
      }
      sb.append("]");
      System.out.println(sb.toString());
    } catch (Exception e) {
      System.out.println(prefix + "ClassLoader is not a URLClassLoader");
    }

    ClassLoader parent = cl.getParent();
    if (parent != null) {
      System.out.println(prefix + "ClassLoader has parent...");
      exploreClassLoader(parent, ++indent);
    }
  }

  private static void exploreClassLoaderSuperClass(String prefix, Class clazz) {
    Class superClazz = clazz.getSuperclass();
    if (superClazz != null) {
      System.out.println(prefix + "                       getSuperclass().getName() = " + superClazz.getName());
      exploreClassLoaderSuperClass(prefix, superClazz);
    }
  }
  
  /**
   * Custom class loader which uses BCEL to always dynamically generate a class for any class name it tries to load.
   */
  private class GeneratingClassLoader extends ClassLoader {

    /**
     * Currently unused but potentially useful for some future test. This causes this loader to only generate a class
     * that the parent could not find.
     * 
     * @param parent
     *          the parent class loader to check with first
     */
    @SuppressWarnings("unused")
    public GeneratingClassLoader(ClassLoader parent) {
      super(parent);
    }

    /**
     * Specifies no parent to ensure that this loader generates the named class.
     */
    public GeneratingClassLoader() {
      super(null); // no parent!!
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
      ClassGen cg = new ClassGen(name, "java.lang.Object", "", Constants.ACC_PUBLIC | Constants.ACC_SUPER, null);
      cg.addEmptyConstructor(Constants.ACC_PUBLIC);
      JavaClass jClazz = cg.getJavaClass();
      byte[] bytes = jClazz.getBytes();
      return defineClass(jClazz.getClassName(), bytes, 0, bytes.length);
    }

    @Override
    protected URL findResource(String name) {
      URL url = null;
      try {
        url = ClassPathLoaderJUnitTest.this.tempFile.getAbsoluteFile().toURI().toURL();
        System.out.println("GeneratingClassLoader#findResource returning " + url);
      } catch (IOException e) {
      }
      return url;
    }

    @Override
    protected Enumeration findResources(String name) throws IOException {
      URL url = null;
      try {
        url = ClassPathLoaderJUnitTest.this.tempFile.getAbsoluteFile().toURI().toURL();
        System.out.println("GeneratingClassLoader#findResources returning " + url);
      } catch (IOException e) {
      }
      Vector urls = new Vector();
      urls.add(url);
      return urls.elements();
    }
  }

  /**
   * Custom class loader which will never find any class or resource.
   */
  private class NullClassLoader extends ClassLoader {
    public NullClassLoader() {
      super(null); // no parent!!
    }

    @Override
    public Class loadClass(String name) throws ClassNotFoundException {
      throw new ClassNotFoundException(name);
    }

    @Override
    public URL getResource(String name) {
      return null;
    }
  }

  /**
   * Custom class loader which will find anything the parent can find.
   */
  private class SimpleClassLoader extends ClassLoader {
    public SimpleClassLoader(ClassLoader parent) {
      super(parent);
    }
  }

  private class OneClassClassLoader extends ClassLoader {
    private final GeneratingClassLoader genClassLoader = new GeneratingClassLoader();
    private String className;

    public OneClassClassLoader(final String className) {
      super(null); // no parent!!
      this.className = className;
    }
    
    @Override
    public Class findClass(String name) throws ClassNotFoundException {
      if (!name.equals(className))
        throw new ClassNotFoundException();

      else
        return this.genClassLoader.findClass(name);
    }
    
    @Override
    public boolean equals(final Object other) {
      return (other instanceof OneClassClassLoader);
    }
  }
  
  /**
   * Custom class loader which is broken and always throws errors.
   */
  private class BrokenClassLoader extends ClassLoader {
    public BrokenClassLoader() {
      super(null); // no parent!!
    }

    @Override
    public Class loadClass(String name) throws ClassNotFoundException {
      throw new BrokenError();
    }

    @Override
    public URL getResource(String name) {
      throw new BrokenError();
    }
  }

  @SuppressWarnings("serial")
  private class BrokenError extends Error {
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy