Monday, 14 November 2011

Java class loader and Maven plugin


I recently spent some time to develop a maven plugin which was supposed to run keyword-data driven spread sheet tests via an internally developed testing framework. The framework was running tests as a stand-alone application and tests were executed by the framework’s class loader which was a bad practice as testing harness and test projects were mixed in the same context and there wasn’t any abstraction between them.

Ideally testing framework and test projects should be de-coupled, since test projects and the testing framework have their own individual dependency trees. When it comes to using Maven as a project build tool, it builds up a dependency tree according to its dependency mechanism, which means it could mix dependency of test projects and testing framework and create a new breed of dependency tree.



maven-dependency-project
A sample view of a test project’s dependency tree.



maven-dependency-frw
A sample view of the testing framework’s dependency tree

When classloaders and dependency trees are considered, I personally think Servlet containers are perfect examples of decoupling and class loaders, they have been around for decades, are proven to be best samples of class loading over the years.

For the testing framework maven-plugin development, I started to do refactoring to extract out an interface/api definition of the testing framework so that those refactored interfaces could be served as a contract between the testing framework and test projects as servlets-api serves. In fact when servlets-api is looked at in detail, it can be observed that it’s a contract between a servlet’s container and web application.

Once having a testing framework contract api, rest of the maven plugin development and class loading involved steps creating another test projects class loader, scanning test spread sheets and facades and running those facades through test projects class loader.

Test project class loader creation

private ClassLoader createTestProjectClassLoader() throws MojoExecutionException {
    List<String> testClasspathElements = null;
    try {
        testClasspathElements = this.mavenProject.getTestClasspathElements();
    } catch (DependencyResolutionRequiredException e) {
        new MojoExecutionException("Dependency resolution failed", e);
    }


    List<URL> projectClasspathList = new ArrayList<URL>();
    for (String element : testClasspathElements) {
        File elementFile = new File(element);
        URL url;
        try {
            url = elementFile.toURI().toURL();
            projectClasspathList.add(url);
            }
        } catch (MalformedURLException e) {
            throw new MojoExecutionException(element + " is an invalid classpath element", e);
        }
    }
    ClassLoader pluginClassLoader = getClass().getClassLoader();
    ClassLoader mavenClassLoader = pluginClassLaoder.getParent();
    ClassLoader testProjectClassLoader =
        new URLClassLoader(projectClasspathList.toArray(new URL[projectClasspathList.size()]), mavenClassLoader);
    return testProjectClassLoader;
}


By using different class loaders in the maven plugin for the framework initialization and spread sheet test execution, any possible jar hell issue was avoided and testing framework context and test project contexts were separated.

As a final word, I would like to emphasize that the key point of achieving different class loaders is having a concrete api/interface definition which can be served as contract between two parties.  Once a contracting interface/api is established, it can be loaded by a parent class loader which is used by both class loaders as shown in the example.

No comments:

Post a Comment