In the migration tool, there are a Kafka 0.7 consumer and a Kafka 0.8 producer.
As these two version 's code path are almost the same, the parent last classloader is written in order to let the tool load the previous version consumer. Normally, classloader in java prefers parent first. Like the graph below:
Source:http://javarevisited.blogspot.hk/2012/12/how-classloader-works-in-java.html (Java Revisit)
After loading the class in the customized class loader, we can use the customized class loader to create the instance by using reflection.
private static class ParentLastURLClassLoader extends ClassLoader {
private ChildURLClassLoader childClassLoader;
/**
* This class allows me to call findClass on a class loader
*/
private static class FindClassClassLoader extends ClassLoader {
public FindClassClassLoader(ClassLoader parent) {
super(parent);
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
}
/**
* This class delegates (child then parent) for the findClass method for a URLClassLoader.
* We need this because findClass is protected in URLClassLoader
*/
private static class ChildURLClassLoader extends URLClassLoader {
private FindClassClassLoader realParent;
public ChildURLClassLoader( URL[] urls, FindClassClassLoader realParent) {
super(urls, null);
this.realParent = realParent;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
try{
// first try to use the URLClassLoader findClass
return super.findClass(name);
}
catch( ClassNotFoundException e ) {
// if that fails, we ask our real parent class loader to load the class (we give up)
return realParent.loadClass(name);
}
}
}
public ParentLastURLClassLoader(URL[] urls) {
super(Thread.currentThread().getContextClassLoader());
childClassLoader = new ChildURLClassLoader(urls, new FindClassClassLoader(this.getParent()));
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
try {
// first we try to find a class inside the child class loader
return childClassLoader.findClass(name);
}
catch( ClassNotFoundException e ) {
// didn't find it, try the parent
return super.loadClass(name, resolve);
}
}
}