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 }