Skip to content

Commit

Permalink
Handle null classloader as system classloader.
Browse files Browse the repository at this point in the history
Probe for HK2 targets in different loaders.
Signed-off-by: jmehrens <[email protected]>
  • Loading branch information
jmehrens committed Nov 16, 2023
1 parent c7c7065 commit d50a76f
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 59 deletions.
56 changes: 37 additions & 19 deletions api/src/main/java/jakarta/mail/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
Expand Down Expand Up @@ -1384,30 +1385,47 @@ EventQueue getEventQueue() {
return q;
}

private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader";

@SuppressWarnings({"unchecked"})
private <T> Iterator<T> lookupUsingHk2ServiceLoader(Class<T> factoryId, ClassLoader loader) {
Class<?> target;
try {
target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME, false, loader);
} catch (ClassNotFoundException ignored) {
return Collections.emptyIterator();
private static Class<?>[] getHk2ServiceLoaderTargets(Class<?> factoryClass) {
ClassLoader[] loaders = new ClassLoader[]{
Thread.currentThread().getContextClassLoader(),
factoryClass.getClassLoader(),
ClassLoader.getSystemClassLoader()};

Class<?>[] classes = new Class<?>[loaders.length];
int w = 0;
for (ClassLoader loader : loaders) {
if (loader != null) {
try {
classes[w++] = Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader", false, loader);
} catch (ClassNotFoundException | LinkageError ignored) {
}
}
}

try {
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
Class<?> serviceClass = Class.forName(factoryId.getName(), false, loader);
Class<?>[] args = new Class<?>[]{serviceClass};
Method m = target.getMethod("lookupProviderInstances", Class.class);
Iterable<T> result = ((Iterable<T>) m.invoke(null, (Object[]) args));
return result != null ? result.iterator() : Collections.emptyIterator();
} catch (Exception ignored) {
// log and continue
return Collections.emptyIterator();
if (classes.length != w) {
classes = Arrays.copyOf(classes, w);
}
return classes;
}

@SuppressWarnings({"unchecked"})
private <T> Iterator<T> lookupUsingHk2ServiceLoader(Class<T> factoryId, ClassLoader loader) {
for (Class<?> target : getHk2ServiceLoaderTargets(factoryId)) {
try {
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
Class<?> serviceClass = Class.forName(factoryId.getName(), false, loader);
Class<?>[] args = new Class<?>[]{serviceClass};
Method m = target.getMethod("lookupProviderInstances", Class.class);
Iterable<T> result = ((Iterable<T>) m.invoke(null, (Object[]) args));
if (result != null) {
return result.iterator();
}
} catch (Exception ignored) {
// log and continue
}
}
return Collections.emptyIterator();
}
}

/**
Expand Down
103 changes: 63 additions & 40 deletions api/src/main/java/jakarta/mail/util/FactoryFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package jakarta.mail.util;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import java.util.ServiceLoader;

Expand All @@ -34,16 +35,24 @@ class FactoryFinder {
*/
static <T> T find(Class<T> factoryClass) throws RuntimeException {
T result;
result = find(factoryClass, Thread.currentThread().getContextClassLoader());
if (result != null) {
return result;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader != null) {
result = find(factoryClass, loader);
if (result != null) {
return result;
}
}

result = find(factoryClass, factoryClass.getClassLoader());
if (result != null) {
return result;
//JakartaMail API ClassLoader / caller classloader
loader = factoryClass.getClassLoader();
if (loader != null) {
result = find(factoryClass, loader);
if (result != null) {
return result;
}
}

//Fallback to system class loader
result = find(factoryClass, ClassLoader.getSystemClassLoader());
if (result != null) {
return result;
Expand All @@ -53,10 +62,8 @@ static <T> T find(Class<T> factoryClass) throws RuntimeException {
}

private static <T> T find(Class<T> factoryClass, ClassLoader loader) throws RuntimeException {
String factoryId = factoryClass.getName();

// Use the system property first
String className = fromSystemProperty(factoryId);
String className = fromSystemProperty(factoryClass.getName());
if (className != null) {
T result = newInstance(className, factoryClass, loader);
if (result != null) {
Expand All @@ -72,24 +79,22 @@ private static <T> T find(Class<T> factoryClass, ClassLoader loader) throws Runt

// handling Glassfish/OSGi (platform specific default)

T result = lookupUsingHk2ServiceLoader(factoryId, factoryClass, loader);
T result = lookupUsingHk2ServiceLoader(factoryClass, loader);
if (result != null) {
return result;
}

return null;
}

@SuppressWarnings({"unchecked"})
private static <T> T newInstance(String className, Class<T> factoryClass, ClassLoader classLoader) throws RuntimeException {
checkPackageAccess(className);
try {
Class<T> clazz;
if (classLoader == null) {
clazz = (Class<T>) Class.forName(className);
} else {
clazz = (Class<T>) Class.forName(className, false, classLoader);
Class<?> clazz;
if (classLoader == null) { //Match behavior of ServiceLoader
classLoader = ClassLoader.getSystemClassLoader();
}
clazz = Class.forName(className, false, classLoader);
return clazz.asSubclass(factoryClass).getConstructor().newInstance();
} catch (ClassCastException wrongLoader) {
return null;
Expand All @@ -102,41 +107,59 @@ private static String fromSystemProperty(String factoryId) {
String systemProp = System.getProperty(factoryId);
return systemProp;
}

private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader";

@SuppressWarnings({"unchecked"})
private static <T> T lookupUsingHk2ServiceLoader(String factoryId, Class<T> factoryClass, ClassLoader loader) {
Class<?> target;
try {
target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME, false, loader);
} catch (ClassNotFoundException ignored) {
return null;

private static Class<?>[] getHk2ServiceLoaderTargets(Class<?> factoryClass) {
ClassLoader[] loaders = new ClassLoader[]{
Thread.currentThread().getContextClassLoader(),
factoryClass.getClassLoader(),
ClassLoader.getSystemClassLoader()};

Class<?>[] classes = new Class<?>[loaders.length];
int w = 0;
for (ClassLoader loader : loaders) {
if (loader != null) {
try {
classes[w++] = Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader", false, loader);
} catch (ClassNotFoundException | LinkageError ignored) {
}
}
}

if (classes.length != w) {
classes = Arrays.copyOf(classes, w);
}
return classes;
}

try {
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
Class<?> serviceClass = Class.forName(factoryId, false, loader);
Class<?>[] args = new Class<?>[]{serviceClass};
Method m = target.getMethod("lookupProviderInstances", Class.class);
Iterable<?> iterable = ((Iterable<?>) m.invoke(null, (Object[]) args));
if (iterable == null) {
return null;
@SuppressWarnings({"unchecked"})
private static <T> T lookupUsingHk2ServiceLoader(Class<T> factoryClass, ClassLoader loader) {
for (Class<?> target : getHk2ServiceLoaderTargets(factoryClass)) {
try {
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
Class<?> serviceClass = Class.forName(factoryClass.getName(), false, loader);
Class<?>[] args = new Class<?>[]{serviceClass};
Method m = target.getMethod("lookupProviderInstances", Class.class);
Iterable<?> iterable = ((Iterable<?>) m.invoke(null, (Object[]) args));
if (iterable != null) {
Iterator<?> iter = iterable.iterator();
if (iter.hasNext()) {
return factoryClass.cast(iter.next()); //Verify classloader.
}
}
} catch (Exception ignored) {
// log and continue
}
Iterator<?> iter = iterable.iterator();
return iter.hasNext() ? factoryClass.cast(iter.next()) : null;
} catch (Exception ignored) {
// log and continue
return null;
}
return null;
}

private static <T> T factoryFromServiceLoader(Class<T> factory, ClassLoader loader) {
//ClassLoader of null is treated as system classloader
try {
ServiceLoader<T> sl = ServiceLoader.load(factory, loader);
Iterator<T> iter = sl.iterator();
if (iter.hasNext()) {
return factory.cast(iter.next());
return factory.cast(iter.next()); //Verify loader
} else {
return null;
}
Expand Down

0 comments on commit d50a76f

Please sign in to comment.