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