ohair@286: /* ohair@286: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.bind.v2; ohair@286: ohair@286: import java.lang.reflect.Constructor; ohair@286: import java.lang.reflect.InvocationTargetException; ohair@286: import java.lang.reflect.Method; ohair@286: import java.lang.reflect.Modifier; ohair@286: import java.lang.ref.WeakReference; ohair@286: import java.util.Map; ohair@286: import java.util.WeakHashMap; ohair@286: import java.util.logging.Level; ohair@286: import java.util.logging.Logger; ohair@286: ohair@286: import com.sun.xml.internal.bind.Util; ohair@286: ohair@286: /** ohair@286: * Creates new instances of classes. ohair@286: * ohair@286: *

ohair@286: * This code handles the case where the class is not public or the constructor is ohair@286: * not public. ohair@286: * ohair@286: * @since 2.0 ohair@286: * @author Kohsuke Kawaguchi ohair@286: */ ohair@286: public final class ClassFactory { ohair@286: private static final Class[] emptyClass = new Class[0]; ohair@286: private static final Object[] emptyObject = new Object[0]; ohair@286: ohair@286: private static final Logger logger = Util.getClassLogger(); ohair@286: ohair@286: /** ohair@286: * Cache from a class to its default constructor. ohair@286: * ohair@286: * To avoid synchronization among threads, we use {@link ThreadLocal}. ohair@286: */ ohair@286: private static final ThreadLocal>> tls = new ThreadLocal>>() { ohair@286: @Override ohair@286: public Map> initialValue() { ohair@286: return new WeakHashMap>(); ohair@286: } ohair@286: }; ohair@286: ohair@286: public static void cleanCache() { ohair@286: if (tls != null) { ohair@286: try { ohair@286: tls.remove(); ohair@286: } catch (Exception e) { ohair@286: logger.log(Level.WARNING, "Unable to clean Thread Local cache of classes used in Unmarshaller: {0}", e.getLocalizedMessage()); ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Creates a new instance of the class but throw exceptions without catching it. ohair@286: */ ohair@286: public static T create0( final Class clazz ) throws IllegalAccessException, InvocationTargetException, InstantiationException { ohair@286: Map> m = tls.get(); ohair@286: Constructor cons = null; ohair@286: WeakReference consRef = m.get(clazz); ohair@286: if(consRef!=null) ohair@286: cons = consRef.get(); ohair@286: if(cons==null) { ohair@286: try { ohair@286: cons = clazz.getDeclaredConstructor(emptyClass); ohair@286: } catch (NoSuchMethodException e) { ohair@286: logger.log(Level.INFO,"No default constructor found on "+clazz,e); ohair@286: NoSuchMethodError exp; ohair@286: if(clazz.getDeclaringClass()!=null && !Modifier.isStatic(clazz.getModifiers())) { ohair@286: exp = new NoSuchMethodError(Messages.NO_DEFAULT_CONSTRUCTOR_IN_INNER_CLASS.format(clazz.getName())); ohair@286: } else { ohair@286: exp = new NoSuchMethodError(e.getMessage()); ohair@286: } ohair@286: exp.initCause(e); ohair@286: throw exp; ohair@286: } ohair@286: ohair@286: int classMod = clazz.getModifiers(); ohair@286: ohair@286: if(!Modifier.isPublic(classMod) || !Modifier.isPublic(cons.getModifiers())) { ohair@286: // attempt to make it work even if the constructor is not accessible ohair@286: try { ohair@286: cons.setAccessible(true); ohair@286: } catch(SecurityException e) { ohair@286: // but if we don't have a permission to do so, work gracefully. ohair@286: logger.log(Level.FINE,"Unable to make the constructor of "+clazz+" accessible",e); ohair@286: throw e; ohair@286: } ohair@286: } ohair@286: ohair@286: m.put(clazz,new WeakReference(cons)); ohair@286: } ohair@286: ohair@286: return cons.newInstance(emptyObject); ohair@286: } ohair@286: ohair@286: /** ohair@286: * The same as {@link #create0} but with an error handling to make ohair@286: * the instanciation error fatal. ohair@286: */ ohair@286: public static T create( Class clazz ) { ohair@286: try { ohair@286: return create0(clazz); ohair@286: } catch (InstantiationException e) { ohair@286: logger.log(Level.INFO,"failed to create a new instance of "+clazz,e); ohair@286: throw new InstantiationError(e.toString()); ohair@286: } catch (IllegalAccessException e) { ohair@286: logger.log(Level.INFO,"failed to create a new instance of "+clazz,e); ohair@286: throw new IllegalAccessError(e.toString()); ohair@286: } catch (InvocationTargetException e) { ohair@286: Throwable target = e.getTargetException(); ohair@286: ohair@286: // most likely an error on the user's code. ohair@286: // just let it through for the ease of debugging ohair@286: if(target instanceof RuntimeException) ohair@286: throw (RuntimeException)target; ohair@286: ohair@286: // error. just forward it for the ease of debugging ohair@286: if(target instanceof Error) ohair@286: throw (Error)target; ohair@286: ohair@286: // a checked exception. ohair@286: // not sure how we should report this error, ohair@286: // but for now we just forward it by wrapping it into a runtime exception ohair@286: throw new IllegalStateException(target); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Call a method in the factory class to get the object. ohair@286: */ ohair@286: public static Object create(Method method) { ohair@286: Throwable errorMsg; ohair@286: try { ohair@286: return method.invoke(null, emptyObject); ohair@286: } catch (InvocationTargetException ive) { ohair@286: Throwable target = ive.getTargetException(); ohair@286: ohair@286: if(target instanceof RuntimeException) ohair@286: throw (RuntimeException)target; ohair@286: ohair@286: if(target instanceof Error) ohair@286: throw (Error)target; ohair@286: ohair@286: throw new IllegalStateException(target); ohair@286: } catch (IllegalAccessException e) { ohair@286: logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),e); ohair@286: throw new IllegalAccessError(e.toString()); ohair@286: } catch (IllegalArgumentException iae){ ohair@286: logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),iae); ohair@286: errorMsg = iae; ohair@286: } catch (NullPointerException npe){ ohair@286: logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),npe); ohair@286: errorMsg = npe; ohair@286: } catch (ExceptionInInitializerError eie){ ohair@286: logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),eie); ohair@286: errorMsg = eie; ohair@286: } ohair@286: ohair@286: NoSuchMethodError exp; ohair@286: exp = new NoSuchMethodError(errorMsg.getMessage()); ohair@286: exp.initCause(errorMsg); ohair@286: throw exp; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Infers the instanciable implementation class that can be assigned to the given field type. ohair@286: * ohair@286: * @return null ohair@286: * if inference fails. ohair@286: */ ohair@286: public static Class inferImplClass(Class fieldType, Class[] knownImplClasses) { ohair@286: if(!fieldType.isInterface()) ohair@286: return fieldType; ohair@286: ohair@286: for( Class impl : knownImplClasses ) { ohair@286: if(fieldType.isAssignableFrom(impl)) ohair@286: return impl.asSubclass(fieldType); ohair@286: } ohair@286: ohair@286: // if we can't find an implementation class, ohair@286: // let's just hope that we will never need to create a new object, ohair@286: // and returns null ohair@286: return null; ohair@286: } ohair@286: }