I am migrating an app from java 8 to java 11. I did most of the upgrade but got stuck with the following exception.
class com.teamhub.plugins.sitemaps.jobs.SitemapUpdateJob cannot be cast to class org.quartz.Job (com...SitemapUpdateJob is in unnamed module of loader com...utils.ChildFirstClassLoader @bb39957; org.quartz.Job is in unnamed module of loader org.akhikhl.gretty.FilteringClassLoader @247667dd)
The application uses the normal context class loader but it loads plugins into the classpath using the ChildFirstClassLoader which on java 8 worked like a charm. In Java 11 though, it fails. Looks like the issue is that the parent classloader loaded Job, and the child, ChildFirstClassLoader failes to use that to cast an implemented job.
public class ChildFirstClassLoader extends URLClassLoader {
private static final Set<String> FORCE_IN_PARENT = new HashSet<String>(); //ImmutableSet.of("");
private ClassLoader system;
public ChildFirstClassLoader(URL[] classpath, ClassLoader parent) {
super(classpath, parent);
system = getSystemClassLoader();
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// checking local
c = findClass(name);
} catch (ClassNotFoundException | SecurityException e) {
c = loadClassFromParent(name, resolve);
}
}
if (resolve)
resolveClass(c);
return c;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
for (String pkg : FORCE_IN_PARENT){
if (name.startsWith(pkg)) {
throw new ClassNotFoundException();
}
}
return super.findClass(name);
}
private Class<?> loadClassFromParent(String name, boolean resolve) throws ClassNotFoundException {
// checking parent
// This call to loadClass may eventually call findClass
// again, in case the parent doesn't find anything.
Class<?> c;
try {
c = super.loadClass(name, resolve);
} catch (ClassNotFoundException | SecurityException e) {
c = loadClassFromSystem(name);
}
return c;
}
private Class<?> loadClassFromSystem(String name) throws ClassNotFoundException{
Class<?> c = null;
if (system != null) {
// checking system: jvm classes, endorsed, cmd classpath,
// etc.
c = system.loadClass(name);
}
return c;
}
@Override
public URL getResource(String name) {
URL url = findResource(name);
if (url == null)
url = super.getResource(name);
if (url == null && system != null)
url = system.getResource(name);
return url;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
/**
* Similar to super, but local resources are enumerated before parent
* resources
*/
Enumeration<URL> systemUrls = null;
if (system != null) {
systemUrls = system.getResources(name);
}
Enumeration<URL> localUrls = findResources(name);
Enumeration<URL> parentUrls = null;
if (getParent() != null) {
parentUrls = getParent().getResources(name);
}
final List<URL> urls = new ArrayList<URL>();
if (localUrls != null) {
while (localUrls.hasMoreElements()) {
URL local = localUrls.nextElement();
urls.add(local);
}
}
if (systemUrls != null) {
while (systemUrls.hasMoreElements()) {
urls.add(systemUrls.nextElement());
}
}
if (parentUrls != null) {
while (parentUrls.hasMoreElements()) {
urls.add(parentUrls.nextElement());
}
}
return new Enumeration<URL>() {
Iterator<URL> iter = urls.iterator();
public boolean hasMoreElements() {
return iter.hasNext();
}
public URL nextElement() {
return iter.next();
}
};
}
public InputStream getResourceAsStream(String name) {
URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
}
return null;
}
@Override
public String toString() {
return String.format("ChildFirstClassLoader [system=%s, getURLs()=%s, getParent()=%s]",
system, Arrays.toString(getURLs()), getParent());
}