001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2;
038:
039: import java.lang.reflect.Constructor;
040: import java.lang.reflect.InvocationTargetException;
041: import java.lang.reflect.Method;
042: import java.lang.reflect.Modifier;
043: import java.lang.ref.WeakReference;
044: import java.util.ArrayList;
045: import java.util.HashSet;
046: import java.util.LinkedList;
047: import java.util.Map;
048: import java.util.Stack;
049: import java.util.TreeSet;
050: import java.util.WeakHashMap;
051: import java.util.logging.Level;
052: import java.util.logging.Logger;
053:
054: import com.sun.xml.bind.Util;
055:
056: /**
057: * Creates new instances of classes.
058: *
059: * <p>
060: * This code handles the case where the class is not public or the constructor is
061: * not public.
062: *
063: * @since 2.0
064: * @author Kohsuke Kawaguchi
065: */
066: public final class ClassFactory {
067: private static final Class[] emptyClass = new Class[0];
068: private static final Object[] emptyObject = new Object[0];
069:
070: private static final Logger logger = Util.getClassLogger();
071:
072: /**
073: * Cache from a class to its default constructor.
074: *
075: * To avoid synchronization among threads, we use {@link ThreadLocal}.
076: */
077: private static final ThreadLocal<Map<Class, WeakReference<Constructor>>> tls = new ThreadLocal<Map<Class, WeakReference<Constructor>>>() {
078: public Map<Class, WeakReference<Constructor>> initialValue() {
079: return new WeakHashMap<Class, WeakReference<Constructor>>();
080: }
081: };
082:
083: /**
084: * Creates a new instance of the class but throw exceptions without catching it.
085: */
086: public static <T> T create0(final Class<T> clazz)
087: throws IllegalAccessException, InvocationTargetException,
088: InstantiationException {
089: Map<Class, WeakReference<Constructor>> m = tls.get();
090: Constructor<T> cons = null;
091: WeakReference<Constructor> consRef = m.get(clazz);
092: if (consRef != null)
093: cons = consRef.get();
094: if (cons == null) {
095: try {
096: cons = clazz.getDeclaredConstructor(emptyClass);
097: } catch (NoSuchMethodException e) {
098: logger.log(Level.INFO,
099: "No default constructor found on " + clazz, e);
100: NoSuchMethodError exp;
101: if (clazz.getDeclaringClass() != null
102: && !Modifier.isStatic(clazz.getModifiers())) {
103: exp = new NoSuchMethodError(
104: Messages.NO_DEFAULT_CONSTRUCTOR_IN_INNER_CLASS
105: .format(clazz.getName()));
106: } else {
107: exp = new NoSuchMethodError(e.getMessage());
108: }
109: exp.initCause(e);
110: throw exp;
111: }
112:
113: int classMod = clazz.getModifiers();
114:
115: if (!Modifier.isPublic(classMod)
116: || !Modifier.isPublic(cons.getModifiers())) {
117: // attempt to make it work even if the constructor is not accessible
118: try {
119: cons.setAccessible(true);
120: } catch (SecurityException e) {
121: // but if we don't have a permission to do so, work gracefully.
122: logger.log(Level.FINE,
123: "Unable to make the constructor of "
124: + clazz + " accessible", e);
125: throw e;
126: }
127: }
128:
129: m.put(clazz, new WeakReference<Constructor>(cons));
130: }
131:
132: return cons.newInstance(emptyObject);
133: }
134:
135: /**
136: * The same as {@link #create0} but with an error handling to make
137: * the instanciation error fatal.
138: */
139: public static <T> T create(Class<T> clazz) {
140: try {
141: return create0(clazz);
142: } catch (InstantiationException e) {
143: logger.log(Level.INFO,
144: "failed to create a new instance of " + clazz, e);
145: throw new InstantiationError(e.toString());
146: } catch (IllegalAccessException e) {
147: logger.log(Level.INFO,
148: "failed to create a new instance of " + clazz, e);
149: throw new IllegalAccessError(e.toString());
150: } catch (InvocationTargetException e) {
151: Throwable target = e.getTargetException();
152:
153: // most likely an error on the user's code.
154: // just let it through for the ease of debugging
155: if (target instanceof RuntimeException)
156: throw (RuntimeException) target;
157:
158: // error. just forward it for the ease of debugging
159: if (target instanceof Error)
160: throw (Error) target;
161:
162: // a checked exception.
163: // not sure how we should report this error,
164: // but for now we just forward it by wrapping it into a runtime exception
165: throw new IllegalStateException(target);
166: }
167: }
168:
169: /**
170: * Call a method in the factory class to get the object.
171: */
172: public static Object create(Method method) {
173: Throwable errorMsg;
174: try {
175: return method.invoke(null, emptyObject);
176: } catch (InvocationTargetException ive) {
177: Throwable target = ive.getTargetException();
178:
179: if (target instanceof RuntimeException)
180: throw (RuntimeException) target;
181:
182: if (target instanceof Error)
183: throw (Error) target;
184:
185: throw new IllegalStateException(target);
186: } catch (IllegalAccessException e) {
187: logger.log(Level.INFO,
188: "failed to create a new instance of "
189: + method.getReturnType().getName(), e);
190: throw new IllegalAccessError(e.toString());
191: } catch (IllegalArgumentException iae) {
192: logger.log(Level.INFO,
193: "failed to create a new instance of "
194: + method.getReturnType().getName(), iae);
195: errorMsg = iae;
196: } catch (NullPointerException npe) {
197: logger.log(Level.INFO,
198: "failed to create a new instance of "
199: + method.getReturnType().getName(), npe);
200: errorMsg = npe;
201: } catch (ExceptionInInitializerError eie) {
202: logger.log(Level.INFO,
203: "failed to create a new instance of "
204: + method.getReturnType().getName(), eie);
205: errorMsg = eie;
206: }
207:
208: NoSuchMethodError exp;
209: exp = new NoSuchMethodError(errorMsg.getMessage());
210: exp.initCause(errorMsg);
211: throw exp;
212: }
213:
214: /**
215: * Infers the instanciable implementation class that can be assigned to the given field type.
216: *
217: * @return null
218: * if inference fails.
219: */
220: public static <T> Class<? extends T> inferImplClass(
221: Class<T> fieldType, Class[] knownImplClasses) {
222: if (!fieldType.isInterface())
223: // instanciable class?
224: // TODO: check if it has a default constructor
225: return fieldType;
226:
227: for (Class<?> impl : knownImplClasses) {
228: if (fieldType.isAssignableFrom(impl))
229: return impl.asSubclass(fieldType);
230: }
231:
232: // if we can't find an implementation class,
233: // let's just hope that we will never need to create a new object,
234: // and returns null
235: return null;
236: }
237:
238: public static final Class[] COLLECTION_IMPL_CLASSES = new Class[] {
239: ArrayList.class, LinkedList.class, HashSet.class,
240: TreeSet.class, Stack.class, };
241: }
|