Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 79 additions & 66 deletions api/src/main/java/jakarta/xml/bind/ContextFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;


/**
* This class is package private and therefore is not exposed as part of the
* Jakarta XML Binding API.
* <p>
* This code is designed to implement the XML Binding spec pluggability feature
*
* @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
* @author
* <ul>
* <li>Ryan Shoemaker, Sun Microsystems, Inc.</li>
* </ul>
* @see JAXBContext
*/
class ContextFinder {
Expand All @@ -41,17 +43,20 @@ class ContextFinder {
/**
* When JAXB is in J2SE, rt.jar has to have a JAXB implementation.
* However, rt.jar cannot have META-INF/services/jakarta.xml.bind.JAXBContext
* because if it has, it will take precedence over any file that applications have
* because if it has, it will take precedence over any file that applications
* have
* in their jar files.
*
* <p>
* When the user bundles his own Jakarta XML Binding implementation, we'd like to use it, and we
* want the platform default to be used only when there's no other Jakarta XML Binding provider.
* When the user bundles his own Jakarta XML Binding implementation, we'd like
* to use it, and we
* want the platform default to be used only when there's no other Jakarta XML
* Binding provider.
*
* <p>
* For this reason, we have to hard-code the class name into the API.
*/
//XXX: should we define and rely on "default" in jakarta?
// XXX: should we define and rely on "default" in jakarta?
static final String DEFAULT_FACTORY_CLASS = "org.glassfish.jaxb.runtime.v2.ContextFactory";

static {
Expand All @@ -76,17 +81,18 @@ class ContextFinder {
}
}

private static ServiceLoaderUtil.ExceptionHandler<JAXBException> EXCEPTION_HANDLER =
new ServiceLoaderUtil.ExceptionHandler<>() {
@Override
public JAXBException createException(Throwable throwable, String message) {
return new JAXBException(message, throwable);
}
};
private static ServiceLoaderUtil.ExceptionHandler<JAXBException> EXCEPTION_HANDLER = new ServiceLoaderUtil.ExceptionHandler<>() {
@Override
public JAXBException createException(Throwable throwable, String message) {
return new JAXBException(message, throwable);
}
};

/**
* If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
* throw the wrapped exception. Otherwise, returns exception to be wrapped for further processing.
* If the {@link InvocationTargetException} wraps an exception that shouldn't be
* wrapped,
* throw the wrapped exception. Otherwise, returns exception to be wrapped for
* further processing.
*/
private static Throwable handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
Throwable t = x.getTargetException();
Expand All @@ -104,23 +110,24 @@ private static Throwable handleInvocationTargetException(InvocationTargetExcepti
return x;
}


/**
* Determine if two types (JAXBContext in this case) will generate a ClassCastException.
* Determine if two types (JAXBContext in this case) will generate a
* ClassCastException.
* <p>
* For example, (targetType)originalType
*
* @param originalType
* The Class object of the type being cast
* The Class object of the type being cast
* @param targetType
* The Class object of the type that is being cast to
* The Class object of the type that is being cast to
* @return JAXBException to be thrown.
*/
private static JAXBException handleClassCastException(Class<?> originalType, Class<?> targetType) {
final URL targetTypeURL = which(targetType);

return new JAXBException(Messages.format(Messages.ILLEGAL_CAST,
// we don't care where the impl class is, we want to know where JAXBContext lives in the impl
// we don't care where the impl class is, we want to know where JAXBContext
// lives in the impl
// class' ClassLoader
getClassClassLoader(originalType).getResource("jakarta/xml/bind/JAXBContext.class"),
targetTypeURL));
Expand All @@ -130,10 +137,10 @@ private static JAXBException handleClassCastException(Class<?> originalType, Cla
* Create an instance of a class using the specified ClassLoader
*/
static JAXBContext newInstance(String contextPath,
Class<?>[] contextPathClasses,
String className,
ClassLoader classLoader,
Map<String, ?> properties) throws JAXBException {
Class<?>[] contextPathClasses,
String className,
ClassLoader classLoader,
Map<String, ?> properties) throws JAXBException {

try {
Class<?> spFactory = ServiceLoaderUtil.safeLoadClass(className, DEFAULT_FACTORY_CLASS, classLoader);
Expand All @@ -148,18 +155,18 @@ static JAXBContext newInstance(String contextPath,
throw x;
} catch (Exception x) {
// can't catch JAXBException because the method is hidden behind
// reflection. Root element collisions detected in the call to
// reflection. Root element collisions detected in the call to
// createContext() are reported as JAXBExceptions - just re-throw it
// some other type of exception - just wrap it
throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, className, x), x);
}
}

static JAXBContext newInstance(String contextPath,
Class<?>[] contextPathClasses,
Class<?> spFactory,
ClassLoader classLoader,
Map<String, ?> properties) throws JAXBException {
Class<?>[] contextPathClasses,
Class<?> spFactory,
ClassLoader classLoader,
Map<String, ?> properties) throws JAXBException {

try {

Expand All @@ -186,7 +193,8 @@ static JAXBContext newInstance(String contextPath,

if (context == null) {
// try the old method that doesn't take properties. compatible with 1.0.
// it is an error for an implementation not to have both forms of the createContext method.
// it is an error for an implementation not to have both forms of the
// createContext method.
Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class);
Object obj = instantiateProviderIfNecessary(spFactory);
// any failure in invoking this method would be considered fatal
Expand All @@ -207,7 +215,7 @@ static JAXBContext newInstance(String contextPath,

} catch (Exception x) {
// can't catch JAXBException because the method is hidden behind
// reflection. Root element collisions detected in the call to
// reflection. Root element collisions detected in the call to
// createContext() are reported as JAXBExceptions - just re-throw it
// some other type of exception - just wrap it
throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, x), x);
Expand All @@ -231,17 +239,11 @@ public Object run() throws Exception {
}
}

/**
* Create an instance of a class using the thread context ClassLoader
*/
private static JAXBContext newInstance(Class<?>[] classes, Map<String, ?> properties, String className) throws JAXBException {
return newInstance(classes, properties, className, getContextClassLoader());
}

/**
* Create an instance of a class using passed in ClassLoader
*/
private static JAXBContext newInstance(Class<?>[] classes, Map<String, ?> properties, String className, ClassLoader loader) throws JAXBException {
private static JAXBContext newInstance(Class<?>[] classes, Map<String, ?> properties, String className,
ClassLoader loader) throws JAXBException {

Class<?> spi;
try {
Expand All @@ -252,17 +254,17 @@ private static JAXBContext newInstance(Class<?>[] classes, Map<String, ?> proper

if (logger.isLoggable(Level.FINE)) {
// extra check to avoid costly which operation if not logged
logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)});
logger.log(Level.FINE, "loaded {0} from {1}", new Object[] { className, which(spi) });
}

return newInstance(classes, properties, spi);
}

static JAXBContext newInstance(Class<?>[] classes,
Map<String, ?> properties,
Class<?> spFactory) throws JAXBException {
Map<String, ?> properties,
Class<?> spFactory) throws JAXBException {
try {
ModuleUtil.delegateAddOpensToImplModule(classes, spFactory);
ModuleUtil.delegateAddOpensToImplModule(classes, spFactory);

Method m = spFactory.getMethod("createContext", Class[].class, Map.class);
Object obj = instantiateProviderIfNecessary(spFactory);
Expand All @@ -285,37 +287,41 @@ static JAXBContext newInstance(Class<?>[] classes,
}

static JAXBContext find(String factoryId,
String contextPath,
ClassLoader classLoader,
Map<String, ?> properties) throws JAXBException {
String contextPath,
ClassLoader classLoader,
Map<String, ?> properties) throws JAXBException {

if (contextPath == null || contextPath.isEmpty()) {
// no context is specified
throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));
}

//ModuleUtil is mr-jar class, scans context path for jaxb classes on jdk9 and higher
// ModuleUtil is mr-jar class, scans context path for jaxb classes on jdk9 and
// higher
Class<?>[] contextPathClasses = ModuleUtil.getClassesFromContextPath(contextPath, classLoader);

String factoryName = classNameFromSystemProperties();
if (factoryName != null) return newInstance(contextPath, contextPathClasses, factoryName, classLoader, properties);
if (factoryName != null)
return newInstance(contextPath, contextPathClasses, factoryName, classLoader, properties);

if (properties != null) {
Object factory = properties.get(factoryId);
if (factory != null) {
if (factory instanceof String) {
factoryName = (String) factory;
} else {
throw new JAXBException(Messages.format(Messages.ILLEGAL_CAST, factory.getClass().getName(), "String"));
throw new JAXBException(
Messages.format(Messages.ILLEGAL_CAST, factory.getClass().getName(), "String"));
}
}
if (factoryName != null) {
return newInstance(contextPath, contextPathClasses, factoryName, classLoader, cleanProperties(properties));
return newInstance(contextPath, contextPathClasses, factoryName, classLoader,
cleanProperties(properties));
}
}

JAXBContextFactory obj = ServiceLoaderUtil.firstByServiceLoader(
JAXBContextFactory.class, logger, EXCEPTION_HANDLER);
JAXBContextFactory.class, logger, EXCEPTION_HANDLER, classLoader);

if (obj != null) {
ModuleUtil.delegateAddOpensToImplModule(contextPathClasses, obj.getClass());
Expand All @@ -341,42 +347,49 @@ static JAXBContext find(String factoryId,
}

static JAXBContext find(Class<?>[] classes, Map<String, ?> properties) throws JAXBException {
return find(classes, getContextClassLoader(), properties);
}

static JAXBContext find(Class<?>[] classes, ClassLoader classLoader, Map<String, ?> properties)
throws JAXBException {
String factoryClassName = classNameFromSystemProperties();
if (factoryClassName != null) return newInstance(classes, properties, factoryClassName);
if (factoryClassName != null)
return newInstance(classes, properties, factoryClassName, classLoader);

if (properties != null) {
Object ctxFactory = properties.get(JAXBContext.JAXB_CONTEXT_FACTORY);
if (ctxFactory != null) {
if (ctxFactory instanceof String) {
factoryClassName = (String) ctxFactory;
} else {
throw new JAXBException(Messages.format(Messages.ILLEGAL_CAST, ctxFactory.getClass().getName(), "String"));
throw new JAXBException(
Messages.format(Messages.ILLEGAL_CAST, ctxFactory.getClass().getName(), "String"));
}
}
if (factoryClassName != null) {
return newInstance(classes, cleanProperties(properties), factoryClassName);
return newInstance(classes, cleanProperties(properties), factoryClassName, classLoader);
}
}

JAXBContextFactory factory =
ServiceLoaderUtil.firstByServiceLoader(JAXBContextFactory.class, logger, EXCEPTION_HANDLER);
JAXBContextFactory factory = ServiceLoaderUtil.firstByServiceLoader(JAXBContextFactory.class, logger,
EXCEPTION_HANDLER, classLoader);

if (factory != null) {
ModuleUtil.delegateAddOpensToImplModule(classes, factory.getClass());
return factory.createContext(classes, properties);
}

logger.fine("Trying to create the platform default provider");
Class<?> ctxFactoryClass =
ServiceLoaderUtil.lookupUsingOSGiServiceLoader(JAXBContext.JAXB_CONTEXT_FACTORY, logger);
Class<?> ctxFactoryClass = ServiceLoaderUtil.lookupUsingOSGiServiceLoader(JAXBContext.JAXB_CONTEXT_FACTORY,
logger);

if (ctxFactoryClass != null) {
return newInstance(classes, properties, ctxFactoryClass);
return newInstance(classes, properties, ctxFactoryClass.toString(), classLoader);
}

// else no provider found
logger.fine("Trying to create the platform default provider");
return newInstance(classes, properties, DEFAULT_FACTORY_CLASS);
return newInstance(classes, properties, DEFAULT_FACTORY_CLASS, classLoader);
}

private static String classNameFromSystemProperties() throws JAXBException {
Expand Down Expand Up @@ -415,12 +428,12 @@ private static String getSystemProperty(String property) {
* return a string representation of the URL that points to the resource.
*
* @param clazz
* The class to search for
* The class to search for
* @param loader
* The ClassLoader to search. If this parameter is null, then the
* system class loader will be searched
* The ClassLoader to search. If this parameter is null, then the
* system class loader will be searched
* @return
* the URL for the class or null if it wasn't found
* the URL for the class or null if it wasn't found
*/
static URL which(Class<?> clazz, ClassLoader loader) {

Expand All @@ -441,9 +454,9 @@ static URL which(Class<?> clazz, ClassLoader loader) {
* Equivalent to calling: which(clazz, clazz.getClassLoader())
*
* @param clazz
* The class to search for
* The class to search for
* @return
* the URL for the class or null if it wasn't found
* the URL for the class or null if it wasn't found
*/
static URL which(Class<?> clazz) {
return which(clazz, getClassClassLoader(clazz));
Expand Down
Loading