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