1/*
2 * Copyright (c) 2006, 2012, 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
26
27
28package com.sun.org.glassfish.gmbal.util;
29
30import java.lang.reflect.Constructor ;
31import java.security.AccessController;
32import java.security.PrivilegedExceptionAction;
33import java.util.logging.Level;
34import java.util.logging.Logger;
35
36/** Class that allows any class to be instantiated via any accessible constructor.
37 * Really a short hand to avoid writing a bunch of reflective code.
38 */
39public class GenericConstructor<T> {
40    private final Object lock = new Object() ;
41
42    private String typeName ;
43    private Class<T> resultType ;
44    private Class<?> type ;
45    private Class<?>[] signature ;
46
47    // Use the raw type of the constructor here, because
48    // MethodInfo can only return a raw type for a constructor.
49    // It is not possible to have MethodInfo return a
50    // Constructor<T> because T may not be known at compile time.
51    private Constructor constructor ;
52
53    /** Create a generic of type T for the untyped class cls.
54     * Generally cls is a class that has been generated and loaded, so
55     * no compiled code can depend on the class directly.  However, the
56     * generated class probably implements some interface T, represented
57     * here by Class<T>.
58     * @param type The expected type of a create call.
59     * @param className The name of the class to use for a constructor.
60     * @param signature The signature of the desired constructor.
61     * @throws IllegalArgumentException if cls is not a subclass of type.
62     */
63    public GenericConstructor( final Class<T> type, final String className,
64        final Class<?>... signature ) {
65        this.resultType = type ;
66        this.typeName = className ;
67        this.signature = signature.clone() ;
68    }
69
70    @SuppressWarnings("unchecked")
71    private void getConstructor() {
72        synchronized( lock ) {
73            if ((type == null) || (constructor == null)) {
74                try {
75                    type = (Class<T>)Class.forName( typeName ) ;
76                    constructor = AccessController.doPrivileged(
77                        new PrivilegedExceptionAction<Constructor>() {
78                            public Constructor run() throws Exception {
79                                synchronized( lock ) {
80                                    return type.getDeclaredConstructor( signature ) ;
81                                }
82                            }
83                        } ) ;
84                } catch (Exception exc) {
85                    // Catch all for several checked exceptions: ignore findbugs
86                    Logger.getLogger( "com.sun.org.glassfish.gmbal.util" ).log( Level.FINE,
87                        "Failure in getConstructor", exc ) ;
88                }
89            }
90        }
91    }
92
93    /** Create an instance of type T using the constructor that
94     * matches the given arguments if possible.  The constructor
95     * is cached, so an instance of GenericClass should always be
96     * used for the same types of arguments.  If a call fails,
97     * a check is made to see if a different constructor could
98     * be used.
99     * @param args The constructor arguments.
100     * @return A new instance of the object.
101     */
102    public synchronized T create( Object... args ) {
103        synchronized(lock) {
104            T result = null ;
105
106            for (int ctr=0; ctr<=1; ctr++) {
107                getConstructor() ;
108                if (constructor == null) {
109                    break ;
110                }
111
112                try {
113                    result = resultType.cast( constructor.newInstance( args ) ) ;
114                    break ;
115                } catch (Exception exc) {
116                    // There are 4 checked exceptions here with identical handling.
117                    // Ignore FindBugs complaints.
118                    constructor = null ;
119                    Logger.getLogger("com.sun.org.glassfish.gmbal.util").
120                        log(Level.WARNING, "Error invoking constructor", exc );
121                }
122            }
123
124            return result ;
125        }
126    }
127}
128