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 ModuleUtil.delegateAddOpensToImplModule(contextPathClasses, spFactory); 187 188 /* 189 * javax.xml.bind.context.factory points to a class which has a 190 * static method called 'createContext' that 191 * returns a javax.xml.JAXBContext. 192 */ 193 194 Object context = null; 195 196 // first check the method that takes Map as the third parameter. 197 // this is added in 2.0. 198 try { 199 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class, Map.class); 200 // any failure in invoking this method would be considered fatal 201 Object obj = instantiateProviderIfNecessary(spFactory); 202 context = m.invoke(obj, contextPath, classLoader, properties); 203 } catch (NoSuchMethodException ignored) { 204 // it's not an error for the provider not to have this method. 205 } 206 207 if (context == null) { 208 // try the old method that doesn't take properties. compatible with 1.0. 209 // it is an error for an implementation not to have both forms of the createContext method. 210 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class); 211 Object obj = instantiateProviderIfNecessary(spFactory); 212 // any failure in invoking this method would be considered fatal 213 context = m.invoke(obj, contextPath, classLoader); 214 } 215 216 if (!(context instanceof JAXBContext)) { 217 // the cast would fail, so generate an exception with a nice message 218 throw handleClassCastException(context.getClass(), JAXBContext.class); 219 } 220 221 return (JAXBContext) context; 222 } catch (InvocationTargetException x) { 223 // throw if it is exception not to be wrapped 224 // otherwise, wrap with a JAXBException 225 Throwable e = handleInvocationTargetException(x); 226 throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, e), e); 227 228 } catch (Exception x) { 229 // can't catch JAXBException because the method is hidden behind 230 // reflection. Root element collisions detected in the call to 231 // createContext() are reported as JAXBExceptions - just re-throw it 232 // some other type of exception - just wrap it 233 throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, x), x); 234 } 235 } 236 237 private static Object instantiateProviderIfNecessary(final Class<?> implClass) throws JAXBException { 238 try { 239 if (JAXBContextFactory.class.isAssignableFrom(implClass)) { 240 return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { 241 @Override 242 public Object run() throws Exception { 243 return implClass.newInstance(); 244 } 245 }); 246 } 247 return null; 248 } catch (PrivilegedActionException x) { 249 Throwable e = (x.getCause() == null) ? x : x.getCause(); 250 throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, implClass, e), e); 251 } 252 } 253 254 /** 255 * Create an instance of a class using the thread context ClassLoader 256 */ 257 static JAXBContext newInstance(Class[] classes, Map properties, String className) throws JAXBException { 258 259 Class spi; 260 try { 261 spi = ServiceLoaderUtil.safeLoadClass(className, PLATFORM_DEFAULT_FACTORY_CLASS, getContextClassLoader()); 262 } catch (ClassNotFoundException e) { 263 throw new JAXBException(Messages.format(Messages.DEFAULT_PROVIDER_NOT_FOUND), e); 264 } 265 266 if (logger.isLoggable(Level.FINE)) { 267 // extra check to avoid costly which operation if not logged 268 logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)}); 269 } 270 271 return newInstance(classes, properties, spi); 272 } 273 274 static JAXBContext newInstance(Class[] classes, 275 Map properties, 276 Class spFactory) throws JAXBException { 277 try { 278 ModuleUtil.delegateAddOpensToImplModule(classes, spFactory); 279 280 Method m = spFactory.getMethod("createContext", Class[].class, Map.class); 281 Object obj = instantiateProviderIfNecessary(spFactory); 282 Object context = m.invoke(obj, classes, properties); 283 if (!(context instanceof JAXBContext)) { 284 // the cast would fail, so generate an exception with a nice message 285 throw handleClassCastException(context.getClass(), JAXBContext.class); 286 } 287 return (JAXBContext) context; 288 289 } catch (NoSuchMethodException | IllegalAccessException e) { 290 throw new JAXBException(e); 291 } catch (InvocationTargetException e) { 292 // throw if it is exception not to be wrapped 293 // otherwise, wrap with a JAXBException 294 Throwable x = handleInvocationTargetException(e); 295 296 throw new JAXBException(x); 297 } 298 } 299 300 static JAXBContext find(String factoryId, 301 String contextPath, 302 ClassLoader classLoader, 303 Map properties) throws JAXBException { 304 305 if (contextPath == null || contextPath.isEmpty()) { 306 // no context is specified 307 throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH)); 308 } 309 310 //ModuleUtil is mr-jar class, scans context path for jaxb classes on jdk9 and higher 311 Class[] contextPathClasses = ModuleUtil.getClassesFromContextPath(contextPath, classLoader); 312 313 //first try with classloader#getResource 314 String factoryClassName = jaxbProperties(contextPath, classLoader, factoryId); 315 if (factoryClassName == null && contextPathClasses != null) { 316 //try with class#getResource 317 factoryClassName = jaxbProperties(contextPathClasses, factoryId); 318 } 319 320 if (factoryClassName != null) { 321 return newInstance(contextPath, contextPathClasses, factoryClassName, classLoader, properties); 322 } 323 324 325 String factoryName = classNameFromSystemProperties(); 326 if (factoryName != null) return newInstance(contextPath, contextPathClasses, factoryName, classLoader, properties); 327 328 JAXBContextFactory obj = ServiceLoaderUtil.firstByServiceLoader( 329 JAXBContextFactory.class, logger, EXCEPTION_HANDLER); 330 331 if (obj != null) { 332 ModuleUtil.delegateAddOpensToImplModule(contextPathClasses, obj.getClass()); 333 return obj.createContext(contextPath, classLoader, properties); 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 ModuleUtil.delegateAddOpensToImplModule(classes, factory.getClass()); 389 return factory.createContext(classes, properties); 390 } 391 392 // to ensure backwards compatibility 393 String className = firstByServiceLoaderDeprecated(JAXBContext.class, getContextClassLoader()); 394 if (className != null) return newInstance(classes, properties, className); 395 396 logger.fine("Trying to create the platform default provider"); 397 Class ctxFactoryClass = 398 (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); 399 400 if (ctxFactoryClass != null) { 401 return newInstance(classes, properties, ctxFactoryClass); 402 } 403 404 // else no provider found 405 logger.fine("Trying to create the platform default provider"); 406 return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS); 407 } 408 409 410 /** 411 * first factoryId should be the preferred one, 412 * more of those can be provided to support backwards compatibility 413 */ 414 private static String classNameFromPackageProperties(URL packagePropertiesUrl, 415 String ... factoryIds) throws JAXBException { 416 417 logger.log(Level.FINE, "Trying to locate {0}", packagePropertiesUrl.toString()); 418 Properties props = loadJAXBProperties(packagePropertiesUrl); 419 for(String factoryId : factoryIds) { 420 if (props.containsKey(factoryId)) { 421 return props.getProperty(factoryId); 422 } 423 } 424 //Factory key not found 425 String propertiesUrl = packagePropertiesUrl.toExternalForm(); 426 String packageName = propertiesUrl.substring(0, propertiesUrl.indexOf("/jaxb.properties")); 427 throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryIds[0])); 428 } 429 430 private static String classNameFromSystemProperties() throws JAXBException { 431 432 String factoryClassName = getSystemProperty(JAXBContext.JAXB_CONTEXT_FACTORY); 433 if (factoryClassName != null) { 434 return factoryClassName; 435 } 436 // leave this here to assure compatibility 437 factoryClassName = getDeprecatedSystemProperty(JAXB_CONTEXT_FACTORY_DEPRECATED); 438 if (factoryClassName != null) { 439 return factoryClassName; 440 } 441 // leave this here to assure compatibility 442 factoryClassName = getDeprecatedSystemProperty(JAXBContext.class.getName()); 443 if (factoryClassName != null) { 444 return factoryClassName; 445 } 446 return null; 447 } 448 449 private static String getDeprecatedSystemProperty(String property) { 450 String value = getSystemProperty(property); 451 if (value != null) { 452 logger.log(Level.WARNING, "Using non-standard property: {0}. Property {1} should be used instead.", 453 new Object[] {property, JAXBContext.JAXB_CONTEXT_FACTORY}); 454 } 455 return value; 456 } 457 458 private static String getSystemProperty(String property) { 459 logger.log(Level.FINE, "Checking system property {0}", property); 460 String value = AccessController.doPrivileged(new GetPropertyAction(property)); 461 if (value != null) { 462 logger.log(Level.FINE, " found {0}", value); 463 } else { 464 logger.log(Level.FINE, " not found"); 465 } 466 return value; 467 } 468 469 private static Properties loadJAXBProperties(URL url) throws JAXBException { 470 471 try { 472 Properties props; 473 logger.log(Level.FINE, "loading props from {0}", url); 474 props = new Properties(); 475 InputStream is = url.openStream(); 476 props.load(is); 477 is.close(); 478 return props; 479 } catch (IOException ioe) { 480 logger.log(Level.FINE, "Unable to load " + url.toString(), ioe); 481 throw new JAXBException(ioe.toString(), ioe); 482 } 483 } 484 485 /** 486 * If run on JPMS package containing resource must be open unconditionally. 487 * 488 * @param classLoader classloader to load resource with 489 * @param resourceName qualified name of the resource 490 * @return resource url if found 491 */ 492 private static URL getResourceUrl(ClassLoader classLoader, String resourceName) { 493 URL url; 494 if (classLoader == null) 495 url = ClassLoader.getSystemResource(resourceName); 496 else 497 url = classLoader.getResource(resourceName); 498 return url; 499 } 500 501 private static URL getResourceUrl(Class<?> clazz, String resourceName) { 502 return clazz.getResource(resourceName); 503 } 504 505 506 /** 507 * Search the given ClassLoader for an instance of the specified class and 508 * return a string representation of the URL that points to the resource. 509 * 510 * @param clazz 511 * The class to search for 512 * @param loader 513 * The ClassLoader to search. If this parameter is null, then the 514 * system class loader will be searched 515 * @return 516 * the URL for the class or null if it wasn't found 517 */ 518 static URL which(Class clazz, ClassLoader loader) { 519 520 String classnameAsResource = clazz.getName().replace('.', '/') + ".class"; 521 522 if (loader == null) { 523 loader = getSystemClassLoader(); 524 } 525 526 return loader.getResource(classnameAsResource); 527 } 528 529 /** 530 * Get the URL for the Class from it's ClassLoader. 531 * 532 * Convenience method for {@link #which(Class, ClassLoader)}. 533 * 534 * Equivalent to calling: which(clazz, clazz.getClassLoader()) 535 * 536 * @param clazz 537 * The class to search for 538 * @return 539 * the URL for the class or null if it wasn't found 540 */ 541 static URL which(Class clazz) { 542 return which(clazz, getClassClassLoader(clazz)); 543 } 544 545 @SuppressWarnings("unchecked") 546 private static ClassLoader getContextClassLoader() { 547 if (System.getSecurityManager() == null) { 548 return Thread.currentThread().getContextClassLoader(); 549 } else { 550 return (ClassLoader) java.security.AccessController.doPrivileged( 551 new java.security.PrivilegedAction() { 552 @Override 553 public java.lang.Object run() { 554 return Thread.currentThread().getContextClassLoader(); 555 } 556 }); 557 } 558 } 559 560 @SuppressWarnings("unchecked") 561 private static ClassLoader getClassClassLoader(final Class c) { 562 if (System.getSecurityManager() == null) { 563 return c.getClassLoader(); 564 } else { 565 return (ClassLoader) java.security.AccessController.doPrivileged( 566 new java.security.PrivilegedAction() { 567 @Override 568 public java.lang.Object run() { 569 return c.getClassLoader(); 570 } 571 }); 572 } 573 } 574 575 private static ClassLoader getSystemClassLoader() { 576 if (System.getSecurityManager() == null) { 577 return ClassLoader.getSystemClassLoader(); 578 } else { 579 return (ClassLoader) java.security.AccessController.doPrivileged( 580 new java.security.PrivilegedAction() { 581 @Override 582 public java.lang.Object run() { 583 return ClassLoader.getSystemClassLoader(); 584 } 585 }); 586 } 587 } 588 589 // ServiceLoaderUtil.firstByServiceLoaderDeprecated should be used instead. 590 @Deprecated 591 static String firstByServiceLoaderDeprecated(Class spiClass, 592 ClassLoader classLoader) throws JAXBException { 593 594 final String jaxbContextFQCN = spiClass.getName(); 595 596 logger.fine("Searching META-INF/services"); 597 598 // search META-INF services next 599 BufferedReader r = null; 600 final String resource = "META-INF/services/" + jaxbContextFQCN; 601 try { 602 final InputStream resourceStream = 603 (classLoader == null) ? 604 ClassLoader.getSystemResourceAsStream(resource) : 605 classLoader.getResourceAsStream(resource); 606 607 if (resourceStream != null) { 608 r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8")); 609 String factoryClassName = r.readLine(); 610 if (factoryClassName != null) { 611 factoryClassName = factoryClassName.trim(); 612 } 613 r.close(); 614 logger.log(Level.FINE, "Configured factorty class:{0}", factoryClassName); 615 return factoryClassName; 616 } else { 617 logger.log(Level.FINE, "Unable to load:{0}", resource); 618 return null; 619 } 620 } catch (IOException e) { 621 throw new JAXBException(e); 622 } finally { 623 try { 624 if (r != null) { 625 r.close(); 626 } 627 } catch (IOException ex) { 628 logger.log(Level.SEVERE, "Unable to close resource: " + resource, ex); 629 } 630 } 631 } 632 633 private static String jaxbProperties(String contextPath, ClassLoader classLoader, String factoryId) throws JAXBException { 634 String[] packages = contextPath.split(":"); 635 636 for (String pkg : packages) { 637 String pkgUrl = pkg.replace('.', '/'); 638 URL jaxbPropertiesUrl = getResourceUrl(classLoader, pkgUrl + "/jaxb.properties"); 639 if (jaxbPropertiesUrl != null) { 640 return classNameFromPackageProperties(jaxbPropertiesUrl, 641 factoryId, JAXB_CONTEXT_FACTORY_DEPRECATED); 642 } 643 } 644 return null; 645 } 646 647 private static String jaxbProperties(Class[] classesFromContextPath, String factoryId) throws JAXBException { 648 for (Class c : classesFromContextPath) { 649 URL jaxbPropertiesUrl = getResourceUrl(c, "jaxb.properties"); 650 if (jaxbPropertiesUrl != null) { 651 return classNameFromPackageProperties(jaxbPropertiesUrl, factoryId, JAXB_CONTEXT_FACTORY_DEPRECATED); 652 } 653 } 654 return null; 655 } 656 657 }