Hansel and Classloaders

Introduction

Hansel has to modify the bytecode of a class to insert probes. This is done by replacing the classloader, that is normally used to load the classes with a modifying classloader. While the classes are loaded, the probes are inserted into the code.

This approach works without problems when using only a single classloader. But the classloaders in java form a hierachy. If the same class is loaded in two different classloaders, the classes are of different type.

Therefor hansel creates its own shadow hierachy. For each classloader used in the hierachy, a modifying classloader is created.

Unfortunatly there still is a problem: When a new Classloader is created it delegates the calls to loadClass() to the system classloader. This causes problems, because these classes are not type compatible with the classes loaded by hansels modifying classloader.

Depending on wether this is happpening in your own code, or a third party library there are different ways of dealing with this problem.

Classloaders in your own code

In your own code you can wrapp your classloaders in a modifying classloader.
      classloader = org.hansel.ClassloaderFactory.wrappClassLoader(classloader);
    
This works regardless of the code being run from a coverage test or in a "normal" environment. But since having references to hansel in production code is perhaps not such a good idea, you probably want to do this in a classloader factory, which can be exchanged, depending on the situation.

Classloaders in third party code

A different problem arises, when classloaders are used in third party libraries. In most cases it is sufficient to exclude the whole third party package from beeing loaded in a modifying classloader. There are two ways of doing this:

  • Creating a hansel.properties file in the classpath. The files should contain all packages, you want to exclude in the form:
            ignorepackages= third.party.package., sun.,java.,javax.,junit., org.hansel.ProbeTableInterface, org.hansel.ClassLoaderFactoryInterface
          
  • Use the constructor of the CoverageDecorator, that allows to specify the ignored packages:
            CoverageDecorator cd = new CoverageDecorator(new Class[]  {your.tested.Class.class},
                                                         new String[] {"third.party.package"},
                                                         true);
          

    In some rare cases, this does not help. These cases occur when code, that is not loaded using the modifying classloader, loads classes, that have to be modified. This happens for example, when objects are created by parsing a xml file.

    In these cases you can only hope, that the developers of the third party code have included a possibility to set the classloader, that is used to load the classes.

    The following list contains the classes, that are known to cause problems, and (if possible) workarounds. If you find libraries, that are not included in this list, please tell me about them.

    Product/Library Workaround
    Apache Digester After creating a new Digester, set the classloader:
            Digester digester = new Digester();
            digester.setClassLoader(getClass().getClassLoader());
            
    WebLogic RMI In the setUp() method of the test set the context classloader:
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    and set it back to the old value in the tearDown() method.