KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > xml > bind > ContextFinder


1 /*
2  * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
3  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4  */

5
6 package javax.xml.bind;
7
8 import java.io.BufferedReader JavaDoc;
9 import java.io.IOException JavaDoc;
10 import java.io.InputStream JavaDoc;
11 import java.io.InputStreamReader JavaDoc;
12 import java.io.UnsupportedEncodingException JavaDoc;
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.lang.reflect.Method JavaDoc;
15 import java.net.URL JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.Properties JavaDoc;
18 import java.util.StringTokenizer JavaDoc;
19 import java.util.logging.ConsoleHandler JavaDoc;
20 import java.util.logging.Level JavaDoc;
21 import java.util.logging.Logger JavaDoc;
22 import java.security.AccessController JavaDoc;
23 import java.security.PrivilegedAction JavaDoc;
24
25 import static javax.xml.bind.JAXBContext.JAXB_CONTEXT_FACTORY;
26
27 //import java.lang.reflect.InvocationTargetException;
28

29 /**
30  * This class is package private and therefore is not exposed as part of the
31  * JAXB API.
32  *
33  * This code is designed to implement the JAXB 1.0 spec pluggability feature
34  *
35  * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul>
36  * @version $Revision$
37  * @see JAXBContext
38  */

39 class ContextFinder {
40     private static final Logger JavaDoc logger;
41     static {
42         logger = Logger.getLogger("javax.xml.bind");
43         try {
44             if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) {
45                 // disconnect the logger from a bigger framework (if any)
46
// and take the matters into our own hands
47
logger.setUseParentHandlers(false);
48                 logger.setLevel(Level.ALL);
49                 ConsoleHandler JavaDoc handler = new ConsoleHandler JavaDoc();
50                 handler.setLevel(Level.ALL);
51                 logger.addHandler(handler);
52             } else {
53                 // don't change the setting of this logger
54
// to honor what other frameworks
55
// have done on configurations.
56
}
57         } catch(Throwable JavaDoc t) {
58             // just to be extra safe. in particular System.getProperty may throw
59
// SecurityException.
60
}
61     }
62
63     /**
64      * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
65      * throw the wrapped exception.
66      */

67     private static void handleInvocationTargetException(InvocationTargetException JavaDoc x) throws JAXBException {
68         Throwable JavaDoc t = x.getTargetException();
69         if( t != null ) {
70             if( t instanceof JAXBException )
71                 // one of our exceptions, just re-throw
72
throw (JAXBException)t;
73             if( t instanceof RuntimeException JavaDoc )
74                 // avoid wrapping exceptions unnecessarily
75
throw (RuntimeException JavaDoc)t;
76             if( t instanceof Error JavaDoc )
77                 throw (Error JavaDoc)t;
78         }
79     }
80
81
82     /**
83      * Determine if two types (JAXBContext in this case) will generate a ClassCastException.
84      *
85      * For example, (targetType)originalType
86      *
87      * @param originalType
88      * The Class object of the type being cast
89      * @param targetType
90      * The Class object of the type that is being cast to
91      * @return JAXBException to be thrown.
92      */

93     private static JAXBException handleClassCastException(Class JavaDoc originalType, Class JavaDoc targetType) {
94         final URL JavaDoc targetTypeURL = which(targetType);
95
96         return new JAXBException(Messages.format(Messages.ILLEGAL_CAST,
97                 // we don't care where the impl class is, we want to know where JAXBContext lives in the impl
98
// class' ClassLoader
99
originalType.getClassLoader().getResource("javax/xml/bind/JAXBContext.class"),
100                 targetTypeURL));
101     }
102
103     /**
104      * Create an instance of a class using the specified ClassLoader
105      */

106     static JAXBContext newInstance( String JavaDoc contextPath,
107                                String JavaDoc className,
108                                ClassLoader JavaDoc classLoader,
109                                Map JavaDoc properties )
110         throws JAXBException
111     {
112         try {
113             Class JavaDoc spiClass;
114             if (classLoader == null) {
115                 spiClass = Class.forName(className);
116             } else {
117                 spiClass = classLoader.loadClass(className);
118             }
119
120             /*
121              * javax.xml.bind.context.factory points to a class which has a
122              * static method called 'createContext' that
123              * returns a javax.xml.JAXBContext.
124              */

125
126             Object JavaDoc context = null;
127
128             // first check the method that takes Map as the third parameter.
129
// this is added in 2.0.
130
try {
131                 Method JavaDoc m = spiClass.getMethod("createContext",String JavaDoc.class,ClassLoader JavaDoc.class,Map JavaDoc.class);
132                 // any failure in invoking this method would be considered fatal
133
context = m.invoke(null,contextPath,classLoader,properties);
134             } catch (NoSuchMethodException JavaDoc e) {
135                 // it's not an error for the provider not to have this method.
136
}
137
138             if(context==null) {
139                 // try the old method that doesn't take properties. compatible with 1.0.
140
// it is an error for an implementation not to have both forms of the createContext method.
141
Method JavaDoc m = spiClass.getMethod("createContext",String JavaDoc.class,ClassLoader JavaDoc.class);
142                 // any failure in invoking this method would be considered fatal
143
context = m.invoke(null,contextPath,classLoader);
144             }
145
146             if(!(context instanceof JAXBContext)) {
147                 // the cast would fail, so generate an exception with a nice message
148
handleClassCastException(context.getClass(), JAXBContext.class);
149             }
150             return (JAXBContext)context;
151         } catch (ClassNotFoundException JavaDoc x) {
152             throw new JAXBException(
153                 Messages.format( Messages.PROVIDER_NOT_FOUND, className ),
154                 x);
155         } catch (InvocationTargetException JavaDoc x) {
156             handleInvocationTargetException(x);
157             // for other exceptions, wrap the internal target exception
158
// with a JAXBException
159
Throwable JavaDoc e = x;
160             if(x.getTargetException()!=null)
161                 e = x.getTargetException();
162
163             throw new JAXBException( Messages.format( Messages.COULD_NOT_INSTANTIATE, className, e ), e );
164         } catch (RuntimeException JavaDoc x) {
165             // avoid wrapping RuntimeException to JAXBException,
166
// because it indicates a bug in this code.
167
throw x;
168         } catch (Exception JavaDoc x) {
169             // can't catch JAXBException because the method is hidden behind
170
// reflection. Root element collisions detected in the call to
171
// createContext() are reported as JAXBExceptions - just re-throw it
172
// some other type of exception - just wrap it
173
throw new JAXBException(
174                 Messages.format( Messages.COULD_NOT_INSTANTIATE, className, x ),
175                 x);
176         }
177     }
178
179
180     /**
181      * Create an instance of a class using the specified ClassLoader
182      */

183     static JAXBContext newInstance(
184                               Class JavaDoc[] classes,
185                               Map JavaDoc properties,
186                               String JavaDoc className) throws JAXBException {
187         ClassLoader JavaDoc cl = Thread.currentThread().getContextClassLoader();
188         Class JavaDoc spi;
189         try {
190             logger.fine("Trying to load "+className);
191             if (cl != null)
192                 spi = cl.loadClass(className);
193             else
194                 spi = Class.forName(className);
195         } catch (ClassNotFoundException JavaDoc e) {
196             throw new JAXBException(e);
197         }
198
199         if(logger.isLoggable(Level.FINE)) {
200             // extra check to avoid costly which operation if not logged
201
logger.fine("loaded "+className+" from "+which(spi));
202         }
203
204         Method JavaDoc m;
205         try {
206             m = spi.getMethod("createContext", Class JavaDoc[].class, Map JavaDoc.class);
207         } catch (NoSuchMethodException JavaDoc e) {
208             throw new JAXBException(e);
209         }
210         try {
211             Object JavaDoc context = m.invoke(null, classes, properties);
212             if(!(context instanceof JAXBContext)) {
213                 // the cast would fail, so generate an exception with a nice message
214
throw handleClassCastException(context.getClass(), JAXBContext.class);
215             }
216             return (JAXBContext)context;
217         } catch (IllegalAccessException JavaDoc e) {
218             throw new JAXBException(e);
219         } catch (InvocationTargetException JavaDoc e) {
220             handleInvocationTargetException(e);
221
222             Throwable JavaDoc x = e;
223             if (e.getTargetException() != null)
224                 x = e.getTargetException();
225
226             throw new JAXBException(x);
227         }
228     }
229
230
231     static JAXBContext find(String JavaDoc factoryId, String JavaDoc contextPath, ClassLoader JavaDoc classLoader, Map JavaDoc properties ) throws JAXBException {
232
233         // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP?
234

235         final String JavaDoc jaxbContextFQCN = JAXBContext.class.getName();
236
237         // search context path for jaxb.properties first
238
StringBuilder JavaDoc propFileName;
239         StringTokenizer JavaDoc packages = new StringTokenizer JavaDoc( contextPath, ":" );
240         String JavaDoc factoryClassName;
241
242         if(!packages.hasMoreTokens())
243             // no context is specified
244
throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));
245
246
247         logger.fine("Searching jaxb.properties");
248
249         while( packages.hasMoreTokens() ) {
250             String JavaDoc packageName = packages.nextToken(":").replace('.','/');
251             // com.acme.foo - > com/acme/foo/jaxb.properties
252
propFileName = new StringBuilder JavaDoc().append(packageName).append("/jaxb.properties");
253
254             Properties JavaDoc props = loadJAXBProperties( classLoader, propFileName.toString() );
255             if (props != null) {
256                 if (props.containsKey(factoryId)) {
257                     factoryClassName = props.getProperty(factoryId);
258                     return newInstance( contextPath, factoryClassName, classLoader, properties );
259                 } else {
260                     throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId));
261                 }
262             }
263         }
264
265         logger.fine("Searching the system property");
266
267         // search for a system property second (javax.xml.bind.JAXBContext)
268
factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));
269         if( factoryClassName != null ) {
270             return newInstance( contextPath, factoryClassName, classLoader, properties );
271         }
272
273         logger.fine("Searching META-INF/services");
274
275         // search META-INF services next
276
BufferedReader JavaDoc r;
277         try {
278             final StringBuilder JavaDoc resource = new StringBuilder JavaDoc().append("META-INF/services/").append(jaxbContextFQCN);
279             final InputStream JavaDoc resourceStream =
280                     classLoader.getResourceAsStream(resource.toString());
281             
282             if (resourceStream != null) {
283                 r = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(resourceStream, "UTF-8"));
284                 factoryClassName = r.readLine().trim();
285                 r.close();
286                 return newInstance(contextPath, factoryClassName, classLoader, properties);
287             } else {
288                 logger.fine("Unable to load:" + resource.toString());
289             }
290         } catch (UnsupportedEncodingException JavaDoc e) {
291             // should never happen
292
throw new JAXBException(e);
293         } catch (IOException JavaDoc e) {
294             throw new JAXBException(e);
295         }
296
297         // else no provider found
298
logger.fine("Trying to create the platform default provider");
299         return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties);
300     }
301
302     // TODO: log each step in the look up process
303
static JAXBContext find( Class JavaDoc[] classes, Map JavaDoc properties ) throws JAXBException {
304
305         // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP?
306

307         final String JavaDoc jaxbContextFQCN = JAXBContext.class.getName();
308         String JavaDoc factoryClassName;
309
310         // search for jaxb.properties in the class loader of each class first
311
for (final Class JavaDoc c : classes) {
312             // this classloader is used only to load jaxb.properties, so doing this should be safe.
313
ClassLoader JavaDoc classLoader = AccessController.doPrivileged(new PrivilegedAction JavaDoc<ClassLoader JavaDoc>() {
314                 public ClassLoader JavaDoc run() {
315                     return c.getClassLoader();
316                 }
317             });
318             Package JavaDoc pkg = c.getPackage();
319             if(pkg==null)
320                 continue; // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders
321
String JavaDoc packageName = pkg.getName().replace('.', '/');
322
323             // TODO: do we want to optimize away searching the same package? org.Foo, org.Bar, com.Baz
324
// classes from the same package might come from different class loades, so it might be a bad idea
325

326             // TODO: it's easier to look things up from the class
327
// c.getResourceAsStream("jaxb.properties");
328

329             // build the resource name and use the property loader code
330
String JavaDoc resourceName = packageName+"/jaxb.properties";
331             logger.fine("Trying to locate "+resourceName);
332             Properties JavaDoc props = loadJAXBProperties(classLoader, resourceName);
333             if (props == null) {
334                 logger.fine(" not found");
335             } else {
336                 logger.fine(" found");
337                 if (props.containsKey(JAXB_CONTEXT_FACTORY)) {
338                     // trim() seems redundant, but adding to satisfy customer complaint
339
factoryClassName = props.getProperty(JAXB_CONTEXT_FACTORY).trim();
340                     return newInstance(classes, properties, factoryClassName);
341                 } else {
342                     throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, JAXB_CONTEXT_FACTORY));
343                 }
344             }
345         }
346
347         // search for a system property second (javax.xml.bind.JAXBContext)
348
logger.fine("Checking system property "+jaxbContextFQCN);
349         factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN));
350         if( factoryClassName != null ) {
351             logger.fine(" found "+factoryClassName);
352             return newInstance( classes, properties, factoryClassName );
353         }
354         logger.fine(" not found");
355
356         // search META-INF services next
357
logger.fine("Checking META-INF/services");
358         BufferedReader JavaDoc r;
359         try {
360             final String JavaDoc resource = new StringBuilder JavaDoc("META-INF/services/").append(jaxbContextFQCN).toString();
361             ClassLoader JavaDoc classLoader = Thread.currentThread().getContextClassLoader();
362             URL JavaDoc resourceURL;
363             if(classLoader==null)
364                 resourceURL = ClassLoader.getSystemResource(resource);
365             else
366                 resourceURL = classLoader.getResource(resource);
367
368             if (resourceURL != null) {
369                 logger.fine("Reading "+resourceURL);
370                 r = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(resourceURL.openStream(), "UTF-8"));
371                 factoryClassName = r.readLine().trim();
372                 return newInstance(classes, properties, factoryClassName);
373             } else {
374                 logger.fine("Unable to find: " + resource);
375             }
376         } catch (UnsupportedEncodingException JavaDoc e) {
377             // should never happen
378
throw new JAXBException(e);
379         } catch (IOException JavaDoc e) {
380             throw new JAXBException(e);
381         }
382
383         // else no provider found
384
logger.fine("Trying to create the platform default provider");
385         return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS);
386     }
387
388
389     private static Properties JavaDoc loadJAXBProperties( ClassLoader JavaDoc classLoader,
390                                                   String JavaDoc propFileName )
391         throws JAXBException {
392                                             
393         Properties JavaDoc props = null;
394                                                     
395         try {
396             URL JavaDoc url;
397             if(classLoader==null)
398                 url = ClassLoader.getSystemResource(propFileName);
399             else
400                 url = classLoader.getResource( propFileName );
401
402             if( url != null ) {
403                 logger.fine("loading props from "+url);
404                 props = new Properties JavaDoc();
405                 InputStream JavaDoc is = url.openStream();
406                 props.load( is );
407                 is.close();
408             }
409         } catch( IOException JavaDoc ioe ) {
410             logger.log(Level.FINE,"Unable to load "+propFileName,ioe);
411             throw new JAXBException( ioe.toString(), ioe );
412         }
413         
414         return props;
415     }
416
417
418     /**
419      * Search the given ClassLoader for an instance of the specified class and
420      * return a string representation of the URL that points to the resource.
421      *
422      * @param clazz
423      * The class to search for
424      * @param loader
425      * The ClassLoader to search. If this parameter is null, then the
426      * system class loader will be searched
427      * @return
428      * the URL for the class or null if it wasn't found
429      */

430     static URL JavaDoc which(Class JavaDoc clazz, ClassLoader JavaDoc loader) {
431
432         String JavaDoc classnameAsResource = clazz.getName().replace('.', '/') + ".class";
433
434         if(loader == null) {
435             loader = ClassLoader.getSystemClassLoader();
436         }
437
438         return loader.getResource(classnameAsResource);
439     }
440
441     /**
442      * Get the URL for the Class from it's ClassLoader.
443      *
444      * Convenience method for {@link #which(Class, ClassLoader)}.
445      *
446      * Equivalent to calling: which(clazz, clazz.getClassLoader())
447      *
448      * @param clazz
449      * The class to search for
450      * @return
451      * the URL for the class or null if it wasn't found
452      */

453     static URL JavaDoc which(Class JavaDoc clazz) {
454         return which(clazz, clazz.getClassLoader());
455     }
456
457     /**
458      * When JAXB is in J2SE, rt.jar has to have a JAXB implementation.
459      * However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext
460      * because if it has, it will take precedence over any file that applications have
461      * in their jar files.
462      *
463      * <p>
464      * When the user bundles his own JAXB implementation, we'd like to use it, and we
465      * want the platform default to be used only when there's no other JAXB provider.
466      *
467      * <p>
468      * For this reason, we have to hard-code the class name into the API.
469      */

470     private static final String JavaDoc PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory";
471 }
472
Popular Tags