1 /* 2 * Copyright (c) 1997, 2015, 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 com.sun.xml.internal.bind.v2; 27 28 import java.lang.reflect.Constructor; 29 import java.lang.reflect.InvocationTargetException; 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Modifier; 32 import java.lang.ref.WeakReference; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 import java.util.Map; 36 import java.util.WeakHashMap; 37 import java.util.logging.Level; 38 import java.util.logging.Logger; 39 40 import com.sun.xml.internal.bind.Util; 41 42 /** 43 * Creates new instances of classes. 44 * 45 * <p> 46 * This code handles the case where the class is not public or the constructor is 47 * not public. 48 * 49 * @since 2.0 50 * @author Kohsuke Kawaguchi 51 */ 52 public final class ClassFactory { 53 private static final Class[] emptyClass = new Class[0]; 54 private static final Object[] emptyObject = new Object[0]; 55 56 private static final Logger logger = Util.getClassLogger(); 57 58 /** 59 * Cache from a class to its default constructor. 60 * 61 * To avoid synchronization among threads, we use {@link ThreadLocal}. 62 */ 63 private static final ThreadLocal<Map<Class, WeakReference<Constructor>>> tls = new ThreadLocal<Map<Class,WeakReference<Constructor>>>() { 64 @Override 65 public Map<Class,WeakReference<Constructor>> initialValue() { 66 return new WeakHashMap<Class,WeakReference<Constructor>>(); 67 } 68 }; 69 70 public static void cleanCache() { 71 if (tls != null) { 72 try { 73 tls.remove(); 74 } catch (Exception e) { 75 logger.log(Level.WARNING, "Unable to clean Thread Local cache of classes used in Unmarshaller: {0}", e.getLocalizedMessage()); 76 } 77 } 78 } 79 80 /** 81 * Creates a new instance of the class but throw exceptions without catching it. 82 */ 83 public static <T> T create0( final Class<T> clazz ) throws IllegalAccessException, InvocationTargetException, InstantiationException { 84 Map<Class,WeakReference<Constructor>> m = tls.get(); 85 Constructor<T> cons = null; 86 WeakReference<Constructor> consRef = m.get(clazz); 87 if(consRef!=null) 88 cons = consRef.get(); 89 if(cons==null) { 90 cons = AccessController.doPrivileged(new PrivilegedAction<Constructor<T>>() { 91 @Override 92 public Constructor<T> run() { 93 try { 94 return clazz.getDeclaredConstructor(emptyClass); 95 } catch (NoSuchMethodException e) { 96 logger.log(Level.INFO,"No default constructor found on "+clazz,e); 97 NoSuchMethodError exp; 98 if(clazz.getDeclaringClass()!=null && !Modifier.isStatic(clazz.getModifiers())) { 99 exp = new NoSuchMethodError(Messages.NO_DEFAULT_CONSTRUCTOR_IN_INNER_CLASS 100 .format(clazz.getName())); 101 } else { 102 exp = new NoSuchMethodError(e.getMessage()); 103 } 104 exp.initCause(e); 105 throw exp; 106 } 107 } 108 }); 109 110 int classMod = clazz.getModifiers(); 111 112 if(!Modifier.isPublic(classMod) || !Modifier.isPublic(cons.getModifiers())) { 113 // attempt to make it work even if the constructor is not accessible 114 try { 115 cons.setAccessible(true); 116 } catch(SecurityException e) { 117 // but if we don't have a permission to do so, work gracefully. 118 logger.log(Level.FINE,"Unable to make the constructor of "+clazz+" accessible",e); 119 throw e; 120 } 121 } 122 123 m.put(clazz,new WeakReference<Constructor>(cons)); 124 } 125 126 return cons.newInstance(emptyObject); 127 } 128 129 /** 130 * The same as {@link #create0} but with an error handling to make 131 * the instantiation error fatal. 132 */ 133 public static <T> T create( Class<T> clazz ) { 134 try { 135 return create0(clazz); 136 } catch (InstantiationException e) { 137 logger.log(Level.INFO,"failed to create a new instance of "+clazz,e); 138 throw new InstantiationError(e.toString()); 139 } catch (IllegalAccessException e) { 140 logger.log(Level.INFO,"failed to create a new instance of "+clazz,e); 141 throw new IllegalAccessError(e.toString()); 142 } catch (InvocationTargetException e) { 143 Throwable target = e.getTargetException(); 144 145 // most likely an error on the user's code. 146 // just let it through for the ease of debugging 147 if(target instanceof RuntimeException) 148 throw (RuntimeException)target; 149 150 // error. just forward it for the ease of debugging 151 if(target instanceof Error) 152 throw (Error)target; 153 154 // a checked exception. 155 // not sure how we should report this error, 156 // but for now we just forward it by wrapping it into a runtime exception 157 throw new IllegalStateException(target); 158 } 159 } 160 161 /** 162 * Call a method in the factory class to get the object. 163 */ 164 public static Object create(Method method) { 165 Throwable errorMsg; 166 try { 167 return method.invoke(null, emptyObject); 168 } catch (InvocationTargetException ive) { 169 Throwable target = ive.getTargetException(); 170 171 if(target instanceof RuntimeException) 172 throw (RuntimeException)target; 173 174 if(target instanceof Error) 175 throw (Error)target; 176 177 throw new IllegalStateException(target); 178 } catch (IllegalAccessException e) { 179 logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),e); 180 throw new IllegalAccessError(e.toString()); 181 } catch (IllegalArgumentException iae){ 182 logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),iae); 183 errorMsg = iae; 184 } catch (NullPointerException npe){ 185 logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),npe); 186 errorMsg = npe; 187 } catch (ExceptionInInitializerError eie){ 188 logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),eie); 189 errorMsg = eie; 190 } 191 192 NoSuchMethodError exp; 193 exp = new NoSuchMethodError(errorMsg.getMessage()); 194 exp.initCause(errorMsg); 195 throw exp; 196 } 197 198 /** 199 * Infers the instanciable implementation class that can be assigned to the given field type. 200 * 201 * @return null 202 * if inference fails. 203 */ 204 public static <T> Class<? extends T> inferImplClass(Class<T> fieldType, Class[] knownImplClasses) { 205 if(!fieldType.isInterface()) 206 return fieldType; 207 208 for( Class<?> impl : knownImplClasses ) { 209 if(fieldType.isAssignableFrom(impl)) 210 return impl.asSubclass(fieldType); 211 } 212 213 // if we can't find an implementation class, 214 // let's just hope that we will never need to create a new object, 215 // and returns null 216 return null; 217 } 218 }