src/share/jaxws_classes/javax/xml/bind/ContextFinder.java

Tue, 07 Nov 2017 18:54:04 -0800

author
asaha
date
Tue, 07 Nov 2017 18:54:04 -0800
changeset 1528
f453f4eaf8b4
parent 397
b99d7e355d4b
child 637
9c07ef4934dd
permissions
-rw-r--r--

Added tag jdk8u162-b06 for changeset 6095742f8034

     1 /*
     2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package javax.xml.bind;
    28 import java.util.Iterator;
    29 import java.io.BufferedReader;
    30 import java.io.IOException;
    31 import java.io.InputStream;
    32 import java.io.InputStreamReader;
    33 import java.io.UnsupportedEncodingException;
    34 import java.lang.reflect.InvocationTargetException;
    35 import java.lang.reflect.Method;
    36 import java.net.URL;
    37 import java.util.Map;
    38 import java.util.Properties;
    39 import java.util.StringTokenizer;
    40 import java.util.logging.ConsoleHandler;
    41 import java.util.logging.Level;
    42 import java.util.logging.Logger;
    43 import java.security.AccessController;
    45 import static javax.xml.bind.JAXBContext.JAXB_CONTEXT_FACTORY;
    48 /**
    49  * This class is package private and therefore is not exposed as part of the
    50  * JAXB API.
    51  *
    52  * This code is designed to implement the JAXB 1.0 spec pluggability feature
    53  *
    54  * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
    55  * @see JAXBContext
    56  */
    57 class ContextFinder {
    58     private static final Logger logger;
    59     static {
    60         logger = Logger.getLogger("javax.xml.bind");
    61         try {
    62             if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {
    63                 // disconnect the logger from a bigger framework (if any)
    64                 // and take the matters into our own hands
    65                 logger.setUseParentHandlers(false);
    66                 logger.setLevel(Level.ALL);
    67                 ConsoleHandler handler = new ConsoleHandler();
    68                 handler.setLevel(Level.ALL);
    69                 logger.addHandler(handler);
    70             } else {
    71                 // don't change the setting of this logger
    72                 // to honor what other frameworks
    73                 // have done on configurations.
    74             }
    75         } catch(Throwable t) {
    76             // just to be extra safe. in particular System.getProperty may throw
    77             // SecurityException.
    78         }
    79     }
    81     /**
    82      * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
    83      * throw the wrapped exception.
    84      */
    85     private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
    86         Throwable t = x.getTargetException();
    87         if( t != null ) {
    88             if( t instanceof JAXBException )
    89                 // one of our exceptions, just re-throw
    90                 throw (JAXBException)t;
    91             if( t instanceof RuntimeException )
    92                 // avoid wrapping exceptions unnecessarily
    93                 throw (RuntimeException)t;
    94             if( t instanceof Error )
    95                 throw (Error)t;
    96         }
    97     }
   100     /**
   101      * Determine if two types (JAXBContext in this case) will generate a ClassCastException.
   102      *
   103      * For example, (targetType)originalType
   104      *
   105      * @param originalType
   106      *          The Class object of the type being cast
   107      * @param targetType
   108      *          The Class object of the type that is being cast to
   109      * @return JAXBException to be thrown.
   110      */
   111     private static JAXBException handleClassCastException(Class originalType, Class targetType) {
   112         final URL targetTypeURL = which(targetType);
   114         return new JAXBException(Messages.format(Messages.ILLEGAL_CAST,
   115                 // we don't care where the impl class is, we want to know where JAXBContext lives in the impl
   116                 // class' ClassLoader
   117                 getClassClassLoader(originalType).getResource("javax/xml/bind/JAXBContext.class"),
   118                 targetTypeURL));
   119     }
   121     /**
   122      * Create an instance of a class using the specified ClassLoader
   123      */
   124     static JAXBContext newInstance( String contextPath,
   125                                String className,
   126                                ClassLoader classLoader,
   127                                Map properties )
   128         throws JAXBException {
   129         try {
   130             Class spFactory = safeLoadClass(className,classLoader);
   131             return newInstance(contextPath, spFactory, classLoader, properties);
   132         } catch (ClassNotFoundException x) {
   133             throw new JAXBException(
   134                 Messages.format( Messages.PROVIDER_NOT_FOUND, className ),
   135                 x);
   136         } catch (RuntimeException x) {
   137             // avoid wrapping RuntimeException to JAXBException,
   138             // because it indicates a bug in this code.
   139             throw x;
   140         } catch (Exception x) {
   141             // can't catch JAXBException because the method is hidden behind
   142             // reflection.  Root element collisions detected in the call to
   143             // createContext() are reported as JAXBExceptions - just re-throw it
   144             // some other type of exception - just wrap it
   145             throw new JAXBException(
   146                 Messages.format( Messages.COULD_NOT_INSTANTIATE, className, x ),
   147                 x);
   148         }
   149     }
   151     static JAXBContext newInstance( String contextPath,
   152                                Class spFactory,
   153                                ClassLoader classLoader,
   154                                Map properties )
   155         throws JAXBException
   156     {
   157         try {
   158             /*
   159              * javax.xml.bind.context.factory points to a class which has a
   160              * static method called 'createContext' that
   161              * returns a javax.xml.JAXBContext.
   162              */
   164             Object context = null;
   166             // first check the method that takes Map as the third parameter.
   167             // this is added in 2.0.
   168             try {
   169                 Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class,Map.class);
   170                 // any failure in invoking this method would be considered fatal
   171                 context = m.invoke(null,contextPath,classLoader,properties);
   172             } catch (NoSuchMethodException e) {
   173                 // it's not an error for the provider not to have this method.
   174             }
   176             if(context==null) {
   177                 // try the old method that doesn't take properties. compatible with 1.0.
   178                 // it is an error for an implementation not to have both forms of the createContext method.
   179                 Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class);
   180                 // any failure in invoking this method would be considered fatal
   181                 context = m.invoke(null,contextPath,classLoader);
   182             }
   184             if(!(context instanceof JAXBContext)) {
   185                 // the cast would fail, so generate an exception with a nice message
   186                 throw handleClassCastException(context.getClass(), JAXBContext.class);
   187             }
   188             return (JAXBContext)context;
   189         } catch (InvocationTargetException x) {
   190             handleInvocationTargetException(x);
   191             // for other exceptions, wrap the internal target exception
   192             // with a JAXBException
   193             Throwable e = x;
   194             if(x.getTargetException()!=null)
   195                 e = x.getTargetException();
   197             throw new JAXBException( Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, e ), e );
   198         } catch (RuntimeException x) {
   199             // avoid wrapping RuntimeException to JAXBException,
   200             // because it indicates a bug in this code.
   201             throw x;
   202         } catch (Exception x) {
   203             // can't catch JAXBException because the method is hidden behind
   204             // reflection.  Root element collisions detected in the call to
   205             // createContext() are reported as JAXBExceptions - just re-throw it
   206             // some other type of exception - just wrap it
   207             throw new JAXBException(
   208                 Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, x ),
   209                 x);
   210         }
   211     }
   214     /**
   215      * Create an instance of a class using the thread context ClassLoader
   216      */
   217     static JAXBContext newInstance(
   218                               Class[] classes,
   219                               Map properties,
   220                               String className) throws JAXBException {
   221         ClassLoader cl = getContextClassLoader();
   222         Class spi;
   223         try {
   224             spi = safeLoadClass(className,cl);
   225         } catch (ClassNotFoundException e) {
   226             throw new JAXBException(e);
   227         }
   229         if(logger.isLoggable(Level.FINE)) {
   230             // extra check to avoid costly which operation if not logged
   231             logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)});
   232         }
   234         return newInstance(classes, properties, spi);
   235     }
   237     static JAXBContext newInstance(Class[] classes,
   238                                    Map properties,
   239                                    Class spFactory) throws JAXBException {
   240         Method m;
   241         try {
   242             m = spFactory.getMethod("createContext", Class[].class, Map.class);
   243         } catch (NoSuchMethodException e) {
   244             throw new JAXBException(e);
   245         }
   246         try {
   247             Object context = m.invoke(null, classes, properties);
   248             if(!(context instanceof JAXBContext)) {
   249                 // the cast would fail, so generate an exception with a nice message
   250                 throw handleClassCastException(context.getClass(), JAXBContext.class);
   251             }
   252             return (JAXBContext)context;
   253         } catch (IllegalAccessException e) {
   254             throw new JAXBException(e);
   255         } catch (InvocationTargetException e) {
   256             handleInvocationTargetException(e);
   258             Throwable x = e;
   259             if (e.getTargetException() != null)
   260                 x = e.getTargetException();
   262             throw new JAXBException(x);
   263         }
   264     }
   266     static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties ) throws JAXBException {
   268         // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP?
   270         final String jaxbContextFQCN = JAXBContext.class.getName();
   272         // search context path for jaxb.properties first
   273         StringBuilder propFileName;
   274         StringTokenizer packages = new StringTokenizer( contextPath, ":" );
   275         String factoryClassName;
   277         if(!packages.hasMoreTokens())
   278             // no context is specified
   279             throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));
   282         logger.fine("Searching jaxb.properties");
   284         while( packages.hasMoreTokens() ) {
   285             String packageName = packages.nextToken(":").replace('.','/');
   286             // com.acme.foo - > com/acme/foo/jaxb.properties
   287              propFileName = new StringBuilder().append(packageName).append("/jaxb.properties");
   289             Properties props = loadJAXBProperties( classLoader, propFileName.toString() );
   290             if (props != null) {
   291                 if (props.containsKey(factoryId)) {
   292                     factoryClassName = props.getProperty(factoryId);
   293                     return newInstance( contextPath, factoryClassName, classLoader, properties );
   294                 } else {
   295                     throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId));
   296                 }
   297             }
   298         }
   300         logger.fine("Searching the system property");
   302         // search for a system property second (javax.xml.bind.JAXBContext)
   303         factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY));
   304         if(  factoryClassName != null ) {
   305             return newInstance( contextPath, factoryClassName, classLoader, properties );
   306         } else { // leave this here to assure compatibility
   307             factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));
   308             if(  factoryClassName != null ) {
   309                 return newInstance( contextPath, factoryClassName, classLoader, properties );
   310             }
   311         }
   313         // OSGi search
   314         Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader();
   315         if (jaxbContext != null) {
   316             logger.fine("OSGi environment detected");
   317             return newInstance(contextPath, jaxbContext, classLoader, properties);
   318         }
   320         logger.fine("Searching META-INF/services");
   321         // search META-INF services next
   322         BufferedReader r = null;
   323         try {
   324             final StringBuilder resource = new StringBuilder().append("META-INF/services/").append(jaxbContextFQCN);
   325             final InputStream resourceStream =
   326                     classLoader.getResourceAsStream(resource.toString());
   328             if (resourceStream != null) {
   329                 r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8"));
   330                 factoryClassName = r.readLine();
   331                 if (factoryClassName != null) {
   332                     factoryClassName = factoryClassName.trim();
   333                 }
   334                 r.close();
   335                 return newInstance(contextPath, factoryClassName, classLoader, properties);
   336             } else {
   337                 logger.log(Level.FINE, "Unable to load:{0}", resource.toString());
   338             }
   339         } catch (UnsupportedEncodingException e) {
   340             // should never happen
   341             throw new JAXBException(e);
   342         } catch (IOException e) {
   343             throw new JAXBException(e);
   344         } finally {
   345             try {
   346                 if (r != null) {
   347                     r.close();
   348                 }
   349             } catch (IOException ex) {
   350                 Logger.getLogger(ContextFinder.class.getName()).log(Level.SEVERE, null, ex);
   351             }
   352         }
   354         // else no provider found
   355         logger.fine("Trying to create the platform default provider");
   356         return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties);
   357     }
   359     static JAXBContext find( Class[] classes, Map properties ) throws JAXBException {
   361         final String jaxbContextFQCN = JAXBContext.class.getName();
   362         String factoryClassName;
   364         // search for jaxb.properties in the class loader of each class first
   365         for (final Class c : classes) {
   366             // this classloader is used only to load jaxb.properties, so doing this should be safe.
   367             ClassLoader classLoader = getClassClassLoader(c);
   368             Package pkg = c.getPackage();
   369             if(pkg==null)
   370                 continue;       // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders
   371             String packageName = pkg.getName().replace('.', '/');
   373             // TODO: do we want to optimize away searching the same package?  org.Foo, org.Bar, com.Baz
   374             //       classes from the same package might come from different class loades, so it might be a bad idea
   376             // TODO: it's easier to look things up from the class
   377             // c.getResourceAsStream("jaxb.properties");
   379             // build the resource name and use the property loader code
   380             String resourceName = packageName+"/jaxb.properties";
   381             logger.log(Level.FINE, "Trying to locate {0}", resourceName);
   382             Properties props = loadJAXBProperties(classLoader, resourceName);
   383             if (props == null) {
   384                 logger.fine("  not found");
   385             } else {
   386                 logger.fine("  found");
   387                 if (props.containsKey(JAXB_CONTEXT_FACTORY)) {
   388                     // trim() seems redundant, but adding to satisfy customer complaint
   389                     factoryClassName = props.getProperty(JAXB_CONTEXT_FACTORY).trim();
   390                     return newInstance(classes, properties, factoryClassName);
   391                 } else {
   392                     throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, JAXB_CONTEXT_FACTORY));
   393                 }
   394             }
   395         }
   397         // search for a system property second (javax.xml.bind.JAXBContext)
   398         logger.log(Level.FINE, "Checking system property {0}", JAXBContext.JAXB_CONTEXT_FACTORY);
   399         factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY));
   400         if (factoryClassName != null) {
   401             logger.log(Level.FINE, "  found {0}", factoryClassName);
   402             return newInstance( classes, properties, factoryClassName );
   403         } else { // leave it here for compatibility reasons
   404             logger.fine("  not found");
   405             logger.log(Level.FINE, "Checking system property {0}", jaxbContextFQCN);
   406             factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));
   407             if (factoryClassName != null) {
   408                 logger.log(Level.FINE, "  found {0}", factoryClassName);
   409                 return newInstance( classes, properties, factoryClassName );
   410             } else {
   411                 logger.fine("  not found");
   412             }
   413         }
   415         // OSGi search
   416         Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader();
   417         if (jaxbContext != null) {
   418             logger.fine("OSGi environment detected");
   419             return newInstance(classes, properties, jaxbContext);
   420         }
   422         // search META-INF services next
   423         logger.fine("Checking META-INF/services");
   424         BufferedReader r = null;
   425         try {
   426             final String resource = new StringBuilder("META-INF/services/").append(jaxbContextFQCN).toString();
   427             ClassLoader classLoader = getContextClassLoader();
   428             URL resourceURL;
   429             if(classLoader==null)
   430                 resourceURL = ClassLoader.getSystemResource(resource);
   431             else
   432                 resourceURL = classLoader.getResource(resource);
   434             if (resourceURL != null) {
   435                 logger.log(Level.FINE, "Reading {0}", resourceURL);
   436                 r = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "UTF-8"));
   437                 factoryClassName = r.readLine();
   438                 if (factoryClassName != null) {
   439                     factoryClassName = factoryClassName.trim();
   440                 }
   441                 return newInstance(classes, properties, factoryClassName);
   442             } else {
   443                 logger.log(Level.FINE, "Unable to find: {0}", resource);
   444             }
   445         } catch (UnsupportedEncodingException e) {
   446             // should never happen
   447             throw new JAXBException(e);
   448         } catch (IOException e) {
   449             throw new JAXBException(e);
   450         } finally {
   451             if (r != null) {
   452                 try {
   453                     r.close();
   454                 } catch (IOException ex) {
   455                     logger.log(Level.FINE, "Unable to close stream", ex);
   456                 }
   457             }
   458         }
   460         // else no provider found
   461         logger.fine("Trying to create the platform default provider");
   462         return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS);
   463     }
   465     private static Class lookupJaxbContextUsingOsgiServiceLoader() {
   466         try {
   467             // Use reflection to avoid having any dependency on ServiceLoader class
   468             Class target = Class.forName("com.sun.org.glassfish.hk2.osgiresourcelocator.ServiceLoader");
   469             Method m = target.getMethod("lookupProviderClasses", Class.class);
   470             Iterator iter = ((Iterable) m.invoke(null, JAXBContext.class)).iterator();
   471             return iter.hasNext() ? (Class)iter.next() : null;
   472         } catch(Exception e) {
   473             logger.log(Level.FINE, "Unable to find from OSGi: javax.xml.bind.JAXBContext");
   474             return null;
   475         }
   476     }
   478     private static Properties loadJAXBProperties( ClassLoader classLoader,
   479                                                   String propFileName )
   480         throws JAXBException {
   482         Properties props = null;
   484         try {
   485             URL url;
   486             if(classLoader==null)
   487                 url = ClassLoader.getSystemResource(propFileName);
   488             else
   489                 url = classLoader.getResource( propFileName );
   491             if( url != null ) {
   492                 logger.log(Level.FINE, "loading props from {0}", url);
   493                 props = new Properties();
   494                 InputStream is = url.openStream();
   495                 props.load( is );
   496                 is.close();
   497             }
   498         } catch( IOException ioe ) {
   499             logger.log(Level.FINE,"Unable to load "+propFileName,ioe);
   500             throw new JAXBException( ioe.toString(), ioe );
   501         }
   503         return props;
   504     }
   507     /**
   508      * Search the given ClassLoader for an instance of the specified class and
   509      * return a string representation of the URL that points to the resource.
   510      *
   511      * @param clazz
   512      *          The class to search for
   513      * @param loader
   514      *          The ClassLoader to search.  If this parameter is null, then the
   515      *          system class loader will be searched
   516      * @return
   517      *          the URL for the class or null if it wasn't found
   518      */
   519     static URL which(Class clazz, ClassLoader loader) {
   521         String classnameAsResource = clazz.getName().replace('.', '/') + ".class";
   523         if(loader == null) {
   524             loader = getSystemClassLoader();
   525         }
   527         return loader.getResource(classnameAsResource);
   528     }
   530     /**
   531      * Get the URL for the Class from it's ClassLoader.
   532      *
   533      * Convenience method for {@link #which(Class, ClassLoader)}.
   534      *
   535      * Equivalent to calling: which(clazz, clazz.getClassLoader())
   536      *
   537      * @param clazz
   538      *          The class to search for
   539      * @return
   540      *          the URL for the class or null if it wasn't found
   541      */
   542     static URL which(Class clazz) {
   543         return which(clazz, getClassClassLoader(clazz));
   544     }
   546     /**
   547      * When JAXB is in J2SE, rt.jar has to have a JAXB implementation.
   548      * However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext
   549      * because if it has, it will take precedence over any file that applications have
   550      * in their jar files.
   551      *
   552      * <p>
   553      * When the user bundles his own JAXB implementation, we'd like to use it, and we
   554      * want the platform default to be used only when there's no other JAXB provider.
   555      *
   556      * <p>
   557      * For this reason, we have to hard-code the class name into the API.
   558      */
   559     private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory";
   561     /**
   562      * Loads the class, provided that the calling thread has an access to the class being loaded.
   563      */
   564     private static Class safeLoadClass(String className, ClassLoader classLoader) throws ClassNotFoundException {
   565        logger.log(Level.FINE, "Trying to load {0}", className);
   566        try {
   567           // make sure that the current thread has an access to the package of the given name.
   568           SecurityManager s = System.getSecurityManager();
   569           if (s != null) {
   570               int i = className.lastIndexOf('.');
   571               if (i != -1) {
   572                   s.checkPackageAccess(className.substring(0,i));
   573               }
   574           }
   576           if (classLoader == null) {
   577               return Class.forName(className);
   578           } else {
   579               return classLoader.loadClass(className);
   580           }
   581        } catch (SecurityException se) {
   582            // anyone can access the platform default factory class without permission
   583            if (PLATFORM_DEFAULT_FACTORY_CLASS.equals(className)) {
   584               return Class.forName(className);
   585            }
   586            throw se;
   587        }
   588     }
   590     private static ClassLoader getContextClassLoader() {
   591         if (System.getSecurityManager() == null) {
   592             return Thread.currentThread().getContextClassLoader();
   593         } else {
   594             return (ClassLoader) java.security.AccessController.doPrivileged(
   595                     new java.security.PrivilegedAction() {
   596                         public java.lang.Object run() {
   597                             return Thread.currentThread().getContextClassLoader();
   598                         }
   599                     });
   600         }
   601     }
   603     private static ClassLoader getClassClassLoader(final Class c) {
   604         if (System.getSecurityManager() == null) {
   605             return c.getClassLoader();
   606         } else {
   607             return (ClassLoader) java.security.AccessController.doPrivileged(
   608                     new java.security.PrivilegedAction() {
   609                         public java.lang.Object run() {
   610                             return c.getClassLoader();
   611                         }
   612                     });
   613         }
   614     }
   616     private static ClassLoader getSystemClassLoader() {
   617         if (System.getSecurityManager() == null) {
   618             return ClassLoader.getSystemClassLoader();
   619         } else {
   620             return (ClassLoader) java.security.AccessController.doPrivileged(
   621                     new java.security.PrivilegedAction() {
   622                         public java.lang.Object run() {
   623                             return ClassLoader.getSystemClassLoader();
   624                         }
   625                     });
   626         }
   627     }
   629 }

mercurial