javax.xml.bind.ContextFinder Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2003-2018 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.xml.bind;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class is package private and therefore is not exposed as part of the
* JAXB API.
*
* This code is designed to implement the JAXB 1.0 spec pluggability feature
*
* @author - Ryan Shoemaker, Sun Microsystems, Inc.
* @see JAXBContext
*/
class ContextFinder {
/**
* When JAXB is in J2SE, rt.jar has to have a JAXB implementation.
* However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext
* because if it has, it will take precedence over any file that applications have
* in their jar files.
*
*
* When the user bundles his own JAXB implementation, we'd like to use it, and we
* want the platform default to be used only when there's no other JAXB provider.
*
*
* For this reason, we have to hard-code the class name into the API.
*/
private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory";
// previous value of JAXBContext.JAXB_CONTEXT_FACTORY, using also this to ensure backwards compatibility
private static final String JAXB_CONTEXT_FACTORY_DEPRECATED = "javax.xml.bind.context.factory";
private static final Logger logger;
static {
logger = Logger.getLogger("javax.xml.bind");
try {
if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {
// disconnect the logger from a bigger framework (if any)
// and take the matters into our own hands
logger.setUseParentHandlers(false);
logger.setLevel(Level.ALL);
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
logger.addHandler(handler);
} else {
// don't change the setting of this logger
// to honor what other frameworks
// have done on configurations.
}
} catch (Throwable t) {
// just to be extra safe. in particular System.getProperty may throw
// SecurityException.
}
}
private static ServiceLoaderUtil.ExceptionHandler 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.
*/
private static Throwable handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
Throwable t = x.getTargetException();
if (t != null) {
if (t instanceof JAXBException)
// one of our exceptions, just re-throw
throw (JAXBException) t;
if (t instanceof RuntimeException)
// avoid wrapping exceptions unnecessarily
throw (RuntimeException) t;
if (t instanceof Error)
throw (Error) t;
return t;
}
return x;
}
/**
* Determine if two types (JAXBContext in this case) will generate a ClassCastException.
*
* For example, (targetType)originalType
*
* @param originalType
* The Class object of the type being cast
* @param targetType
* 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
// class' ClassLoader
getClassClassLoader(originalType).getResource("javax/xml/bind/JAXBContext.class"),
targetTypeURL));
}
/**
* Create an instance of a class using the specified ClassLoader
*/
static JAXBContext newInstance(String contextPath,
Class[] contextPathClasses,
String className,
ClassLoader classLoader,
Map properties) throws JAXBException {
try {
Class spFactory = ServiceLoaderUtil.safeLoadClass(className, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader);
return newInstance(contextPath, contextPathClasses, spFactory, classLoader, properties);
} catch (ClassNotFoundException x) {
throw new JAXBException(Messages.format(Messages.DEFAULT_PROVIDER_NOT_FOUND), x);
} catch (RuntimeException | JAXBException x) {
// avoid wrapping RuntimeException to JAXBException,
// because it indicates a bug in this code.
// JAXBException re-thrown as is
throw x;
} catch (Exception x) {
// can't catch JAXBException because the method is hidden behind
// 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 properties) throws JAXBException {
try {
ModuleUtil.delegateAddOpensToImplModule(contextPathClasses, spFactory);
/*
* javax.xml.bind.context.factory points to a class which has a
* static method called 'createContext' that
* returns a javax.xml.JAXBContext.
*/
Object context = null;
// first check the method that takes Map as the third parameter.
// this is added in 2.0.
try {
Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class, Map.class);
// any failure in invoking this method would be considered fatal
Object obj = instantiateProviderIfNecessary(spFactory);
context = m.invoke(obj, contextPath, classLoader, properties);
} catch (NoSuchMethodException ignored) {
// it's not an error for the provider not to have this method.
}
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.
Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class);
Object obj = instantiateProviderIfNecessary(spFactory);
// any failure in invoking this method would be considered fatal
context = m.invoke(obj, contextPath, classLoader);
}
if (!(context instanceof JAXBContext)) {
// the cast would fail, so generate an exception with a nice message
throw handleClassCastException(context.getClass(), JAXBContext.class);
}
return (JAXBContext) context;
} catch (InvocationTargetException x) {
// throw if it is exception not to be wrapped
// otherwise, wrap with a JAXBException
Throwable e = handleInvocationTargetException(x);
throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, e), e);
} catch (Exception x) {
// can't catch JAXBException because the method is hidden behind
// 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);
}
}
private static Object instantiateProviderIfNecessary(final Class> implClass) throws JAXBException {
try {
if (JAXBContextFactory.class.isAssignableFrom(implClass)) {
return AccessController.doPrivileged(new PrivilegedExceptionAction