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

changeset 0
373ffda63c9a
child 637
9c07ef4934dd
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.xml.bind;
27
28 import java.util.Iterator;
29 import java.io.BufferedReader;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.UnsupportedEncodingException;
34 import java.lang.reflect.InvocationTargetException;
35 import java.lang.reflect.Method;
36 import java.net.URL;
37 import java.util.Map;
38 import java.util.Properties;
39 import java.util.StringTokenizer;
40 import java.util.logging.ConsoleHandler;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43 import java.security.AccessController;
44
45 import static javax.xml.bind.JAXBContext.JAXB_CONTEXT_FACTORY;
46
47
48 /**
49 * This class is package private and therefore is not exposed as part of the
50 * JAXB API.
51 *
52 * This code is designed to implement the JAXB 1.0 spec pluggability feature
53 *
54 * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
55 * @see JAXBContext
56 */
57 class ContextFinder {
58 private static final Logger logger;
59 static {
60 logger = Logger.getLogger("javax.xml.bind");
61 try {
62 if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {
63 // disconnect the logger from a bigger framework (if any)
64 // and take the matters into our own hands
65 logger.setUseParentHandlers(false);
66 logger.setLevel(Level.ALL);
67 ConsoleHandler handler = new ConsoleHandler();
68 handler.setLevel(Level.ALL);
69 logger.addHandler(handler);
70 } else {
71 // don't change the setting of this logger
72 // to honor what other frameworks
73 // have done on configurations.
74 }
75 } catch(Throwable t) {
76 // just to be extra safe. in particular System.getProperty may throw
77 // SecurityException.
78 }
79 }
80
81 /**
82 * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
83 * throw the wrapped exception.
84 */
85 private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
86 Throwable t = x.getTargetException();
87 if( t != null ) {
88 if( t instanceof JAXBException )
89 // one of our exceptions, just re-throw
90 throw (JAXBException)t;
91 if( t instanceof RuntimeException )
92 // avoid wrapping exceptions unnecessarily
93 throw (RuntimeException)t;
94 if( t instanceof Error )
95 throw (Error)t;
96 }
97 }
98
99
100 /**
101 * Determine if two types (JAXBContext in this case) will generate a ClassCastException.
102 *
103 * For example, (targetType)originalType
104 *
105 * @param originalType
106 * The Class object of the type being cast
107 * @param targetType
108 * The Class object of the type that is being cast to
109 * @return JAXBException to be thrown.
110 */
111 private static JAXBException handleClassCastException(Class originalType, Class targetType) {
112 final URL targetTypeURL = which(targetType);
113
114 return new JAXBException(Messages.format(Messages.ILLEGAL_CAST,
115 // we don't care where the impl class is, we want to know where JAXBContext lives in the impl
116 // class' ClassLoader
117 getClassClassLoader(originalType).getResource("javax/xml/bind/JAXBContext.class"),
118 targetTypeURL));
119 }
120
121 /**
122 * Create an instance of a class using the specified ClassLoader
123 */
124 static JAXBContext newInstance( String contextPath,
125 String className,
126 ClassLoader classLoader,
127 Map properties )
128 throws JAXBException {
129 try {
130 Class spFactory = safeLoadClass(className,classLoader);
131 return newInstance(contextPath, spFactory, classLoader, properties);
132 } catch (ClassNotFoundException x) {
133 throw new JAXBException(
134 Messages.format( Messages.PROVIDER_NOT_FOUND, className ),
135 x);
136 } catch (RuntimeException x) {
137 // avoid wrapping RuntimeException to JAXBException,
138 // because it indicates a bug in this code.
139 throw x;
140 } catch (Exception x) {
141 // can't catch JAXBException because the method is hidden behind
142 // reflection. Root element collisions detected in the call to
143 // createContext() are reported as JAXBExceptions - just re-throw it
144 // some other type of exception - just wrap it
145 throw new JAXBException(
146 Messages.format( Messages.COULD_NOT_INSTANTIATE, className, x ),
147 x);
148 }
149 }
150
151 static JAXBContext newInstance( String contextPath,
152 Class spFactory,
153 ClassLoader classLoader,
154 Map properties )
155 throws JAXBException
156 {
157 try {
158 /*
159 * javax.xml.bind.context.factory points to a class which has a
160 * static method called 'createContext' that
161 * returns a javax.xml.JAXBContext.
162 */
163
164 Object context = null;
165
166 // first check the method that takes Map as the third parameter.
167 // this is added in 2.0.
168 try {
169 Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class,Map.class);
170 // any failure in invoking this method would be considered fatal
171 context = m.invoke(null,contextPath,classLoader,properties);
172 } catch (NoSuchMethodException e) {
173 // it's not an error for the provider not to have this method.
174 }
175
176 if(context==null) {
177 // try the old method that doesn't take properties. compatible with 1.0.
178 // it is an error for an implementation not to have both forms of the createContext method.
179 Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class);
180 // any failure in invoking this method would be considered fatal
181 context = m.invoke(null,contextPath,classLoader);
182 }
183
184 if(!(context instanceof JAXBContext)) {
185 // the cast would fail, so generate an exception with a nice message
186 throw handleClassCastException(context.getClass(), JAXBContext.class);
187 }
188 return (JAXBContext)context;
189 } catch (InvocationTargetException x) {
190 handleInvocationTargetException(x);
191 // for other exceptions, wrap the internal target exception
192 // with a JAXBException
193 Throwable e = x;
194 if(x.getTargetException()!=null)
195 e = x.getTargetException();
196
197 throw new JAXBException( Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, e ), e );
198 } catch (RuntimeException x) {
199 // avoid wrapping RuntimeException to JAXBException,
200 // because it indicates a bug in this code.
201 throw x;
202 } catch (Exception x) {
203 // can't catch JAXBException because the method is hidden behind
204 // reflection. Root element collisions detected in the call to
205 // createContext() are reported as JAXBExceptions - just re-throw it
206 // some other type of exception - just wrap it
207 throw new JAXBException(
208 Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, x ),
209 x);
210 }
211 }
212
213
214 /**
215 * Create an instance of a class using the thread context ClassLoader
216 */
217 static JAXBContext newInstance(
218 Class[] classes,
219 Map properties,
220 String className) throws JAXBException {
221 ClassLoader cl = getContextClassLoader();
222 Class spi;
223 try {
224 spi = safeLoadClass(className,cl);
225 } catch (ClassNotFoundException e) {
226 throw new JAXBException(e);
227 }
228
229 if(logger.isLoggable(Level.FINE)) {
230 // extra check to avoid costly which operation if not logged
231 logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)});
232 }
233
234 return newInstance(classes, properties, spi);
235 }
236
237 static JAXBContext newInstance(Class[] classes,
238 Map properties,
239 Class spFactory) throws JAXBException {
240 Method m;
241 try {
242 m = spFactory.getMethod("createContext", Class[].class, Map.class);
243 } catch (NoSuchMethodException e) {
244 throw new JAXBException(e);
245 }
246 try {
247 Object context = m.invoke(null, classes, properties);
248 if(!(context instanceof JAXBContext)) {
249 // the cast would fail, so generate an exception with a nice message
250 throw handleClassCastException(context.getClass(), JAXBContext.class);
251 }
252 return (JAXBContext)context;
253 } catch (IllegalAccessException e) {
254 throw new JAXBException(e);
255 } catch (InvocationTargetException e) {
256 handleInvocationTargetException(e);
257
258 Throwable x = e;
259 if (e.getTargetException() != null)
260 x = e.getTargetException();
261
262 throw new JAXBException(x);
263 }
264 }
265
266 static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties ) throws JAXBException {
267
268 // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP?
269
270 final String jaxbContextFQCN = JAXBContext.class.getName();
271
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
279 throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));
280
281
282 logger.fine("Searching jaxb.properties");
283
284 while( packages.hasMoreTokens() ) {
285 String packageName = packages.nextToken(":").replace('.','/');
286 // com.acme.foo - > com/acme/foo/jaxb.properties
287 propFileName = new StringBuilder().append(packageName).append("/jaxb.properties");
288
289 Properties props = loadJAXBProperties( classLoader, propFileName.toString() );
290 if (props != null) {
291 if (props.containsKey(factoryId)) {
292 factoryClassName = props.getProperty(factoryId);
293 return newInstance( contextPath, factoryClassName, classLoader, properties );
294 } else {
295 throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId));
296 }
297 }
298 }
299
300 logger.fine("Searching the system property");
301
302 // search for a system property second (javax.xml.bind.JAXBContext)
303 factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY));
304 if( factoryClassName != null ) {
305 return newInstance( contextPath, factoryClassName, classLoader, properties );
306 } else { // leave this here to assure compatibility
307 factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));
308 if( factoryClassName != null ) {
309 return newInstance( contextPath, factoryClassName, classLoader, properties );
310 }
311 }
312
313 // OSGi search
314 Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader();
315 if (jaxbContext != null) {
316 logger.fine("OSGi environment detected");
317 return newInstance(contextPath, jaxbContext, classLoader, properties);
318 }
319
320 logger.fine("Searching META-INF/services");
321 // search META-INF services next
322 BufferedReader r = null;
323 try {
324 final StringBuilder resource = new StringBuilder().append("META-INF/services/").append(jaxbContextFQCN);
325 final InputStream resourceStream =
326 classLoader.getResourceAsStream(resource.toString());
327
328 if (resourceStream != null) {
329 r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8"));
330 factoryClassName = r.readLine();
331 if (factoryClassName != null) {
332 factoryClassName = factoryClassName.trim();
333 }
334 r.close();
335 return newInstance(contextPath, factoryClassName, classLoader, properties);
336 } else {
337 logger.log(Level.FINE, "Unable to load:{0}", resource.toString());
338 }
339 } catch (UnsupportedEncodingException e) {
340 // should never happen
341 throw new JAXBException(e);
342 } catch (IOException e) {
343 throw new JAXBException(e);
344 } finally {
345 try {
346 if (r != null) {
347 r.close();
348 }
349 } catch (IOException ex) {
350 Logger.getLogger(ContextFinder.class.getName()).log(Level.SEVERE, null, ex);
351 }
352 }
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 }
627 }
628
629 }

mercurial