1 /*
   2  * Copyright (c) 2003, 2017, 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  */
  25 
  26 package javax.xml.bind;
  27 
  28 import java.io.BufferedReader;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.InputStreamReader;
  32 import java.lang.reflect.InvocationTargetException;
  33 import java.lang.reflect.Method;
  34 import java.net.URL;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedActionException;
  37 import java.security.PrivilegedExceptionAction;
  38 import java.util.Map;
  39 import java.util.Properties;
  40 import java.util.StringTokenizer;
  41 import java.util.logging.ConsoleHandler;
  42 import java.util.logging.Level;
  43 import java.util.logging.Logger;
  44 
  45 
  46 /**
  47  * This class is package private and therefore is not exposed as part of the
  48  * JAXB API.
  49  *
  50  * This code is designed to implement the JAXB 1.0 spec pluggability feature
  51  *
  52  * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
  53  * @see JAXBContext
  54  */
  55 class ContextFinder {
  56 
  57     /**
  58      * When JAXB is in J2SE, rt.jar has to have a JAXB implementation.
  59      * However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext
  60      * because if it has, it will take precedence over any file that applications have
  61      * in their jar files.
  62      *
  63      * <p>
  64      * When the user bundles his own JAXB implementation, we'd like to use it, and we
  65      * want the platform default to be used only when there's no other JAXB provider.
  66      *
  67      * <p>
  68      * For this reason, we have to hard-code the class name into the API.
  69      */
  70     private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory";
  71 
  72     // previous value of JAXBContext.JAXB_CONTEXT_FACTORY, using also this to ensure backwards compatibility
  73     private static final String JAXB_CONTEXT_FACTORY_DEPRECATED = "javax.xml.bind.context.factory";
  74 
  75     private static final Logger logger;
  76 
  77     static {
  78         logger = Logger.getLogger("javax.xml.bind");
  79         try {
  80             if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {
  81                 // disconnect the logger from a bigger framework (if any)
  82                 // and take the matters into our own hands
  83                 logger.setUseParentHandlers(false);
  84                 logger.setLevel(Level.ALL);
  85                 ConsoleHandler handler = new ConsoleHandler();
  86                 handler.setLevel(Level.ALL);
  87                 logger.addHandler(handler);
  88             } else {
  89                 // don't change the setting of this logger
  90                 // to honor what other frameworks
  91                 // have done on configurations.
  92             }
  93         } catch (Throwable t) {
  94             // just to be extra safe. in particular System.getProperty may throw
  95             // SecurityException.
  96         }
  97     }
  98 
  99     private static ServiceLoaderUtil.ExceptionHandler<JAXBException> EXCEPTION_HANDLER =
 100             new ServiceLoaderUtil.ExceptionHandler<JAXBException>() {
 101                 @Override
 102                 public JAXBException createException(Throwable throwable, String message) {
 103                     return new JAXBException(message, throwable);
 104                 }
 105             };
 106 
 107     /**
 108      * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
 109      * throw the wrapped exception. Otherwise returns exception to be wrapped for further processing.
 110      */
 111     private static Throwable handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
 112         Throwable t = x.getTargetException();
 113         if (t != null) {
 114             if (t instanceof JAXBException)
 115                 // one of our exceptions, just re-throw
 116                 throw (JAXBException) t;
 117             if (t instanceof RuntimeException)
 118                 // avoid wrapping exceptions unnecessarily
 119                 throw (RuntimeException) t;
 120             if (t instanceof Error)
 121                 throw (Error) t;
 122             return t;
 123         }
 124         return x;
 125     }
 126 
 127 
 128     /**
 129      * Determine if two types (JAXBContext in this case) will generate a ClassCastException.
 130      *
 131      * For example, (targetType)originalType
 132      *
 133      * @param originalType
 134      *          The Class object of the type being cast
 135      * @param targetType
 136      *          The Class object of the type that is being cast to
 137      * @return JAXBException to be thrown.
 138      */
 139     private static JAXBException handleClassCastException(Class originalType, Class targetType) {
 140         final URL targetTypeURL = which(targetType);
 141 
 142         return new JAXBException(Messages.format(Messages.ILLEGAL_CAST,
 143                 // we don't care where the impl class is, we want to know where JAXBContext lives in the impl
 144                 // class' ClassLoader
 145                 getClassClassLoader(originalType).getResource("javax/xml/bind/JAXBContext.class"),
 146                 targetTypeURL));
 147     }
 148 
 149     /**
 150      * Create an instance of a class using the specified ClassLoader
 151      */
 152     static JAXBContext newInstance(String contextPath,
 153                                    Class[] contextPathClasses,
 154                                    String className,
 155                                    ClassLoader classLoader,
 156                                    Map properties) throws JAXBException {
 157 
 158         try {
 159             Class spFactory = ServiceLoaderUtil.safeLoadClass(className, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader);
 160             return newInstance(contextPath, contextPathClasses, spFactory, classLoader, properties);
 161         } catch (ClassNotFoundException x) {
 162             throw new JAXBException(Messages.format(Messages.DEFAULT_PROVIDER_NOT_FOUND), x);
 163 
 164         } catch (RuntimeException | JAXBException x) {
 165             // avoid wrapping RuntimeException to JAXBException,
 166             // because it indicates a bug in this code.
 167             // JAXBException re-thrown as is
 168             throw x;
 169         } catch (Exception x) {
 170             // can't catch JAXBException because the method is hidden behind
 171             // reflection.  Root element collisions detected in the call to
 172             // createContext() are reported as JAXBExceptions - just re-throw it
 173             // some other type of exception - just wrap it
 174             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, className, x), x);
 175         }
 176     }
 177 
 178     static JAXBContext newInstance(String contextPath,
 179                                    Class[] contextPathClasses,
 180                                    Class spFactory,
 181                                    ClassLoader classLoader,
 182                                    Map properties) throws JAXBException {
 183 
 184         try {
 185             /*
 186              * javax.xml.bind.context.factory points to a class which has a
 187              * static method called 'createContext' that
 188              * returns a javax.xml.JAXBContext.
 189              */
 190 
 191             Object context = null;
 192 
 193             // first check the method that takes Map as the third parameter.
 194             // this is added in 2.0.
 195             try {
 196                 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class, Map.class);
 197                 // any failure in invoking this method would be considered fatal
 198                 Object obj = instantiateProviderIfNecessary(spFactory);
 199                 context = m.invoke(obj, contextPath, classLoader, properties);
 200             } catch (NoSuchMethodException ignored) {
 201                 // it's not an error for the provider not to have this method.
 202             }
 203 
 204             if (context == null) {
 205                 // try the old method that doesn't take properties. compatible with 1.0.
 206                 // it is an error for an implementation not to have both forms of the createContext method.
 207                 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class);
 208                 Object obj = instantiateProviderIfNecessary(spFactory);
 209                 // any failure in invoking this method would be considered fatal
 210                 context = m.invoke(obj, contextPath, classLoader);
 211             }
 212 
 213             if (!(context instanceof JAXBContext)) {
 214                 // the cast would fail, so generate an exception with a nice message
 215                 throw handleClassCastException(context.getClass(), JAXBContext.class);
 216             }
 217 
 218             ModuleUtil.delegateAddOpensToImplModule(contextPathClasses, spFactory);
 219 
 220             return (JAXBContext) context;
 221         } catch (InvocationTargetException x) {
 222             // throw if it is exception not to be wrapped
 223             // otherwise, wrap with a JAXBException
 224             Throwable e = handleInvocationTargetException(x);
 225             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, e), e);
 226 
 227         } catch (Exception x) {
 228             // can't catch JAXBException because the method is hidden behind
 229             // reflection.  Root element collisions detected in the call to
 230             // createContext() are reported as JAXBExceptions - just re-throw it
 231             // some other type of exception - just wrap it
 232             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, x), x);
 233         }
 234     }
 235 
 236     private static Object instantiateProviderIfNecessary(final Class<?> implClass) throws JAXBException {
 237         try {
 238             if (JAXBContextFactory.class.isAssignableFrom(implClass)) {
 239                 return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
 240                     @Override
 241                     public Object run() throws Exception {
 242                         return implClass.newInstance();
 243                     }
 244                 });
 245             }
 246             return null;
 247         } catch (PrivilegedActionException x) {
 248             Throwable e = (x.getCause() == null) ? x : x.getCause();
 249             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, implClass, e), e);
 250         }
 251     }
 252 
 253     /**
 254      * Create an instance of a class using the thread context ClassLoader
 255      */
 256     static JAXBContext newInstance(Class[] classes, Map properties, String className) throws JAXBException {
 257 
 258         Class spi;
 259         try {
 260             spi = ServiceLoaderUtil.safeLoadClass(className, PLATFORM_DEFAULT_FACTORY_CLASS, getContextClassLoader());
 261         } catch (ClassNotFoundException e) {
 262             throw new JAXBException(Messages.format(Messages.DEFAULT_PROVIDER_NOT_FOUND), e);
 263         }
 264 
 265         if (logger.isLoggable(Level.FINE)) {
 266             // extra check to avoid costly which operation if not logged
 267             logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)});
 268         }
 269 
 270         return newInstance(classes, properties, spi);
 271     }
 272 
 273     static JAXBContext newInstance(Class[] classes,
 274                                    Map properties,
 275                                    Class spFactory) throws JAXBException {
 276         try {
 277 
 278             Method m = spFactory.getMethod("createContext", Class[].class, Map.class);
 279             Object obj = instantiateProviderIfNecessary(spFactory);
 280             Object context = m.invoke(obj, classes, properties);
 281             if (!(context instanceof JAXBContext)) {
 282                 // the cast would fail, so generate an exception with a nice message
 283                 throw handleClassCastException(context.getClass(), JAXBContext.class);
 284             }
 285             ModuleUtil.delegateAddOpensToImplModule(classes,  spFactory);
 286             return (JAXBContext) context;
 287 
 288         } catch (NoSuchMethodException | IllegalAccessException e) {
 289             throw new JAXBException(e);
 290         } catch (InvocationTargetException e) {
 291             // throw if it is exception not to be wrapped
 292             // otherwise, wrap with a JAXBException
 293             Throwable x = handleInvocationTargetException(e);
 294 
 295             throw new JAXBException(x);
 296         }
 297     }
 298 
 299     static JAXBContext find(String factoryId,
 300                             String contextPath,
 301                             ClassLoader classLoader,
 302                             Map properties) throws JAXBException {
 303 
 304         if (contextPath == null || contextPath.isEmpty()) {
 305             // no context is specified
 306             throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));
 307         }
 308 
 309         //ModuleUtil is mr-jar class, scans context path for jaxb classes on jdk9 and higher
 310         Class[] contextPathClasses = ModuleUtil.getClassesFromContextPath(contextPath, classLoader);
 311 
 312         //first try with classloader#getResource
 313         String factoryClassName = jaxbProperties(contextPath, classLoader, factoryId);
 314         if (factoryClassName == null && contextPathClasses != null) {
 315             //try with class#getResource
 316             factoryClassName = jaxbProperties(contextPathClasses, factoryId);
 317         }
 318 
 319         if (factoryClassName != null) {
 320             return newInstance(contextPath, contextPathClasses, factoryClassName, classLoader, properties);
 321         }
 322 
 323 
 324         String factoryName = classNameFromSystemProperties();
 325         if (factoryName != null) return newInstance(contextPath, contextPathClasses, factoryName, classLoader, properties);
 326 
 327         JAXBContextFactory obj = ServiceLoaderUtil.firstByServiceLoader(
 328                 JAXBContextFactory.class, logger, EXCEPTION_HANDLER);
 329 
 330         if (obj != null) {
 331             JAXBContext context = obj.createContext(contextPath, classLoader, properties);
 332             ModuleUtil.delegateAddOpensToImplModule(contextPathClasses, obj.getClass());
 333             return context;
 334         }
 335 
 336         // to ensure backwards compatibility
 337         factoryName = firstByServiceLoaderDeprecated(JAXBContext.class, classLoader);
 338         if (factoryName != null) return newInstance(contextPath, contextPathClasses, factoryName, classLoader, properties);
 339 
 340         Class ctxFactory = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader(
 341                 "javax.xml.bind.JAXBContext", logger);
 342 
 343         if (ctxFactory != null) {
 344             return newInstance(contextPath, contextPathClasses, ctxFactory, classLoader, properties);
 345         }
 346 
 347         // else no provider found
 348         logger.fine("Trying to create the platform default provider");
 349         return newInstance(contextPath, contextPathClasses, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties);
 350     }
 351 
 352     static JAXBContext find(Class<?>[] classes, Map<String, ?> properties) throws JAXBException {
 353 
 354         // search for jaxb.properties in the class loader of each class first
 355         logger.fine("Searching jaxb.properties");
 356         for (final Class c : classes) {
 357             // this classloader is used only to load jaxb.properties, so doing this should be safe.
 358             // this is possible for primitives, arrays, and classes that are
 359             // loaded by poorly implemented ClassLoaders
 360             if (c.getPackage() == null) continue;
 361 
 362             // TODO: do we want to optimize away searching the same package?  org.Foo, org.Bar, com.Baz
 363             // classes from the same package might come from different class loades, so it might be a bad idea
 364             // TODO: it's easier to look things up from the class
 365             // c.getResourceAsStream("jaxb.properties");
 366 
 367             URL jaxbPropertiesUrl = getResourceUrl(c, "jaxb.properties");
 368 
 369             if (jaxbPropertiesUrl != null) {
 370 
 371                 String factoryClassName =
 372                         classNameFromPackageProperties(
 373                                 jaxbPropertiesUrl,
 374                                 JAXBContext.JAXB_CONTEXT_FACTORY, JAXB_CONTEXT_FACTORY_DEPRECATED);
 375 
 376                 return newInstance(classes, properties, factoryClassName);
 377             }
 378 
 379         }
 380 
 381         String factoryClassName = classNameFromSystemProperties();
 382         if (factoryClassName != null) return newInstance(classes, properties, factoryClassName);
 383 
 384         JAXBContextFactory factory =
 385                 ServiceLoaderUtil.firstByServiceLoader(JAXBContextFactory.class, logger, EXCEPTION_HANDLER);
 386 
 387         if (factory != null) {
 388             JAXBContext context = factory.createContext(classes, properties);
 389             ModuleUtil.delegateAddOpensToImplModule(classes, factory.getClass());
 390             return context;
 391         }
 392 
 393         // to ensure backwards compatibility
 394         String className = firstByServiceLoaderDeprecated(JAXBContext.class, getContextClassLoader());
 395         if (className != null) return newInstance(classes, properties, className);
 396 
 397         logger.fine("Trying to create the platform default provider");
 398         Class ctxFactoryClass =
 399                 (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger);
 400 
 401         if (ctxFactoryClass != null) {
 402             return newInstance(classes, properties, ctxFactoryClass);
 403         }
 404 
 405         // else no provider found
 406         logger.fine("Trying to create the platform default provider");
 407         return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS);
 408     }
 409 
 410 
 411     /**
 412      * first factoryId should be the preferred one,
 413      * more of those can be provided to support backwards compatibility
 414      */
 415     private static String classNameFromPackageProperties(URL packagePropertiesUrl,
 416                                                          String ... factoryIds) throws JAXBException {
 417 
 418         logger.log(Level.FINE, "Trying to locate {0}", packagePropertiesUrl.toString());
 419         Properties props = loadJAXBProperties(packagePropertiesUrl);
 420         for(String factoryId : factoryIds) {
 421             if (props.containsKey(factoryId)) {
 422                 return props.getProperty(factoryId);
 423             }
 424         }
 425         //Factory key not found
 426         String propertiesUrl = packagePropertiesUrl.toExternalForm();
 427         String packageName = propertiesUrl.substring(0, propertiesUrl.indexOf("/jaxb.properties"));
 428         throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryIds[0]));
 429     }
 430 
 431     private static String classNameFromSystemProperties() throws JAXBException {
 432 
 433         String factoryClassName = getSystemProperty(JAXBContext.JAXB_CONTEXT_FACTORY);
 434         if (factoryClassName != null) {
 435             return factoryClassName;
 436         }
 437         // leave this here to assure compatibility
 438         factoryClassName = getDeprecatedSystemProperty(JAXB_CONTEXT_FACTORY_DEPRECATED);
 439         if (factoryClassName != null) {
 440             return factoryClassName;
 441         }
 442         // leave this here to assure compatibility
 443         factoryClassName = getDeprecatedSystemProperty(JAXBContext.class.getName());
 444         if (factoryClassName != null) {
 445             return factoryClassName;
 446         }
 447         return null;
 448     }
 449 
 450     private static String getDeprecatedSystemProperty(String property) {
 451         String value = getSystemProperty(property);
 452         if (value != null) {
 453             logger.log(Level.WARNING, "Using non-standard property: {0}. Property {1} should be used instead.",
 454                     new Object[] {property, JAXBContext.JAXB_CONTEXT_FACTORY});
 455         }
 456         return value;
 457     }
 458 
 459     private static String getSystemProperty(String property) {
 460         logger.log(Level.FINE, "Checking system property {0}", property);
 461         String value = AccessController.doPrivileged(new GetPropertyAction(property));
 462         if (value != null) {
 463             logger.log(Level.FINE, "  found {0}", value);
 464         } else {
 465             logger.log(Level.FINE, "  not found");
 466         }
 467         return value;
 468     }
 469 
 470     private static Properties loadJAXBProperties(URL url) throws JAXBException {
 471 
 472         try {
 473             Properties props;
 474             logger.log(Level.FINE, "loading props from {0}", url);
 475             props = new Properties();
 476             InputStream is = url.openStream();
 477             props.load(is);
 478             is.close();
 479             return props;
 480         } catch (IOException ioe) {
 481             logger.log(Level.FINE, "Unable to load " + url.toString(), ioe);
 482             throw new JAXBException(ioe.toString(), ioe);
 483         }
 484     }
 485 
 486     /**
 487      * If run on JPMS package containing resource must be open unconditionally.
 488      *
 489      * @param classLoader classloader to load resource with
 490      * @param resourceName qualified name of the resource
 491      * @return resource url if found
 492      */
 493     private static URL getResourceUrl(ClassLoader classLoader, String resourceName) {
 494         URL url;
 495         if (classLoader == null)
 496             url = ClassLoader.getSystemResource(resourceName);
 497         else
 498             url = classLoader.getResource(resourceName);
 499         return url;
 500     }
 501 
 502     private static URL getResourceUrl(Class<?> clazz, String resourceName) {
 503         return clazz.getResource(resourceName);
 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     @SuppressWarnings("unchecked")
 547     private static ClassLoader getContextClassLoader() {
 548         if (System.getSecurityManager() == null) {
 549             return Thread.currentThread().getContextClassLoader();
 550         } else {
 551             return (ClassLoader) java.security.AccessController.doPrivileged(
 552                     new java.security.PrivilegedAction() {
 553                         @Override
 554                         public java.lang.Object run() {
 555                             return Thread.currentThread().getContextClassLoader();
 556                         }
 557                     });
 558         }
 559     }
 560 
 561     @SuppressWarnings("unchecked")
 562     private static ClassLoader getClassClassLoader(final Class c) {
 563         if (System.getSecurityManager() == null) {
 564             return c.getClassLoader();
 565         } else {
 566             return (ClassLoader) java.security.AccessController.doPrivileged(
 567                     new java.security.PrivilegedAction() {
 568                         @Override
 569                         public java.lang.Object run() {
 570                             return c.getClassLoader();
 571                         }
 572                     });
 573         }
 574     }
 575 
 576     private static ClassLoader getSystemClassLoader() {
 577         if (System.getSecurityManager() == null) {
 578             return ClassLoader.getSystemClassLoader();
 579         } else {
 580             return (ClassLoader) java.security.AccessController.doPrivileged(
 581                     new java.security.PrivilegedAction() {
 582                         @Override
 583                         public java.lang.Object run() {
 584                             return ClassLoader.getSystemClassLoader();
 585                         }
 586                     });
 587         }
 588     }
 589 
 590     // ServiceLoaderUtil.firstByServiceLoaderDeprecated should be used instead.
 591     @Deprecated
 592     static String firstByServiceLoaderDeprecated(Class spiClass,
 593                                                  ClassLoader classLoader) throws JAXBException {
 594 
 595         final String jaxbContextFQCN = spiClass.getName();
 596 
 597         logger.fine("Searching META-INF/services");
 598 
 599         // search META-INF services next
 600         BufferedReader r = null;
 601         final String resource = "META-INF/services/" + jaxbContextFQCN;
 602         try {
 603             final InputStream resourceStream =
 604                     (classLoader == null) ?
 605                             ClassLoader.getSystemResourceAsStream(resource) :
 606                             classLoader.getResourceAsStream(resource);
 607 
 608             if (resourceStream != null) {
 609                 r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8"));
 610                 String factoryClassName = r.readLine();
 611                 if (factoryClassName != null) {
 612                     factoryClassName = factoryClassName.trim();
 613                 }
 614                 r.close();
 615                 logger.log(Level.FINE, "Configured factorty class:{0}", factoryClassName);
 616                 return factoryClassName;
 617             } else {
 618                 logger.log(Level.FINE, "Unable to load:{0}", resource);
 619                 return null;
 620             }
 621         } catch (IOException e) {
 622             throw new JAXBException(e);
 623         } finally {
 624             try {
 625                 if (r != null) {
 626                     r.close();
 627                 }
 628             } catch (IOException ex) {
 629                 logger.log(Level.SEVERE, "Unable to close resource: " + resource, ex);
 630             }
 631         }
 632     }
 633 
 634     private static String jaxbProperties(String contextPath, ClassLoader classLoader, String factoryId) throws JAXBException {
 635         String[] packages = contextPath.split(":");
 636 
 637         for (String pkg : packages) {
 638             String pkgUrl = pkg.replace('.', '/');
 639             URL jaxbPropertiesUrl = getResourceUrl(classLoader, pkgUrl + "/jaxb.properties");
 640             if (jaxbPropertiesUrl != null) {
 641                 return classNameFromPackageProperties(jaxbPropertiesUrl,
 642                                                       factoryId, JAXB_CONTEXT_FACTORY_DEPRECATED);
 643             }
 644         }
 645         return null;
 646     }
 647 
 648     private static String jaxbProperties(Class[] classesFromContextPath, String factoryId) throws JAXBException {
 649         for (Class c : classesFromContextPath) {
 650             URL jaxbPropertiesUrl = getResourceUrl(c, "jaxb.properties");
 651             if (jaxbPropertiesUrl != null) {
 652                 return classNameFromPackageProperties(jaxbPropertiesUrl, factoryId, JAXB_CONTEXT_FACTORY_DEPRECATED);
 653             }
 654         }
 655         return null;
 656     }
 657 
 658 }