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 }