Path: blob/aarch64-shenandoah-jdk8u272-b10/jaxws/src/share/jaxws_classes/javax/xml/bind/ContextFinder.java
38890 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.xml.bind;2627import java.util.Iterator;28import java.io.BufferedReader;29import java.io.IOException;30import java.io.InputStream;31import java.io.InputStreamReader;32import java.io.UnsupportedEncodingException;33import java.lang.reflect.InvocationTargetException;34import java.lang.reflect.Method;35import java.net.URL;36import java.util.Map;37import java.util.Properties;38import java.util.StringTokenizer;39import java.util.logging.ConsoleHandler;40import java.util.logging.Level;41import java.util.logging.Logger;42import java.security.AccessController;4344import static javax.xml.bind.JAXBContext.JAXB_CONTEXT_FACTORY;454647/**48* This class is package private and therefore is not exposed as part of the49* JAXB API.50*51* This code is designed to implement the JAXB 1.0 spec pluggability feature52*53* @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>54* @see JAXBContext55*/56class ContextFinder {57private static final Logger logger;58static {59logger = Logger.getLogger("javax.xml.bind");60try {61if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {62// disconnect the logger from a bigger framework (if any)63// and take the matters into our own hands64logger.setUseParentHandlers(false);65logger.setLevel(Level.ALL);66ConsoleHandler handler = new ConsoleHandler();67handler.setLevel(Level.ALL);68logger.addHandler(handler);69} else {70// don't change the setting of this logger71// to honor what other frameworks72// have done on configurations.73}74} catch(Throwable t) {75// just to be extra safe. in particular System.getProperty may throw76// SecurityException.77}78}7980/**81* If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,82* throw the wrapped exception.83*/84private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException {85Throwable t = x.getTargetException();86if( t != null ) {87if( t instanceof JAXBException )88// one of our exceptions, just re-throw89throw (JAXBException)t;90if( t instanceof RuntimeException )91// avoid wrapping exceptions unnecessarily92throw (RuntimeException)t;93if( t instanceof Error )94throw (Error)t;95}96}979899/**100* Determine if two types (JAXBContext in this case) will generate a ClassCastException.101*102* For example, (targetType)originalType103*104* @param originalType105* The Class object of the type being cast106* @param targetType107* The Class object of the type that is being cast to108* @return JAXBException to be thrown.109*/110private static JAXBException handleClassCastException(Class originalType, Class targetType) {111final URL targetTypeURL = which(targetType);112113return new JAXBException(Messages.format(Messages.ILLEGAL_CAST,114// we don't care where the impl class is, we want to know where JAXBContext lives in the impl115// class' ClassLoader116getClassClassLoader(originalType).getResource("javax/xml/bind/JAXBContext.class"),117targetTypeURL));118}119120/**121* Create an instance of a class using the specified ClassLoader122*/123static JAXBContext newInstance( String contextPath,124String className,125ClassLoader classLoader,126Map properties )127throws JAXBException {128try {129Class spFactory = safeLoadClass(className,classLoader);130return newInstance(contextPath, spFactory, classLoader, properties);131} catch (ClassNotFoundException x) {132throw new JAXBException(133Messages.format( Messages.PROVIDER_NOT_FOUND, className ),134x);135} catch (RuntimeException x) {136// avoid wrapping RuntimeException to JAXBException,137// because it indicates a bug in this code.138throw x;139} catch (Exception x) {140// can't catch JAXBException because the method is hidden behind141// reflection. Root element collisions detected in the call to142// createContext() are reported as JAXBExceptions - just re-throw it143// some other type of exception - just wrap it144throw new JAXBException(145Messages.format( Messages.COULD_NOT_INSTANTIATE, className, x ),146x);147}148}149150static JAXBContext newInstance( String contextPath,151Class spFactory,152ClassLoader classLoader,153Map properties )154throws JAXBException155{156try {157/*158* javax.xml.bind.context.factory points to a class which has a159* static method called 'createContext' that160* returns a javax.xml.JAXBContext.161*/162163Object context = null;164165// first check the method that takes Map as the third parameter.166// this is added in 2.0.167try {168Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class,Map.class);169// any failure in invoking this method would be considered fatal170context = m.invoke(null,contextPath,classLoader,properties);171} catch (NoSuchMethodException e) {172// it's not an error for the provider not to have this method.173}174175if(context==null) {176// try the old method that doesn't take properties. compatible with 1.0.177// it is an error for an implementation not to have both forms of the createContext method.178Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class);179// any failure in invoking this method would be considered fatal180context = m.invoke(null,contextPath,classLoader);181}182183if(!(context instanceof JAXBContext)) {184// the cast would fail, so generate an exception with a nice message185throw handleClassCastException(context.getClass(), JAXBContext.class);186}187return (JAXBContext)context;188} catch (InvocationTargetException x) {189handleInvocationTargetException(x);190// for other exceptions, wrap the internal target exception191// with a JAXBException192Throwable e = x;193if(x.getTargetException()!=null)194e = x.getTargetException();195196throw new JAXBException( Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, e ), e );197} catch (RuntimeException x) {198// avoid wrapping RuntimeException to JAXBException,199// because it indicates a bug in this code.200throw x;201} catch (Exception x) {202// can't catch JAXBException because the method is hidden behind203// reflection. Root element collisions detected in the call to204// createContext() are reported as JAXBExceptions - just re-throw it205// some other type of exception - just wrap it206throw new JAXBException(207Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, x ),208x);209}210}211212213/**214* Create an instance of a class using the thread context ClassLoader215*/216static JAXBContext newInstance(217Class[] classes,218Map properties,219String className) throws JAXBException {220ClassLoader cl = getContextClassLoader();221Class spi;222try {223spi = safeLoadClass(className,cl);224} catch (ClassNotFoundException e) {225throw new JAXBException(e);226}227228if(logger.isLoggable(Level.FINE)) {229// extra check to avoid costly which operation if not logged230logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)});231}232233return newInstance(classes, properties, spi);234}235236static JAXBContext newInstance(Class[] classes,237Map properties,238Class spFactory) throws JAXBException {239Method m;240try {241m = spFactory.getMethod("createContext", Class[].class, Map.class);242} catch (NoSuchMethodException e) {243throw new JAXBException(e);244}245try {246Object context = m.invoke(null, classes, properties);247if(!(context instanceof JAXBContext)) {248// the cast would fail, so generate an exception with a nice message249throw handleClassCastException(context.getClass(), JAXBContext.class);250}251return (JAXBContext)context;252} catch (IllegalAccessException e) {253throw new JAXBException(e);254} catch (InvocationTargetException e) {255handleInvocationTargetException(e);256257Throwable x = e;258if (e.getTargetException() != null)259x = e.getTargetException();260261throw new JAXBException(x);262}263}264265static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties ) throws JAXBException {266267// TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP?268269final String jaxbContextFQCN = JAXBContext.class.getName();270271// search context path for jaxb.properties first272StringBuilder propFileName;273StringTokenizer packages = new StringTokenizer( contextPath, ":" );274String factoryClassName;275276if(!packages.hasMoreTokens())277// no context is specified278throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));279280281logger.fine("Searching jaxb.properties");282283while( packages.hasMoreTokens() ) {284String packageName = packages.nextToken(":").replace('.','/');285// com.acme.foo - > com/acme/foo/jaxb.properties286propFileName = new StringBuilder().append(packageName).append("/jaxb.properties");287288Properties props = loadJAXBProperties( classLoader, propFileName.toString() );289if (props != null) {290if (props.containsKey(factoryId)) {291factoryClassName = props.getProperty(factoryId);292return newInstance( contextPath, factoryClassName, classLoader, properties );293} else {294throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId));295}296}297}298299logger.fine("Searching the system property");300301// search for a system property second (javax.xml.bind.JAXBContext)302factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY));303if( factoryClassName != null ) {304return newInstance( contextPath, factoryClassName, classLoader, properties );305} else { // leave this here to assure compatibility306factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));307if( factoryClassName != null ) {308return newInstance( contextPath, factoryClassName, classLoader, properties );309}310}311312// OSGi search313Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader();314if (jaxbContext != null) {315logger.fine("OSGi environment detected");316return newInstance(contextPath, jaxbContext, classLoader, properties);317}318319logger.fine("Searching META-INF/services");320// search META-INF services next321BufferedReader r = null;322try {323final StringBuilder resource = new StringBuilder().append("META-INF/services/").append(jaxbContextFQCN);324final InputStream resourceStream =325classLoader.getResourceAsStream(resource.toString());326327if (resourceStream != null) {328r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8"));329factoryClassName = r.readLine();330if (factoryClassName != null) {331factoryClassName = factoryClassName.trim();332}333r.close();334return newInstance(contextPath, factoryClassName, classLoader, properties);335} else {336logger.log(Level.FINE, "Unable to load:{0}", resource.toString());337}338} catch (UnsupportedEncodingException e) {339// should never happen340throw new JAXBException(e);341} catch (IOException e) {342throw new JAXBException(e);343} finally {344try {345if (r != null) {346r.close();347}348} catch (IOException ex) {349Logger.getLogger(ContextFinder.class.getName()).log(Level.SEVERE, null, ex);350}351}352353// else no provider found354logger.fine("Trying to create the platform default provider");355return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties);356}357358static JAXBContext find( Class[] classes, Map properties ) throws JAXBException {359360final String jaxbContextFQCN = JAXBContext.class.getName();361String factoryClassName;362363// search for jaxb.properties in the class loader of each class first364for (final Class c : classes) {365// this classloader is used only to load jaxb.properties, so doing this should be safe.366ClassLoader classLoader = getClassClassLoader(c);367Package pkg = c.getPackage();368if(pkg==null)369continue; // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders370String packageName = pkg.getName().replace('.', '/');371372// TODO: do we want to optimize away searching the same package? org.Foo, org.Bar, com.Baz373// classes from the same package might come from different class loades, so it might be a bad idea374375// TODO: it's easier to look things up from the class376// c.getResourceAsStream("jaxb.properties");377378// build the resource name and use the property loader code379String resourceName = packageName+"/jaxb.properties";380logger.log(Level.FINE, "Trying to locate {0}", resourceName);381Properties props = loadJAXBProperties(classLoader, resourceName);382if (props == null) {383logger.fine(" not found");384} else {385logger.fine(" found");386if (props.containsKey(JAXB_CONTEXT_FACTORY)) {387// trim() seems redundant, but adding to satisfy customer complaint388factoryClassName = props.getProperty(JAXB_CONTEXT_FACTORY).trim();389return newInstance(classes, properties, factoryClassName);390} else {391throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, JAXB_CONTEXT_FACTORY));392}393}394}395396// search for a system property second (javax.xml.bind.JAXBContext)397logger.log(Level.FINE, "Checking system property {0}", JAXBContext.JAXB_CONTEXT_FACTORY);398factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY));399if (factoryClassName != null) {400logger.log(Level.FINE, " found {0}", factoryClassName);401return newInstance( classes, properties, factoryClassName );402} else { // leave it here for compatibility reasons403logger.fine(" not found");404logger.log(Level.FINE, "Checking system property {0}", jaxbContextFQCN);405factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));406if (factoryClassName != null) {407logger.log(Level.FINE, " found {0}", factoryClassName);408return newInstance( classes, properties, factoryClassName );409} else {410logger.fine(" not found");411}412}413414// OSGi search415Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader();416if (jaxbContext != null) {417logger.fine("OSGi environment detected");418return newInstance(classes, properties, jaxbContext);419}420421// search META-INF services next422logger.fine("Checking META-INF/services");423BufferedReader r = null;424try {425final String resource = new StringBuilder("META-INF/services/").append(jaxbContextFQCN).toString();426ClassLoader classLoader = getContextClassLoader();427URL resourceURL;428if(classLoader==null)429resourceURL = ClassLoader.getSystemResource(resource);430else431resourceURL = classLoader.getResource(resource);432433if (resourceURL != null) {434logger.log(Level.FINE, "Reading {0}", resourceURL);435r = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "UTF-8"));436factoryClassName = r.readLine();437if (factoryClassName != null) {438factoryClassName = factoryClassName.trim();439}440return newInstance(classes, properties, factoryClassName);441} else {442logger.log(Level.FINE, "Unable to find: {0}", resource);443}444} catch (UnsupportedEncodingException e) {445// should never happen446throw new JAXBException(e);447} catch (IOException e) {448throw new JAXBException(e);449} finally {450if (r != null) {451try {452r.close();453} catch (IOException ex) {454logger.log(Level.FINE, "Unable to close stream", ex);455}456}457}458459// else no provider found460logger.fine("Trying to create the platform default provider");461return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS);462}463464private static Class lookupJaxbContextUsingOsgiServiceLoader() {465try {466// Use reflection to avoid having any dependency on ServiceLoader class467Class target = Class.forName("com.sun.org.glassfish.hk2.osgiresourcelocator.ServiceLoader");468Method m = target.getMethod("lookupProviderClasses", Class.class);469Iterator iter = ((Iterable) m.invoke(null, JAXBContext.class)).iterator();470return iter.hasNext() ? (Class)iter.next() : null;471} catch(Exception e) {472logger.log(Level.FINE, "Unable to find from OSGi: javax.xml.bind.JAXBContext");473return null;474}475}476477private static Properties loadJAXBProperties( ClassLoader classLoader,478String propFileName )479throws JAXBException {480481Properties props = null;482483try {484URL url;485if(classLoader==null)486url = ClassLoader.getSystemResource(propFileName);487else488url = classLoader.getResource( propFileName );489490if( url != null ) {491logger.log(Level.FINE, "loading props from {0}", url);492props = new Properties();493InputStream is = url.openStream();494props.load( is );495is.close();496}497} catch( IOException ioe ) {498logger.log(Level.FINE,"Unable to load "+propFileName,ioe);499throw new JAXBException( ioe.toString(), ioe );500}501502return props;503}504505506/**507* Search the given ClassLoader for an instance of the specified class and508* return a string representation of the URL that points to the resource.509*510* @param clazz511* The class to search for512* @param loader513* The ClassLoader to search. If this parameter is null, then the514* system class loader will be searched515* @return516* the URL for the class or null if it wasn't found517*/518static URL which(Class clazz, ClassLoader loader) {519520String classnameAsResource = clazz.getName().replace('.', '/') + ".class";521522if(loader == null) {523loader = getSystemClassLoader();524}525526return loader.getResource(classnameAsResource);527}528529/**530* Get the URL for the Class from it's ClassLoader.531*532* Convenience method for {@link #which(Class, ClassLoader)}.533*534* Equivalent to calling: which(clazz, clazz.getClassLoader())535*536* @param clazz537* The class to search for538* @return539* the URL for the class or null if it wasn't found540*/541static URL which(Class clazz) {542return which(clazz, getClassClassLoader(clazz));543}544545/**546* When JAXB is in J2SE, rt.jar has to have a JAXB implementation.547* However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext548* because if it has, it will take precedence over any file that applications have549* in their jar files.550*551* <p>552* When the user bundles his own JAXB implementation, we'd like to use it, and we553* want the platform default to be used only when there's no other JAXB provider.554*555* <p>556* For this reason, we have to hard-code the class name into the API.557*/558private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory";559560/**561* Loads the class, provided that the calling thread has an access to the class being loaded.562*/563private static Class safeLoadClass(String className, ClassLoader classLoader) throws ClassNotFoundException {564logger.log(Level.FINE, "Trying to load {0}", className);565try {566// make sure that the current thread has an access to the package of the given name.567SecurityManager s = System.getSecurityManager();568if (s != null) {569int i = className.lastIndexOf('.');570if (i != -1) {571s.checkPackageAccess(className.substring(0,i));572}573}574575if (classLoader == null) {576return Class.forName(className);577} else {578return classLoader.loadClass(className);579}580} catch (SecurityException se) {581// anyone can access the platform default factory class without permission582if (PLATFORM_DEFAULT_FACTORY_CLASS.equals(className)) {583return Class.forName(className);584}585throw se;586}587}588589private static ClassLoader getContextClassLoader() {590if (System.getSecurityManager() == null) {591return Thread.currentThread().getContextClassLoader();592} else {593return (ClassLoader) java.security.AccessController.doPrivileged(594new java.security.PrivilegedAction() {595public java.lang.Object run() {596return Thread.currentThread().getContextClassLoader();597}598});599}600}601602private static ClassLoader getClassClassLoader(final Class c) {603if (System.getSecurityManager() == null) {604return c.getClassLoader();605} else {606return (ClassLoader) java.security.AccessController.doPrivileged(607new java.security.PrivilegedAction() {608public java.lang.Object run() {609return c.getClassLoader();610}611});612}613}614615private static ClassLoader getSystemClassLoader() {616if (System.getSecurityManager() == null) {617return ClassLoader.getSystemClassLoader();618} else {619return (ClassLoader) java.security.AccessController.doPrivileged(620new java.security.PrivilegedAction() {621public java.lang.Object run() {622return ClassLoader.getSystemClassLoader();623}624});625}626}627628}629630631