ClassFactory.java revision 678:4d5296e0920a
1243750Srwatson/* 2156283Srwatson * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 3156283Srwatson * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4243750Srwatson * 5243750Srwatson * This code is free software; you can redistribute it and/or modify it 6156283Srwatson * under the terms of the GNU General Public License version 2 only, as 7156283Srwatson * published by the Free Software Foundation. Oracle designates this 8156283Srwatson * particular file as subject to the "Classpath" exception as provided 9156283Srwatson * by Oracle in the LICENSE file that accompanied this code. 10156283Srwatson * 11156283Srwatson * This code is distributed in the hope that it will be useful, but WITHOUT 12156283Srwatson * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13156283Srwatson * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14156283Srwatson * version 2 for more details (a copy is included in the LICENSE file that 15156283Srwatson * accompanied this code). 16156283Srwatson * 17243750Srwatson * You should have received a copy of the GNU General Public License version 18243750Srwatson * 2 along with this work; if not, write to the Free Software Foundation, 19243750Srwatson * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20243750Srwatson * 21243750Srwatson * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22243750Srwatson * or visit www.oracle.com if you need additional information or have any 23243750Srwatson * questions. 24243750Srwatson */ 25243750Srwatson 26243750Srwatsonpackage com.sun.xml.internal.bind.v2; 27243750Srwatson 28243750Srwatsonimport java.lang.reflect.Constructor; 29243750Srwatsonimport java.lang.reflect.InvocationTargetException; 30243750Srwatsonimport java.lang.reflect.Method; 31243750Srwatsonimport java.lang.reflect.Modifier; 32243750Srwatsonimport java.lang.ref.WeakReference; 33243750Srwatsonimport java.security.AccessController; 34156283Srwatsonimport java.security.PrivilegedAction; 35243750Srwatsonimport java.util.Map; 36156283Srwatsonimport java.util.WeakHashMap; 37243750Srwatsonimport java.util.logging.Level; 38156283Srwatsonimport java.util.logging.Logger; 39156283Srwatson 40156283Srwatsonimport com.sun.xml.internal.bind.Util; 41156283Srwatson 42156283Srwatson/** 43156283Srwatson * Creates new instances of classes. 44156283Srwatson * 45156283Srwatson * <p> 46156283Srwatson * This code handles the case where the class is not public or the constructor is 47156283Srwatson * not public. 48156283Srwatson * 49156283Srwatson * @since 2.0 50156283Srwatson * @author Kohsuke Kawaguchi 51156283Srwatson */ 52156283Srwatsonpublic final class ClassFactory { 53156283Srwatson private static final Class[] emptyClass = new Class[0]; 54156283Srwatson private static final Object[] emptyObject = new Object[0]; 55156283Srwatson 56156283Srwatson private static final Logger logger = Util.getClassLogger(); 57156283Srwatson 58156283Srwatson /** 59156283Srwatson * Cache from a class to its default constructor. 60156283Srwatson * 61156283Srwatson * To avoid synchronization among threads, we use {@link ThreadLocal}. 62156283Srwatson */ 63156283Srwatson private static final ThreadLocal<Map<Class, WeakReference<Constructor>>> tls = new ThreadLocal<Map<Class,WeakReference<Constructor>>>() { 64156283Srwatson @Override 65243750Srwatson public Map<Class,WeakReference<Constructor>> initialValue() { 66156283Srwatson return new WeakHashMap<Class,WeakReference<Constructor>>(); 67156283Srwatson } 68156283Srwatson }; 69156283Srwatson 70173143Srwatson public static void cleanCache() { 71173143Srwatson if (tls != null) { 72173143Srwatson try { 73173143Srwatson tls.remove(); 74173143Srwatson } catch (Exception e) { 75243750Srwatson logger.log(Level.WARNING, "Unable to clean Thread Local cache of classes used in Unmarshaller: {0}", e.getLocalizedMessage()); 76243750Srwatson } 77243750Srwatson } 78243750Srwatson } 79243750Srwatson 80173143Srwatson /** 81173143Srwatson * Creates a new instance of the class but throw exceptions without catching it. 82243750Srwatson */ 83243750Srwatson public static <T> T create0( final Class<T> clazz ) throws IllegalAccessException, InvocationTargetException, InstantiationException { 84243750Srwatson Map<Class,WeakReference<Constructor>> m = tls.get(); 85156283Srwatson Constructor<T> cons = null; 86156283Srwatson WeakReference<Constructor> consRef = m.get(clazz); 87243750Srwatson if(consRef!=null) 88243750Srwatson cons = consRef.get(); 89156283Srwatson if(cons==null) { 90243750Srwatson if (System.getSecurityManager() == null) { 91243750Srwatson cons = tryGetDeclaredConstructor(clazz); 92243750Srwatson } else { 93243750Srwatson cons = AccessController.doPrivileged(new PrivilegedAction<Constructor<T>>() { 94243750Srwatson @Override 95243750Srwatson public Constructor<T> run() { 96243750Srwatson return tryGetDeclaredConstructor(clazz); 97243750Srwatson } 98243750Srwatson }); 99243750Srwatson } 100243750Srwatson 101243750Srwatson int classMod = clazz.getModifiers(); 102243750Srwatson 103243750Srwatson if(!Modifier.isPublic(classMod) || !Modifier.isPublic(cons.getModifiers())) { 104243750Srwatson // attempt to make it work even if the constructor is not accessible 105243750Srwatson try { 106243750Srwatson cons.setAccessible(true); 107243750Srwatson } catch(SecurityException e) { 108243750Srwatson // but if we don't have a permission to do so, work gracefully. 109243750Srwatson logger.log(Level.FINE,"Unable to make the constructor of "+clazz+" accessible",e); 110243750Srwatson throw e; 111243750Srwatson } 112243750Srwatson } 113243750Srwatson 114243750Srwatson m.put(clazz,new WeakReference<Constructor>(cons)); 115156283Srwatson } 116156283Srwatson return cons.newInstance(emptyObject); 117156283Srwatson } 118156283Srwatson 119156283Srwatson private static <T> Constructor<T> tryGetDeclaredConstructor(Class<T> clazz) { 120156283Srwatson try { 121156283Srwatson return clazz.getDeclaredConstructor((Class<T>[])emptyClass); 122156283Srwatson } catch (NoSuchMethodException e) { 123156283Srwatson logger.log(Level.INFO,"No default constructor found on "+clazz,e); 124156283Srwatson NoSuchMethodError exp; 125156283Srwatson if(clazz.getDeclaringClass()!=null && !Modifier.isStatic(clazz.getModifiers())) { 126156283Srwatson exp = new NoSuchMethodError(Messages.NO_DEFAULT_CONSTRUCTOR_IN_INNER_CLASS 127156283Srwatson .format(clazz.getName())); 128156283Srwatson } else { 129156283Srwatson exp = new NoSuchMethodError(e.getMessage()); 130243750Srwatson } 131191273Srwatson exp.initCause(e); 132243750Srwatson throw exp; 133156283Srwatson } 134156283Srwatson } 135156283Srwatson 136156283Srwatson /** 137156283Srwatson * The same as {@link #create0} but with an error handling to make 138243750Srwatson * the instantiation error fatal. 139173143Srwatson */ 140173143Srwatson public static <T> T create( Class<T> clazz ) { 141156283Srwatson try { 142156283Srwatson return create0(clazz); 143156283Srwatson } catch (InstantiationException e) { 144156283Srwatson logger.log(Level.INFO,"failed to create a new instance of "+clazz,e); 145243750Srwatson throw new InstantiationError(e.toString()); 146156283Srwatson } catch (IllegalAccessException e) { 147243750Srwatson logger.log(Level.INFO,"failed to create a new instance of "+clazz,e); 148243750Srwatson throw new IllegalAccessError(e.toString()); 149243750Srwatson } catch (InvocationTargetException e) { 150156283Srwatson Throwable target = e.getTargetException(); 151156283Srwatson 152156283Srwatson // most likely an error on the user's code. 153243750Srwatson // just let it through for the ease of debugging 154156283Srwatson if(target instanceof RuntimeException) 155156283Srwatson throw (RuntimeException)target; 156156283Srwatson 157156283Srwatson // error. just forward it for the ease of debugging 158243750Srwatson if(target instanceof Error) 159185573Srwatson throw (Error)target; 160173143Srwatson 161243750Srwatson // a checked exception. 162191273Srwatson // not sure how we should report this error, 163243750Srwatson // but for now we just forward it by wrapping it into a runtime exception 164156283Srwatson throw new IllegalStateException(target); 165243750Srwatson } 166243750Srwatson } 167156283Srwatson 168156283Srwatson /** 169156283Srwatson * Call a method in the factory class to get the object. 170156283Srwatson */ 171156283Srwatson public static Object create(Method method) { 172243750Srwatson Throwable errorMsg; 173156283Srwatson try { 174156283Srwatson return method.invoke(null, emptyObject); 175156283Srwatson } catch (InvocationTargetException ive) { 176173143Srwatson Throwable target = ive.getTargetException(); 177156283Srwatson 178156283Srwatson if(target instanceof RuntimeException) 179156283Srwatson throw (RuntimeException)target; 180156283Srwatson 181243750Srwatson if(target instanceof Error) 182243750Srwatson throw (Error)target; 183173143Srwatson 184173143Srwatson throw new IllegalStateException(target); 185173143Srwatson } catch (IllegalAccessException e) { 186173143Srwatson logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),e); 187243750Srwatson throw new IllegalAccessError(e.toString()); 188156283Srwatson } catch (IllegalArgumentException iae){ 189243750Srwatson logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),iae); 190156283Srwatson errorMsg = iae; 191156283Srwatson } catch (NullPointerException npe){ 192156283Srwatson logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),npe); 193156283Srwatson errorMsg = npe; 194156283Srwatson } catch (ExceptionInInitializerError eie){ 195156283Srwatson logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),eie); 196156283Srwatson errorMsg = eie; 197156283Srwatson } 198156283Srwatson 199156283Srwatson NoSuchMethodError exp; 200156283Srwatson exp = new NoSuchMethodError(errorMsg.getMessage()); 201173143Srwatson exp.initCause(errorMsg); 202156283Srwatson throw exp; 203173143Srwatson } 204173143Srwatson 205173143Srwatson /** 206156283Srwatson * Infers the instanciable implementation class that can be assigned to the given field type. 207156283Srwatson * 208156283Srwatson * @return null 209156283Srwatson * if inference fails. 210156283Srwatson */ 211156283Srwatson public static <T> Class<? extends T> inferImplClass(Class<T> fieldType, Class[] knownImplClasses) { 212173143Srwatson if(!fieldType.isInterface()) 213156283Srwatson return fieldType; 214156283Srwatson 215156283Srwatson for( Class<?> impl : knownImplClasses ) { 216156283Srwatson if(fieldType.isAssignableFrom(impl)) 217156283Srwatson return impl.asSubclass(fieldType); 218173143Srwatson } 219156283Srwatson 220156283Srwatson // if we can't find an implementation class, 221156283Srwatson // let's just hope that we will never need to create a new object, 222156283Srwatson // and returns null 223173143Srwatson return null; 224156283Srwatson } 225156283Srwatson} 226173143Srwatson