Tue, 07 Nov 2017 18:54:04 -0800
Added tag jdk8u162-b06 for changeset 6095742f8034
ohair@286 | 1 | /* |
mkos@397 | 2 | * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. |
ohair@286 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
ohair@286 | 4 | * |
ohair@286 | 5 | * This code is free software; you can redistribute it and/or modify it |
ohair@286 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@286 | 7 | * published by the Free Software Foundation. Oracle designates this |
ohair@286 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@286 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
ohair@286 | 10 | * |
ohair@286 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
ohair@286 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
ohair@286 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
ohair@286 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
ohair@286 | 15 | * accompanied this code). |
ohair@286 | 16 | * |
ohair@286 | 17 | * You should have received a copy of the GNU General Public License version |
ohair@286 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
ohair@286 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
ohair@286 | 20 | * |
ohair@286 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@286 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@286 | 23 | * questions. |
ohair@286 | 24 | */ |
ohair@286 | 25 | |
ohair@286 | 26 | package javax.xml.bind; |
ohair@286 | 27 | |
ohair@286 | 28 | import java.util.Iterator; |
ohair@286 | 29 | import java.io.BufferedReader; |
ohair@286 | 30 | import java.io.IOException; |
ohair@286 | 31 | import java.io.InputStream; |
ohair@286 | 32 | import java.io.InputStreamReader; |
ohair@286 | 33 | import java.io.UnsupportedEncodingException; |
ohair@286 | 34 | import java.lang.reflect.InvocationTargetException; |
ohair@286 | 35 | import java.lang.reflect.Method; |
ohair@286 | 36 | import java.net.URL; |
ohair@286 | 37 | import java.util.Map; |
ohair@286 | 38 | import java.util.Properties; |
ohair@286 | 39 | import java.util.StringTokenizer; |
ohair@286 | 40 | import java.util.logging.ConsoleHandler; |
ohair@286 | 41 | import java.util.logging.Level; |
ohair@286 | 42 | import java.util.logging.Logger; |
ohair@286 | 43 | import java.security.AccessController; |
ohair@286 | 44 | |
ohair@286 | 45 | import static javax.xml.bind.JAXBContext.JAXB_CONTEXT_FACTORY; |
ohair@286 | 46 | |
ohair@286 | 47 | |
ohair@286 | 48 | /** |
ohair@286 | 49 | * This class is package private and therefore is not exposed as part of the |
ohair@286 | 50 | * JAXB API. |
ohair@286 | 51 | * |
ohair@286 | 52 | * This code is designed to implement the JAXB 1.0 spec pluggability feature |
ohair@286 | 53 | * |
ohair@286 | 54 | * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul> |
ohair@286 | 55 | * @see JAXBContext |
ohair@286 | 56 | */ |
ohair@286 | 57 | class ContextFinder { |
ohair@286 | 58 | private static final Logger logger; |
ohair@286 | 59 | static { |
ohair@286 | 60 | logger = Logger.getLogger("javax.xml.bind"); |
ohair@286 | 61 | try { |
ohair@286 | 62 | if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) { |
ohair@286 | 63 | // disconnect the logger from a bigger framework (if any) |
ohair@286 | 64 | // and take the matters into our own hands |
ohair@286 | 65 | logger.setUseParentHandlers(false); |
ohair@286 | 66 | logger.setLevel(Level.ALL); |
ohair@286 | 67 | ConsoleHandler handler = new ConsoleHandler(); |
ohair@286 | 68 | handler.setLevel(Level.ALL); |
ohair@286 | 69 | logger.addHandler(handler); |
ohair@286 | 70 | } else { |
ohair@286 | 71 | // don't change the setting of this logger |
ohair@286 | 72 | // to honor what other frameworks |
ohair@286 | 73 | // have done on configurations. |
ohair@286 | 74 | } |
ohair@286 | 75 | } catch(Throwable t) { |
ohair@286 | 76 | // just to be extra safe. in particular System.getProperty may throw |
ohair@286 | 77 | // SecurityException. |
ohair@286 | 78 | } |
ohair@286 | 79 | } |
ohair@286 | 80 | |
ohair@286 | 81 | /** |
ohair@286 | 82 | * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped, |
ohair@286 | 83 | * throw the wrapped exception. |
ohair@286 | 84 | */ |
ohair@286 | 85 | private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException { |
ohair@286 | 86 | Throwable t = x.getTargetException(); |
ohair@286 | 87 | if( t != null ) { |
ohair@286 | 88 | if( t instanceof JAXBException ) |
ohair@286 | 89 | // one of our exceptions, just re-throw |
ohair@286 | 90 | throw (JAXBException)t; |
ohair@286 | 91 | if( t instanceof RuntimeException ) |
ohair@286 | 92 | // avoid wrapping exceptions unnecessarily |
ohair@286 | 93 | throw (RuntimeException)t; |
ohair@286 | 94 | if( t instanceof Error ) |
ohair@286 | 95 | throw (Error)t; |
ohair@286 | 96 | } |
ohair@286 | 97 | } |
ohair@286 | 98 | |
ohair@286 | 99 | |
ohair@286 | 100 | /** |
ohair@286 | 101 | * Determine if two types (JAXBContext in this case) will generate a ClassCastException. |
ohair@286 | 102 | * |
ohair@286 | 103 | * For example, (targetType)originalType |
ohair@286 | 104 | * |
ohair@286 | 105 | * @param originalType |
ohair@286 | 106 | * The Class object of the type being cast |
ohair@286 | 107 | * @param targetType |
ohair@286 | 108 | * The Class object of the type that is being cast to |
ohair@286 | 109 | * @return JAXBException to be thrown. |
ohair@286 | 110 | */ |
ohair@286 | 111 | private static JAXBException handleClassCastException(Class originalType, Class targetType) { |
ohair@286 | 112 | final URL targetTypeURL = which(targetType); |
ohair@286 | 113 | |
ohair@286 | 114 | return new JAXBException(Messages.format(Messages.ILLEGAL_CAST, |
ohair@286 | 115 | // we don't care where the impl class is, we want to know where JAXBContext lives in the impl |
ohair@286 | 116 | // class' ClassLoader |
ohair@286 | 117 | getClassClassLoader(originalType).getResource("javax/xml/bind/JAXBContext.class"), |
ohair@286 | 118 | targetTypeURL)); |
ohair@286 | 119 | } |
ohair@286 | 120 | |
ohair@286 | 121 | /** |
ohair@286 | 122 | * Create an instance of a class using the specified ClassLoader |
ohair@286 | 123 | */ |
ohair@286 | 124 | static JAXBContext newInstance( String contextPath, |
ohair@286 | 125 | String className, |
ohair@286 | 126 | ClassLoader classLoader, |
ohair@286 | 127 | Map properties ) |
ohair@286 | 128 | throws JAXBException { |
ohair@286 | 129 | try { |
ohair@286 | 130 | Class spFactory = safeLoadClass(className,classLoader); |
ohair@286 | 131 | return newInstance(contextPath, spFactory, classLoader, properties); |
ohair@286 | 132 | } catch (ClassNotFoundException x) { |
ohair@286 | 133 | throw new JAXBException( |
ohair@286 | 134 | Messages.format( Messages.PROVIDER_NOT_FOUND, className ), |
ohair@286 | 135 | x); |
ohair@286 | 136 | } catch (RuntimeException x) { |
ohair@286 | 137 | // avoid wrapping RuntimeException to JAXBException, |
ohair@286 | 138 | // because it indicates a bug in this code. |
ohair@286 | 139 | throw x; |
ohair@286 | 140 | } catch (Exception x) { |
ohair@286 | 141 | // can't catch JAXBException because the method is hidden behind |
ohair@286 | 142 | // reflection. Root element collisions detected in the call to |
ohair@286 | 143 | // createContext() are reported as JAXBExceptions - just re-throw it |
ohair@286 | 144 | // some other type of exception - just wrap it |
ohair@286 | 145 | throw new JAXBException( |
ohair@286 | 146 | Messages.format( Messages.COULD_NOT_INSTANTIATE, className, x ), |
ohair@286 | 147 | x); |
ohair@286 | 148 | } |
ohair@286 | 149 | } |
ohair@286 | 150 | |
ohair@286 | 151 | static JAXBContext newInstance( String contextPath, |
ohair@286 | 152 | Class spFactory, |
ohair@286 | 153 | ClassLoader classLoader, |
ohair@286 | 154 | Map properties ) |
ohair@286 | 155 | throws JAXBException |
ohair@286 | 156 | { |
ohair@286 | 157 | try { |
ohair@286 | 158 | /* |
ohair@286 | 159 | * javax.xml.bind.context.factory points to a class which has a |
ohair@286 | 160 | * static method called 'createContext' that |
ohair@286 | 161 | * returns a javax.xml.JAXBContext. |
ohair@286 | 162 | */ |
ohair@286 | 163 | |
ohair@286 | 164 | Object context = null; |
ohair@286 | 165 | |
ohair@286 | 166 | // first check the method that takes Map as the third parameter. |
ohair@286 | 167 | // this is added in 2.0. |
ohair@286 | 168 | try { |
ohair@286 | 169 | Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class,Map.class); |
ohair@286 | 170 | // any failure in invoking this method would be considered fatal |
ohair@286 | 171 | context = m.invoke(null,contextPath,classLoader,properties); |
ohair@286 | 172 | } catch (NoSuchMethodException e) { |
ohair@286 | 173 | // it's not an error for the provider not to have this method. |
ohair@286 | 174 | } |
ohair@286 | 175 | |
ohair@286 | 176 | if(context==null) { |
ohair@286 | 177 | // try the old method that doesn't take properties. compatible with 1.0. |
ohair@286 | 178 | // it is an error for an implementation not to have both forms of the createContext method. |
ohair@286 | 179 | Method m = spFactory.getMethod("createContext",String.class,ClassLoader.class); |
ohair@286 | 180 | // any failure in invoking this method would be considered fatal |
ohair@286 | 181 | context = m.invoke(null,contextPath,classLoader); |
ohair@286 | 182 | } |
ohair@286 | 183 | |
ohair@286 | 184 | if(!(context instanceof JAXBContext)) { |
ohair@286 | 185 | // the cast would fail, so generate an exception with a nice message |
ohair@286 | 186 | throw handleClassCastException(context.getClass(), JAXBContext.class); |
ohair@286 | 187 | } |
ohair@286 | 188 | return (JAXBContext)context; |
ohair@286 | 189 | } catch (InvocationTargetException x) { |
ohair@286 | 190 | handleInvocationTargetException(x); |
ohair@286 | 191 | // for other exceptions, wrap the internal target exception |
ohair@286 | 192 | // with a JAXBException |
ohair@286 | 193 | Throwable e = x; |
ohair@286 | 194 | if(x.getTargetException()!=null) |
ohair@286 | 195 | e = x.getTargetException(); |
ohair@286 | 196 | |
ohair@286 | 197 | throw new JAXBException( Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, e ), e ); |
ohair@286 | 198 | } catch (RuntimeException x) { |
ohair@286 | 199 | // avoid wrapping RuntimeException to JAXBException, |
ohair@286 | 200 | // because it indicates a bug in this code. |
ohair@286 | 201 | throw x; |
ohair@286 | 202 | } catch (Exception x) { |
ohair@286 | 203 | // can't catch JAXBException because the method is hidden behind |
ohair@286 | 204 | // reflection. Root element collisions detected in the call to |
ohair@286 | 205 | // createContext() are reported as JAXBExceptions - just re-throw it |
ohair@286 | 206 | // some other type of exception - just wrap it |
ohair@286 | 207 | throw new JAXBException( |
ohair@286 | 208 | Messages.format( Messages.COULD_NOT_INSTANTIATE, spFactory, x ), |
ohair@286 | 209 | x); |
ohair@286 | 210 | } |
ohair@286 | 211 | } |
ohair@286 | 212 | |
ohair@286 | 213 | |
ohair@286 | 214 | /** |
ohair@286 | 215 | * Create an instance of a class using the thread context ClassLoader |
ohair@286 | 216 | */ |
ohair@286 | 217 | static JAXBContext newInstance( |
ohair@286 | 218 | Class[] classes, |
ohair@286 | 219 | Map properties, |
ohair@286 | 220 | String className) throws JAXBException { |
ohair@286 | 221 | ClassLoader cl = getContextClassLoader(); |
ohair@286 | 222 | Class spi; |
ohair@286 | 223 | try { |
ohair@286 | 224 | spi = safeLoadClass(className,cl); |
ohair@286 | 225 | } catch (ClassNotFoundException e) { |
ohair@286 | 226 | throw new JAXBException(e); |
ohair@286 | 227 | } |
ohair@286 | 228 | |
ohair@286 | 229 | if(logger.isLoggable(Level.FINE)) { |
ohair@286 | 230 | // extra check to avoid costly which operation if not logged |
ohair@286 | 231 | logger.log(Level.FINE, "loaded {0} from {1}", new Object[]{className, which(spi)}); |
ohair@286 | 232 | } |
ohair@286 | 233 | |
ohair@286 | 234 | return newInstance(classes, properties, spi); |
ohair@286 | 235 | } |
ohair@286 | 236 | |
ohair@286 | 237 | static JAXBContext newInstance(Class[] classes, |
ohair@286 | 238 | Map properties, |
ohair@286 | 239 | Class spFactory) throws JAXBException { |
ohair@286 | 240 | Method m; |
ohair@286 | 241 | try { |
ohair@286 | 242 | m = spFactory.getMethod("createContext", Class[].class, Map.class); |
ohair@286 | 243 | } catch (NoSuchMethodException e) { |
ohair@286 | 244 | throw new JAXBException(e); |
ohair@286 | 245 | } |
ohair@286 | 246 | try { |
ohair@286 | 247 | Object context = m.invoke(null, classes, properties); |
ohair@286 | 248 | if(!(context instanceof JAXBContext)) { |
ohair@286 | 249 | // the cast would fail, so generate an exception with a nice message |
ohair@286 | 250 | throw handleClassCastException(context.getClass(), JAXBContext.class); |
ohair@286 | 251 | } |
ohair@286 | 252 | return (JAXBContext)context; |
ohair@286 | 253 | } catch (IllegalAccessException e) { |
ohair@286 | 254 | throw new JAXBException(e); |
ohair@286 | 255 | } catch (InvocationTargetException e) { |
ohair@286 | 256 | handleInvocationTargetException(e); |
ohair@286 | 257 | |
ohair@286 | 258 | Throwable x = e; |
ohair@286 | 259 | if (e.getTargetException() != null) |
ohair@286 | 260 | x = e.getTargetException(); |
ohair@286 | 261 | |
ohair@286 | 262 | throw new JAXBException(x); |
ohair@286 | 263 | } |
ohair@286 | 264 | } |
ohair@286 | 265 | |
ohair@286 | 266 | static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties ) throws JAXBException { |
ohair@286 | 267 | |
ohair@286 | 268 | // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP? |
ohair@286 | 269 | |
ohair@286 | 270 | final String jaxbContextFQCN = JAXBContext.class.getName(); |
ohair@286 | 271 | |
ohair@286 | 272 | // search context path for jaxb.properties first |
ohair@286 | 273 | StringBuilder propFileName; |
ohair@286 | 274 | StringTokenizer packages = new StringTokenizer( contextPath, ":" ); |
ohair@286 | 275 | String factoryClassName; |
ohair@286 | 276 | |
ohair@286 | 277 | if(!packages.hasMoreTokens()) |
ohair@286 | 278 | // no context is specified |
ohair@286 | 279 | throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH)); |
ohair@286 | 280 | |
ohair@286 | 281 | |
ohair@286 | 282 | logger.fine("Searching jaxb.properties"); |
ohair@286 | 283 | |
ohair@286 | 284 | while( packages.hasMoreTokens() ) { |
ohair@286 | 285 | String packageName = packages.nextToken(":").replace('.','/'); |
ohair@286 | 286 | // com.acme.foo - > com/acme/foo/jaxb.properties |
ohair@286 | 287 | propFileName = new StringBuilder().append(packageName).append("/jaxb.properties"); |
ohair@286 | 288 | |
ohair@286 | 289 | Properties props = loadJAXBProperties( classLoader, propFileName.toString() ); |
ohair@286 | 290 | if (props != null) { |
ohair@286 | 291 | if (props.containsKey(factoryId)) { |
ohair@286 | 292 | factoryClassName = props.getProperty(factoryId); |
ohair@286 | 293 | return newInstance( contextPath, factoryClassName, classLoader, properties ); |
ohair@286 | 294 | } else { |
ohair@286 | 295 | throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId)); |
ohair@286 | 296 | } |
ohair@286 | 297 | } |
ohair@286 | 298 | } |
ohair@286 | 299 | |
ohair@286 | 300 | logger.fine("Searching the system property"); |
ohair@286 | 301 | |
ohair@286 | 302 | // search for a system property second (javax.xml.bind.JAXBContext) |
ohair@286 | 303 | factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY)); |
ohair@286 | 304 | if( factoryClassName != null ) { |
ohair@286 | 305 | return newInstance( contextPath, factoryClassName, classLoader, properties ); |
ohair@286 | 306 | } else { // leave this here to assure compatibility |
ohair@286 | 307 | factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN)); |
ohair@286 | 308 | if( factoryClassName != null ) { |
ohair@286 | 309 | return newInstance( contextPath, factoryClassName, classLoader, properties ); |
ohair@286 | 310 | } |
ohair@286 | 311 | } |
ohair@286 | 312 | |
mkos@397 | 313 | // OSGi search |
mkos@397 | 314 | Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader(); |
mkos@397 | 315 | if (jaxbContext != null) { |
mkos@397 | 316 | logger.fine("OSGi environment detected"); |
mkos@397 | 317 | return newInstance(contextPath, jaxbContext, classLoader, properties); |
ohair@286 | 318 | } |
ohair@286 | 319 | |
ohair@286 | 320 | logger.fine("Searching META-INF/services"); |
ohair@286 | 321 | // search META-INF services next |
mkos@397 | 322 | BufferedReader r = null; |
ohair@286 | 323 | try { |
ohair@286 | 324 | final StringBuilder resource = new StringBuilder().append("META-INF/services/").append(jaxbContextFQCN); |
ohair@286 | 325 | final InputStream resourceStream = |
ohair@286 | 326 | classLoader.getResourceAsStream(resource.toString()); |
ohair@286 | 327 | |
ohair@286 | 328 | if (resourceStream != null) { |
ohair@286 | 329 | r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8")); |
mkos@397 | 330 | factoryClassName = r.readLine(); |
mkos@397 | 331 | if (factoryClassName != null) { |
mkos@397 | 332 | factoryClassName = factoryClassName.trim(); |
mkos@397 | 333 | } |
ohair@286 | 334 | r.close(); |
ohair@286 | 335 | return newInstance(contextPath, factoryClassName, classLoader, properties); |
ohair@286 | 336 | } else { |
ohair@286 | 337 | logger.log(Level.FINE, "Unable to load:{0}", resource.toString()); |
ohair@286 | 338 | } |
ohair@286 | 339 | } catch (UnsupportedEncodingException e) { |
ohair@286 | 340 | // should never happen |
ohair@286 | 341 | throw new JAXBException(e); |
ohair@286 | 342 | } catch (IOException e) { |
ohair@286 | 343 | throw new JAXBException(e); |
mkos@397 | 344 | } finally { |
mkos@397 | 345 | try { |
mkos@397 | 346 | if (r != null) { |
mkos@397 | 347 | r.close(); |
mkos@397 | 348 | } |
mkos@397 | 349 | } catch (IOException ex) { |
mkos@397 | 350 | Logger.getLogger(ContextFinder.class.getName()).log(Level.SEVERE, null, ex); |
mkos@397 | 351 | } |
ohair@286 | 352 | } |
ohair@286 | 353 | |
ohair@286 | 354 | // else no provider found |
ohair@286 | 355 | logger.fine("Trying to create the platform default provider"); |
ohair@286 | 356 | return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties); |
ohair@286 | 357 | } |
ohair@286 | 358 | |
ohair@286 | 359 | static JAXBContext find( Class[] classes, Map properties ) throws JAXBException { |
ohair@286 | 360 | |
ohair@286 | 361 | final String jaxbContextFQCN = JAXBContext.class.getName(); |
ohair@286 | 362 | String factoryClassName; |
ohair@286 | 363 | |
ohair@286 | 364 | // search for jaxb.properties in the class loader of each class first |
ohair@286 | 365 | for (final Class c : classes) { |
ohair@286 | 366 | // this classloader is used only to load jaxb.properties, so doing this should be safe. |
ohair@286 | 367 | ClassLoader classLoader = getClassClassLoader(c); |
ohair@286 | 368 | Package pkg = c.getPackage(); |
ohair@286 | 369 | if(pkg==null) |
ohair@286 | 370 | continue; // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders |
ohair@286 | 371 | String packageName = pkg.getName().replace('.', '/'); |
ohair@286 | 372 | |
ohair@286 | 373 | // TODO: do we want to optimize away searching the same package? org.Foo, org.Bar, com.Baz |
ohair@286 | 374 | // classes from the same package might come from different class loades, so it might be a bad idea |
ohair@286 | 375 | |
ohair@286 | 376 | // TODO: it's easier to look things up from the class |
ohair@286 | 377 | // c.getResourceAsStream("jaxb.properties"); |
ohair@286 | 378 | |
ohair@286 | 379 | // build the resource name and use the property loader code |
ohair@286 | 380 | String resourceName = packageName+"/jaxb.properties"; |
ohair@286 | 381 | logger.log(Level.FINE, "Trying to locate {0}", resourceName); |
ohair@286 | 382 | Properties props = loadJAXBProperties(classLoader, resourceName); |
ohair@286 | 383 | if (props == null) { |
ohair@286 | 384 | logger.fine(" not found"); |
ohair@286 | 385 | } else { |
ohair@286 | 386 | logger.fine(" found"); |
ohair@286 | 387 | if (props.containsKey(JAXB_CONTEXT_FACTORY)) { |
ohair@286 | 388 | // trim() seems redundant, but adding to satisfy customer complaint |
ohair@286 | 389 | factoryClassName = props.getProperty(JAXB_CONTEXT_FACTORY).trim(); |
ohair@286 | 390 | return newInstance(classes, properties, factoryClassName); |
ohair@286 | 391 | } else { |
ohair@286 | 392 | throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, JAXB_CONTEXT_FACTORY)); |
ohair@286 | 393 | } |
ohair@286 | 394 | } |
ohair@286 | 395 | } |
ohair@286 | 396 | |
ohair@286 | 397 | // search for a system property second (javax.xml.bind.JAXBContext) |
ohair@286 | 398 | logger.log(Level.FINE, "Checking system property {0}", JAXBContext.JAXB_CONTEXT_FACTORY); |
ohair@286 | 399 | factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY)); |
ohair@286 | 400 | if (factoryClassName != null) { |
ohair@286 | 401 | logger.log(Level.FINE, " found {0}", factoryClassName); |
ohair@286 | 402 | return newInstance( classes, properties, factoryClassName ); |
ohair@286 | 403 | } else { // leave it here for compatibility reasons |
ohair@286 | 404 | logger.fine(" not found"); |
ohair@286 | 405 | logger.log(Level.FINE, "Checking system property {0}", jaxbContextFQCN); |
ohair@286 | 406 | factoryClassName = AccessController.doPrivileged(new GetPropertyAction(jaxbContextFQCN)); |
ohair@286 | 407 | if (factoryClassName != null) { |
ohair@286 | 408 | logger.log(Level.FINE, " found {0}", factoryClassName); |
ohair@286 | 409 | return newInstance( classes, properties, factoryClassName ); |
ohair@286 | 410 | } else { |
ohair@286 | 411 | logger.fine(" not found"); |
ohair@286 | 412 | } |
ohair@286 | 413 | } |
ohair@286 | 414 | |
mkos@397 | 415 | // OSGi search |
mkos@397 | 416 | Class jaxbContext = lookupJaxbContextUsingOsgiServiceLoader(); |
mkos@397 | 417 | if (jaxbContext != null) { |
ohair@286 | 418 | logger.fine("OSGi environment detected"); |
mkos@397 | 419 | return newInstance(classes, properties, jaxbContext); |
ohair@286 | 420 | } |
ohair@286 | 421 | |
ohair@286 | 422 | // search META-INF services next |
ohair@286 | 423 | logger.fine("Checking META-INF/services"); |
mkos@397 | 424 | BufferedReader r = null; |
ohair@286 | 425 | try { |
ohair@286 | 426 | final String resource = new StringBuilder("META-INF/services/").append(jaxbContextFQCN).toString(); |
ohair@286 | 427 | ClassLoader classLoader = getContextClassLoader(); |
ohair@286 | 428 | URL resourceURL; |
ohair@286 | 429 | if(classLoader==null) |
ohair@286 | 430 | resourceURL = ClassLoader.getSystemResource(resource); |
ohair@286 | 431 | else |
ohair@286 | 432 | resourceURL = classLoader.getResource(resource); |
ohair@286 | 433 | |
ohair@286 | 434 | if (resourceURL != null) { |
ohair@286 | 435 | logger.log(Level.FINE, "Reading {0}", resourceURL); |
ohair@286 | 436 | r = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "UTF-8")); |
mkos@397 | 437 | factoryClassName = r.readLine(); |
mkos@397 | 438 | if (factoryClassName != null) { |
mkos@397 | 439 | factoryClassName = factoryClassName.trim(); |
mkos@397 | 440 | } |
ohair@286 | 441 | return newInstance(classes, properties, factoryClassName); |
ohair@286 | 442 | } else { |
ohair@286 | 443 | logger.log(Level.FINE, "Unable to find: {0}", resource); |
ohair@286 | 444 | } |
ohair@286 | 445 | } catch (UnsupportedEncodingException e) { |
ohair@286 | 446 | // should never happen |
ohair@286 | 447 | throw new JAXBException(e); |
ohair@286 | 448 | } catch (IOException e) { |
ohair@286 | 449 | throw new JAXBException(e); |
mkos@397 | 450 | } finally { |
mkos@397 | 451 | if (r != null) { |
mkos@397 | 452 | try { |
mkos@397 | 453 | r.close(); |
mkos@397 | 454 | } catch (IOException ex) { |
mkos@397 | 455 | logger.log(Level.FINE, "Unable to close stream", ex); |
mkos@397 | 456 | } |
mkos@397 | 457 | } |
ohair@286 | 458 | } |
ohair@286 | 459 | |
ohair@286 | 460 | // else no provider found |
ohair@286 | 461 | logger.fine("Trying to create the platform default provider"); |
ohair@286 | 462 | return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS); |
ohair@286 | 463 | } |
ohair@286 | 464 | |
mkos@397 | 465 | private static Class lookupJaxbContextUsingOsgiServiceLoader() { |
ohair@286 | 466 | try { |
mkos@397 | 467 | // Use reflection to avoid having any dependency on ServiceLoader class |
ohair@286 | 468 | Class target = Class.forName("com.sun.org.glassfish.hk2.osgiresourcelocator.ServiceLoader"); |
ohair@286 | 469 | Method m = target.getMethod("lookupProviderClasses", Class.class); |
mkos@397 | 470 | Iterator iter = ((Iterable) m.invoke(null, JAXBContext.class)).iterator(); |
ohair@286 | 471 | return iter.hasNext() ? (Class)iter.next() : null; |
ohair@286 | 472 | } catch(Exception e) { |
mkos@397 | 473 | logger.log(Level.FINE, "Unable to find from OSGi: javax.xml.bind.JAXBContext"); |
ohair@286 | 474 | return null; |
ohair@286 | 475 | } |
ohair@286 | 476 | } |
ohair@286 | 477 | |
ohair@286 | 478 | private static Properties loadJAXBProperties( ClassLoader classLoader, |
ohair@286 | 479 | String propFileName ) |
ohair@286 | 480 | throws JAXBException { |
ohair@286 | 481 | |
ohair@286 | 482 | Properties props = null; |
ohair@286 | 483 | |
ohair@286 | 484 | try { |
ohair@286 | 485 | URL url; |
ohair@286 | 486 | if(classLoader==null) |
ohair@286 | 487 | url = ClassLoader.getSystemResource(propFileName); |
ohair@286 | 488 | else |
ohair@286 | 489 | url = classLoader.getResource( propFileName ); |
ohair@286 | 490 | |
ohair@286 | 491 | if( url != null ) { |
ohair@286 | 492 | logger.log(Level.FINE, "loading props from {0}", url); |
ohair@286 | 493 | props = new Properties(); |
ohair@286 | 494 | InputStream is = url.openStream(); |
ohair@286 | 495 | props.load( is ); |
ohair@286 | 496 | is.close(); |
ohair@286 | 497 | } |
ohair@286 | 498 | } catch( IOException ioe ) { |
ohair@286 | 499 | logger.log(Level.FINE,"Unable to load "+propFileName,ioe); |
ohair@286 | 500 | throw new JAXBException( ioe.toString(), ioe ); |
ohair@286 | 501 | } |
ohair@286 | 502 | |
ohair@286 | 503 | return props; |
ohair@286 | 504 | } |
ohair@286 | 505 | |
ohair@286 | 506 | |
ohair@286 | 507 | /** |
ohair@286 | 508 | * Search the given ClassLoader for an instance of the specified class and |
ohair@286 | 509 | * return a string representation of the URL that points to the resource. |
ohair@286 | 510 | * |
ohair@286 | 511 | * @param clazz |
ohair@286 | 512 | * The class to search for |
ohair@286 | 513 | * @param loader |
ohair@286 | 514 | * The ClassLoader to search. If this parameter is null, then the |
ohair@286 | 515 | * system class loader will be searched |
ohair@286 | 516 | * @return |
ohair@286 | 517 | * the URL for the class or null if it wasn't found |
ohair@286 | 518 | */ |
ohair@286 | 519 | static URL which(Class clazz, ClassLoader loader) { |
ohair@286 | 520 | |
ohair@286 | 521 | String classnameAsResource = clazz.getName().replace('.', '/') + ".class"; |
ohair@286 | 522 | |
ohair@286 | 523 | if(loader == null) { |
alanb@368 | 524 | loader = getSystemClassLoader(); |
ohair@286 | 525 | } |
ohair@286 | 526 | |
ohair@286 | 527 | return loader.getResource(classnameAsResource); |
ohair@286 | 528 | } |
ohair@286 | 529 | |
ohair@286 | 530 | /** |
ohair@286 | 531 | * Get the URL for the Class from it's ClassLoader. |
ohair@286 | 532 | * |
ohair@286 | 533 | * Convenience method for {@link #which(Class, ClassLoader)}. |
ohair@286 | 534 | * |
ohair@286 | 535 | * Equivalent to calling: which(clazz, clazz.getClassLoader()) |
ohair@286 | 536 | * |
ohair@286 | 537 | * @param clazz |
ohair@286 | 538 | * The class to search for |
ohair@286 | 539 | * @return |
ohair@286 | 540 | * the URL for the class or null if it wasn't found |
ohair@286 | 541 | */ |
ohair@286 | 542 | static URL which(Class clazz) { |
ohair@286 | 543 | return which(clazz, getClassClassLoader(clazz)); |
ohair@286 | 544 | } |
ohair@286 | 545 | |
ohair@286 | 546 | /** |
ohair@286 | 547 | * When JAXB is in J2SE, rt.jar has to have a JAXB implementation. |
ohair@286 | 548 | * However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext |
ohair@286 | 549 | * because if it has, it will take precedence over any file that applications have |
ohair@286 | 550 | * in their jar files. |
ohair@286 | 551 | * |
ohair@286 | 552 | * <p> |
ohair@286 | 553 | * When the user bundles his own JAXB implementation, we'd like to use it, and we |
ohair@286 | 554 | * want the platform default to be used only when there's no other JAXB provider. |
ohair@286 | 555 | * |
ohair@286 | 556 | * <p> |
ohair@286 | 557 | * For this reason, we have to hard-code the class name into the API. |
ohair@286 | 558 | */ |
ohair@286 | 559 | private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory"; |
ohair@286 | 560 | |
ohair@286 | 561 | /** |
ohair@286 | 562 | * Loads the class, provided that the calling thread has an access to the class being loaded. |
ohair@286 | 563 | */ |
ohair@286 | 564 | private static Class safeLoadClass(String className, ClassLoader classLoader) throws ClassNotFoundException { |
ohair@286 | 565 | logger.log(Level.FINE, "Trying to load {0}", className); |
ohair@286 | 566 | try { |
ohair@286 | 567 | // make sure that the current thread has an access to the package of the given name. |
ohair@286 | 568 | SecurityManager s = System.getSecurityManager(); |
ohair@286 | 569 | if (s != null) { |
ohair@286 | 570 | int i = className.lastIndexOf('.'); |
ohair@286 | 571 | if (i != -1) { |
ohair@286 | 572 | s.checkPackageAccess(className.substring(0,i)); |
ohair@286 | 573 | } |
ohair@286 | 574 | } |
ohair@286 | 575 | |
ohair@286 | 576 | if (classLoader == null) { |
ohair@286 | 577 | return Class.forName(className); |
ohair@286 | 578 | } else { |
ohair@286 | 579 | return classLoader.loadClass(className); |
ohair@286 | 580 | } |
ohair@286 | 581 | } catch (SecurityException se) { |
ohair@286 | 582 | // anyone can access the platform default factory class without permission |
ohair@286 | 583 | if (PLATFORM_DEFAULT_FACTORY_CLASS.equals(className)) { |
ohair@286 | 584 | return Class.forName(className); |
ohair@286 | 585 | } |
ohair@286 | 586 | throw se; |
ohair@286 | 587 | } |
ohair@286 | 588 | } |
ohair@286 | 589 | |
ohair@286 | 590 | private static ClassLoader getContextClassLoader() { |
ohair@286 | 591 | if (System.getSecurityManager() == null) { |
ohair@286 | 592 | return Thread.currentThread().getContextClassLoader(); |
ohair@286 | 593 | } else { |
ohair@286 | 594 | return (ClassLoader) java.security.AccessController.doPrivileged( |
ohair@286 | 595 | new java.security.PrivilegedAction() { |
ohair@286 | 596 | public java.lang.Object run() { |
ohair@286 | 597 | return Thread.currentThread().getContextClassLoader(); |
ohair@286 | 598 | } |
ohair@286 | 599 | }); |
ohair@286 | 600 | } |
ohair@286 | 601 | } |
ohair@286 | 602 | |
ohair@286 | 603 | private static ClassLoader getClassClassLoader(final Class c) { |
ohair@286 | 604 | if (System.getSecurityManager() == null) { |
ohair@286 | 605 | return c.getClassLoader(); |
ohair@286 | 606 | } else { |
ohair@286 | 607 | return (ClassLoader) java.security.AccessController.doPrivileged( |
ohair@286 | 608 | new java.security.PrivilegedAction() { |
ohair@286 | 609 | public java.lang.Object run() { |
ohair@286 | 610 | return c.getClassLoader(); |
ohair@286 | 611 | } |
ohair@286 | 612 | }); |
ohair@286 | 613 | } |
ohair@286 | 614 | } |
ohair@286 | 615 | |
alanb@368 | 616 | private static ClassLoader getSystemClassLoader() { |
alanb@368 | 617 | if (System.getSecurityManager() == null) { |
alanb@368 | 618 | return ClassLoader.getSystemClassLoader(); |
alanb@368 | 619 | } else { |
alanb@368 | 620 | return (ClassLoader) java.security.AccessController.doPrivileged( |
alanb@368 | 621 | new java.security.PrivilegedAction() { |
alanb@368 | 622 | public java.lang.Object run() { |
alanb@368 | 623 | return ClassLoader.getSystemClassLoader(); |
alanb@368 | 624 | } |
alanb@368 | 625 | }); |
alanb@368 | 626 | } |
alanb@368 | 627 | } |
alanb@368 | 628 | |
ohair@286 | 629 | } |