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

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