() {
@Override
public RuntimeException createException(Throwable throwable, String message) {
- return new RuntimeException(message, throwable);
+ return new IllegalStateException(message, throwable);
}
};
/**
* Finds the implementation {@code Class} object for the given
- * factory type. If it fails and {@code tryFallback} is {@code true}
- * finds the {@code Class} object for the given default class name.
- * The arguments supplied must be used in order
- * Note the default class name may be needed even if fallback
- * is not to be attempted in order to check if requested type is fallback.
+ * factory type.
*
* This method is package private so that this code can be shared.
*
+ * @param factoryClass factory abstract class or interface to be found
* @return the {@code Class} object of the specified message factory;
- * may not be {@code null}
- *
- * @param factoryClass factory abstract class or interface to be found
- * @param defaultClassName the implementation class name, which is
- * to be used only if nothing else
- * is found; {@code null} to indicate
- * that there is no default class name
- * @param tryFallback whether to try the default class as a
- * fallback
- * @exception RuntimeException if there is no factory found
+ * may not be {@code null}
+ * @throws IllegalStateException if there is no factory found
*/
- static T find(Class factoryClass,
- String defaultClassName,
- boolean tryFallback) throws RuntimeException {
+ static T find(Class factoryClass) throws RuntimeException {
+ for (ClassLoader l : getClassLoaders(
+ Thread.class,
+ FactoryFinder.class,
+ System.class)) {
+ T f = find(factoryClass, l);
+ if (f != null) {
+ return f;
+ }
+ }
- ClassLoader tccl = ServiceLoaderUtil.contextClassLoader(EXCEPTION_HANDLER);
- String factoryId = factoryClass.getName();
+ throw EXCEPTION_HANDLER.createException((Throwable) null,
+ "Provider for " + factoryClass.getName() + " cannot be found");
+ }
+ static T find(Class factoryClass, ClassLoader loader) throws RuntimeException {
// Use the system property first
- String className = fromSystemProperty(factoryId);
+ String className = fromSystemProperty(factoryClass.getName());
if (className != null) {
- T result = newInstance(className, defaultClassName, tccl);
- if (result != null) {
- return result;
- }
- // try api loader
- result = newInstance(className, defaultClassName, FactoryFinder.class.getClassLoader());
+ T result = newInstance(className, factoryClass, loader);
if (result != null) {
return result;
}
@@ -76,6 +70,7 @@ static T find(Class factoryClass,
// standard services: java.util.ServiceLoader
T factory = ServiceLoaderUtil.firstByServiceLoader(
factoryClass,
+ loader,
logger,
EXCEPTION_HANDLER);
if (factory != null) {
@@ -83,33 +78,21 @@ static T find(Class factoryClass,
}
// handling Glassfish/OSGi (platform specific default)
- if (isOsgi()) {
- T result = lookupUsingOSGiServiceLoader(factoryId);
- if (result != null) {
- return result;
- }
+ T result = lookupUsingHk2ServiceLoader(factoryClass, loader);
+ if (result != null) {
+ return result;
}
- // If not found and fallback should not be tried, throw RuntimeException.
- if (!tryFallback) {
- throw new RuntimeException(
- "Provider for " + factoryId + " cannot be found", null);
- }
-
- // We didn't find the class through the usual means so try the default
- // (built in) factory if specified.
- if (defaultClassName == null) {
- throw new RuntimeException(
- "Provider for " + factoryId + " cannot be found", null);
- }
- return newInstance(defaultClassName, defaultClassName, tccl);
+ return null;
}
- private static T newInstance(String className, String defaultClassName, ClassLoader tccl) throws RuntimeException {
+ private static T newInstance(String className,
+ Class extends T> service, ClassLoader loader)
+ throws RuntimeException {
return ServiceLoaderUtil.newInstance(
className,
- defaultClassName,
- tccl,
+ service,
+ loader,
EXCEPTION_HANDLER);
}
@@ -138,31 +121,85 @@ private static void logFound(String value) {
}
}
- private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader";
+ private static Class>[] getHk2ServiceLoaderTargets(Class> factoryClass) {
+ ClassLoader[] loaders = getClassLoaders(Thread.class, factoryClass, System.class);
+
+ 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 (Exception | LinkageError ignored) {
+ } //GlassFish class loaders can throw undocumented exceptions
+ }
+ }
- private static boolean isOsgi() {
- try {
- Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
- return true;
- } catch (ClassNotFoundException ignored) {
+ if (classes.length != w) {
+ classes = Arrays.copyOf(classes, w);
}
- return false;
+ return classes;
}
@SuppressWarnings({"unchecked"})
- private static T lookupUsingOSGiServiceLoader(String factoryId) {
- try {
- // Use reflection to avoid having any dependency on HK2 ServiceLoader class
- Class> serviceClass = Class.forName(factoryId);
- Class>[] args = new Class>[]{serviceClass};
- Class> target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
- Method m = target.getMethod("lookupProviderInstances", Class.class);
- Iterator> iter = ((Iterable>) m.invoke(null, (Object[]) args)).iterator();
- return iter.hasNext() ? (T) iter.next() : null;
- } catch (Exception ignored) {
- // log and continue
- return null;
+ private static T lookupUsingHk2ServiceLoader(Class 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
+ }
}
+ return null;
}
+ private static ClassLoader[] getClassLoaders(final Class>... classes) {
+ return AccessController.doPrivileged(
+ new PrivilegedAction() {
+ @Override
+ public ClassLoader[] run() {
+ ClassLoader[] loaders = new ClassLoader[classes.length];
+ int w = 0;
+ for (Class> k : classes) {
+ ClassLoader cl = null;
+ if (k == Thread.class) {
+ try {
+ cl = Thread.currentThread().getContextClassLoader();
+ } catch (SecurityException ex) {
+ }
+ } else if (k == System.class) {
+ try {
+ cl = ClassLoader.getSystemClassLoader();
+ } catch (SecurityException ex) {
+ }
+ } else {
+ try {
+ cl = k.getClassLoader();
+ } catch (SecurityException ex) {
+ }
+ }
+
+ if (cl != null) {
+ loaders[w++] = cl;
+ }
+ }
+
+ if (loaders.length != w) {
+ loaders = Arrays.copyOf(loaders, w);
+ }
+ return loaders;
+ }
+ }
+ );
+ }
}
diff --git a/api/src/main/java/jakarta/activation/MailcapCommandMap.java b/api/src/main/java/jakarta/activation/MailcapCommandMap.java
index f3a186d..9974333 100644
--- a/api/src/main/java/jakarta/activation/MailcapCommandMap.java
+++ b/api/src/main/java/jakarta/activation/MailcapCommandMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -200,7 +200,7 @@ private MailcapRegistry loadResource(String name) {
} catch (IOException | SecurityException e) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: can't load " + name, e);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
"MailcapRegistry: can't load " + name, e);
@@ -251,7 +251,7 @@ private void loadAllResources(List v, String name) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: can't load " +
url, ioex);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
"MailcapRegistry: can't load " + name, e);
@@ -286,7 +286,7 @@ private MailcapRegistry loadFile(String name) {
if (LogSupport.isLoggable()) {
LogSupport.log("MailcapRegistry: can't load from file - " + name, e);
}
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
"MailcapRegistry: can't load " + name, e);
@@ -307,7 +307,7 @@ public MailcapCommandMap(String fileName) throws IOException {
if (DB[PROG] == null) {
try {
DB[PROG] = getImplementation().getByFileName(fileName);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
String message = "Cannot find or load an implementation for MailcapRegistryProvider. " +
"MailcapRegistry: can't load " + fileName;
if (LogSupport.isLoggable()) {
@@ -336,7 +336,7 @@ public MailcapCommandMap(InputStream is) {
DB[PROG] = getImplementation().getByInputStream(is);
} catch (IOException ex) {
// XXX - should throw it
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider." +
"MailcapRegistry: can't load InputStream", e);
@@ -537,7 +537,7 @@ public synchronized void addMailcap(String mail_cap) {
DB[PROG] = getImplementation().getInMemory();
}
DB[PROG].appendToMailcap(mail_cap);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MailcapRegistryProvider. " +
"MailcapRegistry: can't load", e);
@@ -696,15 +696,11 @@ private MailcapRegistryProvider getImplementation() {
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged(new PrivilegedAction() {
public MailcapRegistryProvider run() {
- return FactoryFinder.find(MailcapRegistryProvider.class,
- null,
- false);
+ return FactoryFinder.find(MailcapRegistryProvider.class);
}
});
} else {
- return FactoryFinder.find(MailcapRegistryProvider.class,
- null,
- false);
+ return FactoryFinder.find(MailcapRegistryProvider.class);
}
}
diff --git a/api/src/main/java/jakarta/activation/MimetypesFileTypeMap.java b/api/src/main/java/jakarta/activation/MimetypesFileTypeMap.java
index 716a865..82bcc5c 100644
--- a/api/src/main/java/jakarta/activation/MimetypesFileTypeMap.java
+++ b/api/src/main/java/jakarta/activation/MimetypesFileTypeMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -161,10 +161,7 @@ private MimeTypeRegistry loadResource(String name) {
} catch (IOException e) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: can't load " + name, e);
- } catch (SecurityException sex) {
- if (LogSupport.isLoggable())
- LogSupport.log("MimetypesFileTypeMap: can't load " + name, sex);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MimeTypeRegistryProvider." +
"MimeTypeRegistry: can't load " + name, e);
@@ -226,11 +223,7 @@ private void loadAllResources(Vector v, String name) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: can't load " +
url, ioex);
- } catch (SecurityException sex) {
- if (LogSupport.isLoggable())
- LogSupport.log("MimetypesFileTypeMap: can't load " +
- url, sex);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MimeTypeRegistryProvider." +
"MimeTypeRegistry: can't load " + url, e);
@@ -272,7 +265,7 @@ private MimeTypeRegistry loadFile(String name) {
if (LogSupport.isLoggable()) {
LogSupport.log("MimeTypeRegistry: can't load from file - " + name, e);
}
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MimeTypeRegistryProvider." +
"MimeTypeRegistry: can't load " + name, e);
@@ -292,7 +285,7 @@ public MimetypesFileTypeMap(String mimeTypeFileName) throws IOException {
this();
try {
DB[PROG] = getImplementation().getByFileName(mimeTypeFileName);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
String errorMessage = "Cannot find or load an implementation for MimeTypeRegistryProvider." +
"MimeTypeRegistry: can't load " + mimeTypeFileName;
if (LogSupport.isLoggable()) {
@@ -314,7 +307,7 @@ public MimetypesFileTypeMap(InputStream is) {
DB[PROG] = getImplementation().getByInputStream(is);
} catch (IOException ex) {
// XXX - really should throw it
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MimeTypeRegistryProvider." +
"MimeTypeRegistry: can't load InputStream", e);
@@ -334,7 +327,7 @@ public synchronized void addMimeTypes(String mime_types) {
DB[PROG] = getImplementation().getInMemory();
}
DB[PROG].appendToRegistry(mime_types);
- } catch (NoSuchElementException | ServiceConfigurationError e) {
+ } catch (NoSuchElementException | IllegalStateException | ServiceConfigurationError e) {
if (LogSupport.isLoggable()) {
LogSupport.log("Cannot find or load an implementation for MimeTypeRegistryProvider." +
"MimeTypeRegistry: can't add " + mime_types, e);
@@ -388,15 +381,11 @@ private MimeTypeRegistryProvider getImplementation() {
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged(new PrivilegedAction() {
public MimeTypeRegistryProvider run() {
- return FactoryFinder.find(MimeTypeRegistryProvider.class,
- null,
- false);
+ return FactoryFinder.find(MimeTypeRegistryProvider.class);
}
});
} else {
- return FactoryFinder.find(MimeTypeRegistryProvider.class,
- null,
- false);
+ return FactoryFinder.find(MimeTypeRegistryProvider.class);
}
}
diff --git a/api/src/main/java/jakarta/activation/ServiceLoaderUtil.java b/api/src/main/java/jakarta/activation/ServiceLoaderUtil.java
index 83ecf63..dfeabdf 100644
--- a/api/src/main/java/jakarta/activation/ServiceLoaderUtil.java
+++ b/api/src/main/java/jakarta/activation/ServiceLoaderUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -15,20 +15,22 @@
import java.util.logging.Logger;
/**
- * Shared ServiceLoader/FactoryFinder Utils shared among JAF, MAIL, SAAJ, JAXB and JAXWS
- * Class duplicated to all those projects.
+ * Shared ServiceLoader/FactoryFinder Utils. JAF and MAIL use the same loading
+ * logic of thread context class loader, calling class loader, and finally the
+ * system class loader.
*
* @author Miroslav.Kos@oracle.com
*/
class ServiceLoaderUtil {
static P firstByServiceLoader(Class
spiClass,
+ ClassLoader loader,
Logger logger,
ExceptionHandler handler) throws T {
logger.log(Level.FINE, "Using java.util.ServiceLoader to find {0}", spiClass.getName());
// service discovery
try {
- ServiceLoader serviceLoader = ServiceLoader.load(spiClass);
+ ServiceLoader
serviceLoader = ServiceLoader.load(spiClass, loader);
for (P impl : serviceLoader) {
logger.log(Level.FINE, "ServiceProvider loading Facility used; returning object [{0}]", impl.getClass().getName());
@@ -54,22 +56,19 @@ static void checkPackageAccess(String className) {
@SuppressWarnings({"unchecked"})
static
Class
nullSafeLoadClass(String className, ClassLoader classLoader) throws ClassNotFoundException {
- if (classLoader == null) {
- return (Class
) Class.forName(className);
- } else {
- return (Class
) classLoader.loadClass(className);
+ if (classLoader == null) { //Match behavior of ServiceLoader
+ classLoader = ClassLoader.getSystemClassLoader();
}
+ return (Class
) Class.forName(className, false, classLoader);
}
- // Returns instance of required class. It checks package access (security)
- // unless it is defaultClassname. It means if you are trying to instantiate
- // default implementation (fallback), pass the class name to both first and second parameter.
+ // Returns instance of required class. It checks package access (security).
static
P newInstance(String className,
- String defaultImplClassName, ClassLoader classLoader,
- final ExceptionHandler handler) throws T {
+ Class service, ClassLoader classLoader,
+ final ExceptionHandler handler) throws T {
try {
- Class cls = safeLoadClass(className, defaultImplClassName, classLoader);
- return cls.getConstructor().newInstance();
+ Class
cls = safeLoadClass(className, classLoader);
+ return service.cast(cls.getConstructor().newInstance());
} catch (ClassNotFoundException x) {
throw handler.createException(x, "Provider " + className + " not found");
} catch (Exception x) {
@@ -79,34 +78,14 @@ static
P newInstance(String className,
@SuppressWarnings({"unchecked"})
static
Class
safeLoadClass(String className,
- String defaultImplClassName,
- ClassLoader classLoader) throws ClassNotFoundException {
-
- try {
- checkPackageAccess(className);
- } catch (SecurityException se) {
- // anyone can access the platform default factory class without permission
- if (defaultImplClassName != null && defaultImplClassName.equals(className)) {
- return (Class
) Class.forName(className);
- }
- // not platform default implementation ...
- throw se;
- }
+ ClassLoader classLoader) throws ClassNotFoundException {
+ checkPackageAccess(className);
return nullSafeLoadClass(className, classLoader);
}
- static ClassLoader contextClassLoader(ExceptionHandler exceptionHandler) throws T {
- try {
- return Thread.currentThread().getContextClassLoader();
- } catch (Exception x) {
- throw exceptionHandler.createException(x, x.toString());
- }
- }
-
static abstract class ExceptionHandler {
public abstract T createException(Throwable throwable, String message);
}
-
}