jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java
changeset 29839 6d5d546e953b
parent 25871 b80b84e87032
child 31109 a542f8dcbbf8
equal deleted inserted replaced
29838:fe5fd9871a13 29839:6d5d546e953b
     1 /*
     1 /*
     2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package javax.xml.bind;
    26 package javax.xml.bind;
    27 
    27 
    28 import java.util.Iterator;
       
    29 import java.io.BufferedReader;
    28 import java.io.BufferedReader;
    30 import java.io.IOException;
    29 import java.io.IOException;
    31 import java.io.InputStream;
    30 import java.io.InputStream;
    32 import java.io.InputStreamReader;
    31 import java.io.InputStreamReader;
    33 import java.io.UnsupportedEncodingException;
    32 import java.io.UnsupportedEncodingException;
    34 import java.lang.reflect.InvocationTargetException;
    33 import java.lang.reflect.InvocationTargetException;
    35 import java.lang.reflect.Method;
    34 import java.lang.reflect.Method;
    36 import java.net.URL;
    35 import java.net.URL;
       
    36 import java.security.AccessController;
    37 import java.util.Map;
    37 import java.util.Map;
    38 import java.util.Properties;
    38 import java.util.Properties;
    39 import java.util.StringTokenizer;
    39 import java.util.StringTokenizer;
    40 import java.util.logging.ConsoleHandler;
    40 import java.util.logging.ConsoleHandler;
    41 import java.util.logging.Level;
    41 import java.util.logging.Level;
    42 import java.util.logging.Logger;
    42 import java.util.logging.Logger;
    43 import java.security.AccessController;
       
    44 
       
    45 import static javax.xml.bind.JAXBContext.JAXB_CONTEXT_FACTORY;
       
    46 
    43 
    47 
    44 
    48 /**
    45 /**
    49  * This class is package private and therefore is not exposed as part of the
    46  * This class is package private and therefore is not exposed as part of the
    50  * JAXB API.
    47  * JAXB API.
    53  *
    50  *
    54  * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
    51  * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
    55  * @see JAXBContext
    52  * @see JAXBContext
    56  */
    53  */
    57 class ContextFinder {
    54 class ContextFinder {
       
    55 
       
    56     /**
       
    57      * When JAXB is in J2SE, rt.jar has to have a JAXB implementation.
       
    58      * However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext
       
    59      * because if it has, it will take precedence over any file that applications have
       
    60      * in their jar files.
       
    61      *
       
    62      * <p>
       
    63      * When the user bundles his own JAXB implementation, we'd like to use it, and we
       
    64      * want the platform default to be used only when there's no other JAXB provider.
       
    65      *
       
    66      * <p>
       
    67      * For this reason, we have to hard-code the class name into the API.
       
    68      */
       
    69     private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory";
       
    70 
    58     private static final Logger logger;
    71     private static final Logger logger;
       
    72 
    59     static {
    73     static {
    60         logger = Logger.getLogger("javax.xml.bind");
    74         logger = Logger.getLogger("javax.xml.bind");
    61         try {
    75         try {
    62             if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {
    76             if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {
    63                 // disconnect the logger from a bigger framework (if any)
    77                 // disconnect the logger from a bigger framework (if any)
    70             } else {
    84             } else {
    71                 // don't change the setting of this logger
    85                 // don't change the setting of this logger
    72                 // to honor what other frameworks
    86                 // to honor what other frameworks
    73                 // have done on configurations.
    87                 // have done on configurations.
    74             }
    88             }
    75         } catch(Throwable t) {
    89         } catch (Throwable t) {
    76             // just to be extra safe. in particular System.getProperty may throw
    90             // just to be extra safe. in particular System.getProperty may throw
    77             // SecurityException.
    91             // SecurityException.
    78         }
    92         }
    79     }
    93     }
    80 
    94 
    82      * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
    96      * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
    83      * throw the wrapped exception.
    97      * throw the wrapped exception.
    84      */
    98      */
    85     private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
    99     private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
    86         Throwable t = x.getTargetException();
   100         Throwable t = x.getTargetException();
    87         if( t != null ) {
   101         if (t != null) {
    88             if( t instanceof JAXBException )
   102             if (t instanceof JAXBException)
    89                 // one of our exceptions, just re-throw
   103                 // one of our exceptions, just re-throw
    90                 throw (JAXBException)t;
   104                 throw (JAXBException) t;
    91             if( t instanceof RuntimeException )
   105             if (t instanceof RuntimeException)
    92                 // avoid wrapping exceptions unnecessarily
   106                 // avoid wrapping exceptions unnecessarily
    93                 throw (RuntimeException)t;
   107                 throw (RuntimeException) t;
    94             if( t instanceof Error )
   108             if (t instanceof Error)
    95                 throw (Error)t;
   109                 throw (Error) t;
    96         }
   110         }
    97     }
   111     }
    98 
   112 
    99 
   113 
   100     /**
   114     /**
   119     }
   133     }
   120 
   134 
   121     /**
   135     /**
   122      * Create an instance of a class using the specified ClassLoader
   136      * Create an instance of a class using the specified ClassLoader
   123      */
   137      */
   124     static JAXBContext newInstance( String contextPath,
   138     static JAXBContext newInstance(String contextPath,
   125                                String className,
   139                                    String className,
   126                                ClassLoader classLoader,
   140                                    ClassLoader classLoader,
   127                                Map properties )
   141                                    Map properties) throws JAXBException {
   128         throws JAXBException {
   142 
   129         try {
   143         try {
   130             Class spFactory = safeLoadClass(className,classLoader);
   144             Class spFactory = ServiceLoaderUtil.safeLoadClass(className, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader);
   131             return newInstance(contextPath, spFactory, classLoader, properties);
   145             return newInstance(contextPath, spFactory, classLoader, properties);
   132         } catch (ClassNotFoundException x) {
   146         } catch (ClassNotFoundException x) {
   133             throw new JAXBException(
   147             throw new JAXBException(Messages.format(Messages.PROVIDER_NOT_FOUND, className), x);
   134                 Messages.format( Messages.PROVIDER_NOT_FOUND, className ),
   148 
   135                 x);
       
   136         } catch (RuntimeException x) {
   149         } catch (RuntimeException x) {
   137             // avoid wrapping RuntimeException to JAXBException,
   150             // avoid wrapping RuntimeException to JAXBException,
   138             // because it indicates a bug in this code.
   151             // because it indicates a bug in this code.
   139             throw x;
   152             throw x;
   140         } catch (Exception x) {
   153         } catch (Exception x) {
   141             // can't catch JAXBException because the method is hidden behind
   154             // can't catch JAXBException because the method is hidden behind
   142             // reflection.  Root element collisions detected in the call to
   155             // reflection.  Root element collisions detected in the call to
   143             // createContext() are reported as JAXBExceptions - just re-throw it
   156             // createContext() are reported as JAXBExceptions - just re-throw it
   144             // some other type of exception - just wrap it
   157             // some other type of exception - just wrap it
   145             throw new JAXBException(
   158             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, className, x), x);
   146                 Messages.format( Messages.COULD_NOT_INSTANTIATE, className, x ),
   159         }
   147                 x);
   160     }
   148         }
   161 
   149     }
   162     static JAXBContext newInstance(String contextPath, Class spFactory, ClassLoader classLoader, Map properties) throws JAXBException {
   150 
   163 
   151     static JAXBContext newInstance( String contextPath,
       
   152                                Class spFactory,
       
   153                                ClassLoader classLoader,
       
   154                                Map properties )
       
   155         throws JAXBException
       
   156     {
       
   157         try {
   164         try {
   158             /*
   165             /*
   159              * javax.xml.bind.context.factory points to a class which has a
   166              * javax.xml.bind.context.factory points to a class which has a
   160              * static method called 'createContext' that
   167              * static method called 'createContext' that
   161              * returns a javax.xml.JAXBContext.
   168              * returns a javax.xml.JAXBContext.
   164             Object context = null;
   171             Object context = null;
   165 
   172 
   166             // first check the method that takes Map as the third parameter.
   173             // first check the method that takes Map as the third parameter.
   167             // this is added in 2.0.
   174             // this is added in 2.0.
   168             try {
   175             try {
   169                 Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class,Map.class);
   176                 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class, Map.class);
   170                 // any failure in invoking this method would be considered fatal
   177                 // any failure in invoking this method would be considered fatal
   171                 context = m.invoke(null,contextPath,classLoader,properties);
   178                 context = m.invoke(null, contextPath, classLoader, properties);
   172             } catch (NoSuchMethodException e) {
   179             } catch (NoSuchMethodException e) {
   173                 // it's not an error for the provider not to have this method.
   180                 // it's not an error for the provider not to have this method.
   174             }
   181             }
   175 
   182 
   176             if(context==null) {
   183             if (context == null) {
   177                 // try the old method that doesn't take properties. compatible with 1.0.
   184                 // 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.
   185                 // 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);
   186                 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class);
   180                 // any failure in invoking this method would be considered fatal
   187                 // any failure in invoking this method would be considered fatal
   181                 context = m.invoke(null,contextPath,classLoader);
   188                 context = m.invoke(null, contextPath, classLoader);
   182             }
   189             }
   183 
   190 
   184             if(!(context instanceof JAXBContext)) {
   191             if (!(context instanceof JAXBContext)) {
   185                 // the cast would fail, so generate an exception with a nice message
   192                 // the cast would fail, so generate an exception with a nice message
   186                 throw handleClassCastException(context.getClass(), JAXBContext.class);
   193                 throw handleClassCastException(context.getClass(), JAXBContext.class);
   187             }
   194             }
   188             return (JAXBContext)context;
   195             return (JAXBContext) context;
   189         } catch (InvocationTargetException x) {
   196         } catch (InvocationTargetException x) {
   190             handleInvocationTargetException(x);
   197             handleInvocationTargetException(x);
   191             // for other exceptions, wrap the internal target exception
   198             // for other exceptions, wrap the internal target exception
   192             // with a JAXBException
   199             // with a JAXBException
   193             Throwable e = x;
   200             Throwable e = x;
   194             if(x.getTargetException()!=null)
   201             if (x.getTargetException() != null)
   195                 e = x.getTargetException();
   202                 e = x.getTargetException();
   196 
   203 
   197             throw new JAXBException( Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, e ), e );
   204             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, e), e);
   198         } catch (RuntimeException x) {
   205         } catch (RuntimeException x) {
   199             // avoid wrapping RuntimeException to JAXBException,
   206             // avoid wrapping RuntimeException to JAXBException,
   200             // because it indicates a bug in this code.
   207             // because it indicates a bug in this code.
   201             throw x;
   208             throw x;
   202         } catch (Exception x) {
   209         } catch (Exception x) {
   203             // can't catch JAXBException because the method is hidden behind
   210             // can't catch JAXBException because the method is hidden behind
   204             // reflection.  Root element collisions detected in the call to
   211             // reflection.  Root element collisions detected in the call to
   205             // createContext() are reported as JAXBExceptions - just re-throw it
   212             // createContext() are reported as JAXBExceptions - just re-throw it
   206             // some other type of exception - just wrap it
   213             // some other type of exception - just wrap it
   207             throw new JAXBException(
   214             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, x), x);
   208                 Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, x ),
   215         }
   209                 x);
   216     }
   210         }
       
   211     }
       
   212 
       
   213 
   217 
   214     /**
   218     /**
   215      * Create an instance of a class using the thread context ClassLoader
   219      * Create an instance of a class using the thread context ClassLoader
   216      */
   220      */
   217     static JAXBContext newInstance(
   221     static JAXBContext newInstance(Class[] classes, Map properties, String className) throws JAXBException {
   218                               Class[] classes,
   222 
   219                               Map properties,
       
   220                               String className) throws JAXBException {
       
   221         ClassLoader cl = getContextClassLoader();
       
   222         Class spi;
   223         Class spi;
   223         try {
   224         try {
   224             spi = safeLoadClass(className,cl);
   225             spi = ServiceLoaderUtil.safeLoadClass(className, PLATFORM_DEFAULT_FACTORY_CLASS, getContextClassLoader());
   225         } catch (ClassNotFoundException e) {
   226         } catch (ClassNotFoundException e) {
   226             throw new JAXBException(e);
   227             throw new JAXBException(e);
   227         }
   228         }
   228 
   229 
   229         if(logger.isLoggable(Level.FINE)) {
   230         if (logger.isLoggable(Level.FINE)) {
   230             // extra check to avoid costly which operation if not logged
   231             // 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             logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)});
   232         }
   233         }
   233 
   234 
   234         return newInstance(classes, properties, spi);
   235         return newInstance(classes, properties, spi);
   235     }
   236     }
   236 
   237 
   237     static JAXBContext newInstance(Class[] classes,
   238     static JAXBContext newInstance(Class[] classes,
   238                                    Map properties,
   239                                    Map properties,
   239                                    Class spFactory) throws JAXBException {
   240                                    Class spFactory) throws JAXBException {
   240         Method m;
   241         try {
   241         try {
   242             Method m = spFactory.getMethod("createContext", Class[].class, Map.class);
   242             m = spFactory.getMethod("createContext", Class[].class, Map.class);
   243             Object context = m.invoke(null, classes, properties);
       
   244             if (!(context instanceof JAXBContext)) {
       
   245                 // the cast would fail, so generate an exception with a nice message
       
   246                 throw handleClassCastException(context.getClass(), JAXBContext.class);
       
   247             }
       
   248             return (JAXBContext) context;
   243         } catch (NoSuchMethodException e) {
   249         } catch (NoSuchMethodException e) {
   244             throw new JAXBException(e);
   250             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) {
   251         } catch (IllegalAccessException e) {
   254             throw new JAXBException(e);
   252             throw new JAXBException(e);
   255         } catch (InvocationTargetException e) {
   253         } catch (InvocationTargetException e) {
   256             handleInvocationTargetException(e);
   254             handleInvocationTargetException(e);
   257 
   255 
   261 
   259 
   262             throw new JAXBException(x);
   260             throw new JAXBException(x);
   263         }
   261         }
   264     }
   262     }
   265 
   263 
   266     static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties ) throws JAXBException {
   264     static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties) throws JAXBException {
   267 
   265 
   268         // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP?
   266         // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP?
   269 
   267 
   270         final String jaxbContextFQCN = JAXBContext.class.getName();
   268         StringTokenizer packages = new StringTokenizer(contextPath, ":");
   271 
   269         if (!packages.hasMoreTokens()) {
   272         // search context path for jaxb.properties first
       
   273         StringBuilder propFileName;
       
   274         StringTokenizer packages = new StringTokenizer( contextPath, ":" );
       
   275         String factoryClassName;
       
   276 
       
   277         if(!packages.hasMoreTokens())
       
   278             // no context is specified
   270             // no context is specified
   279             throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));
   271             throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));
   280 
   272         }
   281 
   273 
       
   274         // search for jaxb.properties in the class loader of each class first
   282         logger.fine("Searching jaxb.properties");
   275         logger.fine("Searching jaxb.properties");
   283 
   276         while (packages.hasMoreTokens()) {
   284         while( packages.hasMoreTokens() ) {
       
   285             String packageName = packages.nextToken(":").replace('.','/');
       
   286             // com.acme.foo - > com/acme/foo/jaxb.properties
   277             // com.acme.foo - > com/acme/foo/jaxb.properties
   287              propFileName = new StringBuilder().append(packageName).append("/jaxb.properties");
   278             String className = classNameFromPackageProperties(factoryId, classLoader, packages.nextToken(":").replace('.', '/'));
   288 
   279             if (className != null) return newInstance(contextPath, className, classLoader, properties);
   289             Properties props = loadJAXBProperties( classLoader, propFileName.toString() );
   280         }
   290             if (props != null) {
   281 
   291                 if (props.containsKey(factoryId)) {
   282         String factoryName = classNameFromSystemProperties();
   292                     factoryClassName = props.getProperty(factoryId);
   283         if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties);
   293                     return newInstance( contextPath, factoryClassName, classLoader, properties );
   284 
   294                 } else {
   285         Class ctxFactory = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger);
   295                     throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId));
   286         if (ctxFactory != null) {
   296                 }
   287             return newInstance(contextPath, ctxFactory, classLoader, properties);
   297             }
   288         }
   298         }
   289 
   299 
   290         // TODO: SPEC change required! This is supposed to be!
   300         logger.fine("Searching the system property");
   291         // JAXBContext obj = firstByServiceLoader(JAXBContext.class, EXCEPTION_HANDLER);
   301 
   292         // if (obj != null) return obj;
       
   293 
       
   294         // TODO: Deprecated - SPEC change required!
       
   295         factoryName = firstByServiceLoaderDeprecated(JAXBContext.class, classLoader);
       
   296         if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties);
       
   297 
       
   298         // else no provider found
       
   299         logger.fine("Trying to create the platform default provider");
       
   300         return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties);
       
   301     }
       
   302 
       
   303     static JAXBContext find(Class[] classes, Map properties) throws JAXBException {
       
   304 
       
   305         // search for jaxb.properties in the class loader of each class first
       
   306         logger.fine("Searching jaxb.properties");
       
   307         for (final Class c : classes) {
       
   308             // this classloader is used only to load jaxb.properties, so doing this should be safe.
       
   309             if (c.getPackage() == null) continue;       // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders
       
   310 
       
   311             // TODO: do we want to optimize away searching the same package?  org.Foo, org.Bar, com.Baz
       
   312             // classes from the same package might come from different class loades, so it might be a bad idea
       
   313             // TODO: it's easier to look things up from the class
       
   314             // c.getResourceAsStream("jaxb.properties");
       
   315 
       
   316             String className = classNameFromPackageProperties(JAXBContext.JAXB_CONTEXT_FACTORY, getClassClassLoader(c), c.getPackage().getName().replace('.', '/'));
       
   317             if (className != null) return newInstance(classes, properties, className);
       
   318         }
       
   319 
       
   320         String factoryName = classNameFromSystemProperties();
       
   321         if (factoryName != null) return newInstance(classes, properties, factoryName);
       
   322 
       
   323         Class ctxFactoryClass = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger);
       
   324         if (ctxFactoryClass != null) {
       
   325             return newInstance(classes, properties, ctxFactoryClass);
       
   326         }
       
   327 
       
   328         // TODO: to be removed - deprecated!!! Requires SPEC change!!!
       
   329         String className = firstByServiceLoaderDeprecated(JAXBContext.class, getContextClassLoader());
       
   330         if (className != null) return newInstance(classes, properties, className);
       
   331 
       
   332         //    // TODO: supposed to be:
       
   333         //    obj = firstByServiceLoader(JAXBContext.class, EXCEPTION_HANDLER);
       
   334         //    if (obj != null) return obj;
       
   335 
       
   336         // else no provider found
       
   337         logger.fine("Trying to create the platform default provider");
       
   338         return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS);
       
   339     }
       
   340 
       
   341 
       
   342     private static String classNameFromPackageProperties(String factoryId, ClassLoader classLoader, String packageName) throws JAXBException {
       
   343         String resourceName = packageName + "/jaxb.properties";
       
   344         logger.log(Level.FINE, "Trying to locate {0}", resourceName);
       
   345         Properties props = loadJAXBProperties(classLoader, resourceName);
       
   346         if (props != null) {
       
   347             if (props.containsKey(factoryId)) {
       
   348                 return props.getProperty(factoryId);
       
   349             } else {
       
   350                 throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId));
       
   351             }
       
   352         }
       
   353         return null;
       
   354     }
       
   355 
       
   356     private static String classNameFromSystemProperties() throws JAXBException {
       
   357         logger.log(Level.FINE, "Checking system property {0}", JAXBContext.JAXB_CONTEXT_FACTORY);
   302         // search for a system property second (javax.xml.bind.JAXBContext)
   358         // search for a system property second (javax.xml.bind.JAXBContext)
   303         factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY));
   359         String factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY));
   304         if(  factoryClassName != null ) {
   360         if (factoryClassName != null) {
   305             return newInstance( contextPath, factoryClassName, classLoader, properties );
   361             logger.log(Level.FINE, "  found {0}", factoryClassName);
       
   362             return factoryClassName;
   306         } else { // leave this here to assure compatibility
   363         } else { // leave this here to assure compatibility
   307             factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));
   364             logger.fine("  not found");
   308             if(  factoryClassName != null ) {
   365             logger.log(Level.FINE, "Checking system property {0}", JAXBContext.class.getName());
   309                 return newInstance( contextPath, factoryClassName, classLoader, properties );
   366             factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.class.getName()));
   310             }
   367             if (factoryClassName != null) {
   311         }
   368                 logger.log(Level.FINE, "  found {0}", factoryClassName);
   312 
   369                 return factoryClassName;
   313         // OSGi search
   370             } else {
   314         Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader();
   371                 logger.fine("  not found");
   315         if (jaxbContext != null) {
   372             }
   316             logger.fine("OSGi environment detected");
   373         }
   317             return newInstance(contextPath, jaxbContext, classLoader, properties);
   374         return null;
   318         }
   375     }
       
   376 
       
   377     private static Properties loadJAXBProperties(ClassLoader classLoader, String propFileName) throws JAXBException {
       
   378 
       
   379         Properties props = null;
       
   380         try {
       
   381             URL url;
       
   382             if (classLoader == null)
       
   383                 url = ClassLoader.getSystemResource(propFileName);
       
   384             else
       
   385                 url = classLoader.getResource(propFileName);
       
   386 
       
   387             if (url != null) {
       
   388                 logger.log(Level.FINE, "loading props from {0}", url);
       
   389                 props = new Properties();
       
   390                 InputStream is = url.openStream();
       
   391                 props.load(is);
       
   392                 is.close();
       
   393             }
       
   394         } catch (IOException ioe) {
       
   395             logger.log(Level.FINE, "Unable to load " + propFileName, ioe);
       
   396             throw new JAXBException(ioe.toString(), ioe);
       
   397         }
       
   398 
       
   399         return props;
       
   400     }
       
   401 
       
   402 
       
   403     /**
       
   404      * Search the given ClassLoader for an instance of the specified class and
       
   405      * return a string representation of the URL that points to the resource.
       
   406      *
       
   407      * @param clazz
       
   408      *          The class to search for
       
   409      * @param loader
       
   410      *          The ClassLoader to search.  If this parameter is null, then the
       
   411      *          system class loader will be searched
       
   412      * @return
       
   413      *          the URL for the class or null if it wasn't found
       
   414      */
       
   415     static URL which(Class clazz, ClassLoader loader) {
       
   416 
       
   417         String classnameAsResource = clazz.getName().replace('.', '/') + ".class";
       
   418 
       
   419         if (loader == null) {
       
   420             loader = getSystemClassLoader();
       
   421         }
       
   422 
       
   423         return loader.getResource(classnameAsResource);
       
   424     }
       
   425 
       
   426     /**
       
   427      * Get the URL for the Class from it's ClassLoader.
       
   428      *
       
   429      * Convenience method for {@link #which(Class, ClassLoader)}.
       
   430      *
       
   431      * Equivalent to calling: which(clazz, clazz.getClassLoader())
       
   432      *
       
   433      * @param clazz
       
   434      *          The class to search for
       
   435      * @return
       
   436      *          the URL for the class or null if it wasn't found
       
   437      */
       
   438     static URL which(Class clazz) {
       
   439         return which(clazz, getClassClassLoader(clazz));
       
   440     }
       
   441 
       
   442     @SuppressWarnings("unchecked")
       
   443     private static ClassLoader getContextClassLoader() {
       
   444         if (System.getSecurityManager() == null) {
       
   445             return Thread.currentThread().getContextClassLoader();
       
   446         } else {
       
   447             return (ClassLoader) java.security.AccessController.doPrivileged(
       
   448                     new java.security.PrivilegedAction() {
       
   449                         public java.lang.Object run() {
       
   450                             return Thread.currentThread().getContextClassLoader();
       
   451                         }
       
   452                     });
       
   453         }
       
   454     }
       
   455 
       
   456     @SuppressWarnings("unchecked")
       
   457     private static ClassLoader getClassClassLoader(final Class c) {
       
   458         if (System.getSecurityManager() == null) {
       
   459             return c.getClassLoader();
       
   460         } else {
       
   461             return (ClassLoader) java.security.AccessController.doPrivileged(
       
   462                     new java.security.PrivilegedAction() {
       
   463                         public java.lang.Object run() {
       
   464                             return c.getClassLoader();
       
   465                         }
       
   466                     });
       
   467         }
       
   468     }
       
   469 
       
   470     private static ClassLoader getSystemClassLoader() {
       
   471         if (System.getSecurityManager() == null) {
       
   472             return ClassLoader.getSystemClassLoader();
       
   473         } else {
       
   474             return (ClassLoader) java.security.AccessController.doPrivileged(
       
   475                     new java.security.PrivilegedAction() {
       
   476                         public java.lang.Object run() {
       
   477                             return ClassLoader.getSystemClassLoader();
       
   478                         }
       
   479                     });
       
   480         }
       
   481     }
       
   482 
       
   483     // TODO: to be removed - SPEC change required
       
   484     //    ServiceLoaderUtil.firstByServiceLoaderDeprecated should be used instead.
       
   485     @Deprecated
       
   486     static String firstByServiceLoaderDeprecated(Class spiClass, ClassLoader classLoader) throws JAXBException {
       
   487         final String jaxbContextFQCN = spiClass.getName();
   319 
   488 
   320         logger.fine("Searching META-INF/services");
   489         logger.fine("Searching META-INF/services");
       
   490 
   321         // search META-INF services next
   491         // search META-INF services next
   322         BufferedReader r = null;
   492         BufferedReader r = null;
   323         try {
   493         final String resource = new StringBuilder().append("META-INF/services/").append(jaxbContextFQCN).toString();
   324             final StringBuilder resource = new StringBuilder().append("META-INF/services/").append(jaxbContextFQCN);
   494         try {
   325             final InputStream resourceStream =
   495             final InputStream resourceStream =
   326                     classLoader.getResourceAsStream(resource.toString());
   496                     (classLoader == null) ?
       
   497                             ClassLoader.getSystemResourceAsStream(resource) :
       
   498                             classLoader.getResourceAsStream(resource);
   327 
   499 
   328             if (resourceStream != null) {
   500             if (resourceStream != null) {
   329                 r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8"));
   501                 r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8"));
   330                 factoryClassName = r.readLine();
   502                 String factoryClassName = r.readLine();
   331                 if (factoryClassName != null) {
   503                 if (factoryClassName != null) {
   332                     factoryClassName = factoryClassName.trim();
   504                     factoryClassName = factoryClassName.trim();
   333                 }
   505                 }
   334                 r.close();
   506                 r.close();
   335                 return newInstance(contextPath, factoryClassName, classLoader, properties);
   507                 logger.log(Level.FINE, "Configured factorty class:{0}", factoryClassName);
       
   508                 return factoryClassName;
   336             } else {
   509             } else {
   337                 logger.log(Level.FINE, "Unable to load:{0}", resource.toString());
   510                 logger.log(Level.FINE, "Unable to load:{0}", resource);
       
   511                 return null;
   338             }
   512             }
   339         } catch (UnsupportedEncodingException e) {
   513         } catch (UnsupportedEncodingException e) {
   340             // should never happen
   514             // should never happen
   341             throw new JAXBException(e);
   515             throw new JAXBException(e);
   342         } catch (IOException e) {
   516         } catch (IOException e) {
   345             try {
   519             try {
   346                 if (r != null) {
   520                 if (r != null) {
   347                     r.close();
   521                     r.close();
   348                 }
   522                 }
   349             } catch (IOException ex) {
   523             } catch (IOException ex) {
   350                 Logger.getLogger(ContextFinder.class.getName()).log(Level.SEVERE, null, ex);
   524                 logger.log(Level.SEVERE, "Unable to close resource: " + resource, ex);
   351             }
   525             }
   352         }
       
   353 
       
   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     }
       
   358 
       
   359     static JAXBContext find( Class[] classes, Map properties ) throws JAXBException {
       
   360 
       
   361         final String jaxbContextFQCN = JAXBContext.class.getName();
       
   362         String factoryClassName;
       
   363 
       
   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('.', '/');
       
   372 
       
   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
       
   375 
       
   376             // TODO: it's easier to look things up from the class
       
   377             // c.getResourceAsStream("jaxb.properties");
       
   378 
       
   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         }
       
   396 
       
   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         }
       
   414 
       
   415         // OSGi search
       
   416         Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader();
       
   417         if (jaxbContext != null) {
       
   418             logger.fine("OSGi environment detected");
       
   419             return newInstance(classes, properties, jaxbContext);
       
   420         }
       
   421 
       
   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);
       
   433 
       
   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         }
       
   459 
       
   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     }
       
   464 
       
   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     }
       
   477 
       
   478     private static Properties loadJAXBProperties( ClassLoader classLoader,
       
   479                                                   String propFileName )
       
   480         throws JAXBException {
       
   481 
       
   482         Properties props = null;
       
   483 
       
   484         try {
       
   485             URL url;
       
   486             if(classLoader==null)
       
   487                 url = ClassLoader.getSystemResource(propFileName);
       
   488             else
       
   489                 url = classLoader.getResource( propFileName );
       
   490 
       
   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         }
       
   502 
       
   503         return props;
       
   504     }
       
   505 
       
   506 
       
   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) {
       
   520 
       
   521         String classnameAsResource = clazz.getName().replace('.', '/') + ".class";
       
   522 
       
   523         if(loader == null) {
       
   524             loader = getSystemClassLoader();
       
   525         }
       
   526 
       
   527         return loader.getResource(classnameAsResource);
       
   528     }
       
   529 
       
   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     }
       
   545 
       
   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";
       
   560 
       
   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           }
       
   575 
       
   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     }
       
   589 
       
   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     }
       
   602 
       
   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     }
       
   615 
       
   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         }
   526         }
   627     }
   527     }
   628 
   528 
   629 }
   529 }