View Javadoc
1   /*
2    * Copyright (c) 2006, 2010, 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  
28  package com.sun.org.glassfish.gmbal.util;
29  
30  import java.lang.reflect.Constructor ;
31  import java.security.AccessController;
32  import java.security.PrivilegedExceptionAction;
33  import java.util.logging.Level;
34  import 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   */
39  public 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 }