Archive for April 2013

Reverse Order Class Loader (Child First)   Leave a comment

Recently while working on using JMockit to write unit tests for some legacy code, I came across a problem with classloaders that forced me to write my own class loader. In Java, classloaders like URLClassLoader delegates to it’s parent classloader before searching it’s own classpath. The issue was that my JUnit tests were being launched in Eclipse by a laucher that created a new URLClassLoader and used that to launch the tests. This worked well in most cases except when using JMockit. The reason why this was a problem was because JMockit realized that it was being loaded by a custom class loader and tried to reinitialize itself with the URLClassLoader created by the launcher. This led to ClassNoDefFoundError where it couldn’t find the junit classes even though junit was present in the classpath. The reason I guess is that JMockit classes was found by the parent classloader of the URLClassLoader which was the system classloader that didn’t have the junit.jar in the classpath. Once a class is loaded by a classloader it uses that classloader to load every other class it needs. The solution for this was to use a Reverse Order ClassLoader or a class loader that searched it’s classpath first before delegating to it’s parent classloader. Here is the code of the ReverseOrderClassLoader:


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;

public class ReverseOrderClassLoader extends URLClassLoader {

    private static String classPath = "Very Very long classPath....";

    public ReverseOrderClassLoader(ClassLoader parent) {
    	super(getClassPathAsURLs(classPath),null);    	
    	System.out.println("Parent Class loader : " + parent.getClass().getName());        
    }

    public static URL[] getClassPathAsURLs(String classpath) {
		 List urlList = new ArrayList();
       for(String path : classpath.split(";")){
           try {
               urlList.add(new File(path).toURI().toURL());
           } catch (MalformedURLException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
       }
       return urlList.toArray(new URL[]{});        
   }

    @Override
    protected synchronized Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
    	Logger logger = Logger.getLogger(ReverseOrderClassLoader.class.getName());
    	logger.info("Received request to load class : " + name + " resolve = " + resolve);
        Class<?> c = findLoadedClass(name);
        if(c != null)
        	logger.info("Found loaded class : " + name);

        if (c == null) {          
                try {
                	logger.info("Trying to find class : " + name + " in local classpath");
                    c = findClass(name);
                } catch (ClassNotFoundException e) {
                	logger.info("Class : " + name + " not found in local classpath. Delegating to parent classloader");
                	if(name.contains("RtmCampaignPlacementAccess"))
                    	logger.info("RtmCampaignPlacementAccess test.");
                	c = super.loadClass(name, resolve);
                    if(c == null)
                    	logger.info("Class : " + name + " not found in any classpath.");
                    else if(name.contains("RtmCampaignPlacementAccess"))
                    	logger.info("Found RtmCampaignPlacementAccess in classpath.");
                }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }

    @Override
    public URL getResource(String name) {    
    	Logger logger = Logger.getLogger(ReverseOrderClassLoader.class.getName());
    	logger.info("Trying to find resource : " + name + " in local classpath.");
        URL url = findResource(name);
        if (url == null) {

        	logger.info("Couldn't find resource : " + name + " in local classpath. Delegating to parent");
            url = super.getResource(name);
            if(url == null)
            	logger.info("Couldn't find resource : " + name + " in any of the paths returning null");
        }
        return url;
    }

    @Override
    public Enumeration getResources(String name) throws IOException {

        Enumeration localUrls = findResources(name);
        printEnumerations("Local resources for : " + name, localUrls);

        Enumeration parentUrls = null;
        if (getParent() != null) 
            parentUrls = getParent().getResources(name);        
        printEnumerations("Parent resources for : " + name, parentUrls);

        final List urlList = new ArrayList();
        addURLsIntoList(urlList, localUrls);
        addURLsIntoList(urlList, parentUrls);

        return Collections.enumeration(urlList);
    }

    @Override
    public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
        }
        return null;
    }

    private void printEnumerations(String header,Enumeration urlEnumerator){
    	Logger logger = Logger.getLogger(ReverseOrderClassLoader.class.getName());
    	logger.info(header);
    	if(urlEnumerator != null){
    		while(urlEnumerator.hasMoreElements())
    			System.out.print(urlEnumerator.nextElement().toString()+":");
    	}
    	logger.info("\n");
    }

    private void addURLsIntoList(List urlList,Enumeration urlEnumeration){
    	 if (urlEnumeration != null && urlList != null) {
             while (urlEnumeration.hasMoreElements()) {
            	 urlList.add(urlEnumeration.nextElement());
             }
         }
    }

}
Advertisements

Posted April 9, 2013 by salilsurendran in Uncategorized

Tagged with