/**
* Copyright 2007-2008 University Of Southern California
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.isi.pegasus.common.util;
import java.lang.reflect.*;
/**
* This class provides a dynamic class loading facility. It is
* tightly coupled to the property facility. To dynamically obtain
* an instance of a class through its constructor:
*
* <pre>
* Integer i = null;
* DynamicLoader dl = new DynamicLoader( "java.lang.Integer" );
* try {
* // instantiate as Integer("42")
* String arg[] = new String[1];
* arg[0] = "42";
* i = (Integer) dl.instantiate(arg);
* } catch ( Exception e ) {
* System.err.println( dl.convertException(e) );
* System.exit(1);
* }
* </pre>
*
* Similarily, to obtain an instance of a class through a static
* method provided by the same class, or another class:
*
* <pre>
* Integer i = null;
* DynamicLoader dl = new DynamicLoader( "java.lang.Integer" );
* try {
* // instantiate as Integer("42")
* String arg[] = new String[1];
* arg[0] = "42";
* i = (Integer) dl.static_method( "valueOf", arg );
* } catch ( Exception e ) {
* System.err.println( dl.convertException(e) );
* System.exit(1);
* }
* </pre>
*
* @author Karan Vahi
* @author Jens-S. Vöckler
* @version $Revision$
*/
public class DynamicLoader
{
/**
* Stores the fully qualified class name to dynamically instantiate.
*/
private String m_classname;
/**
*
*/
public DynamicLoader( String classname )
{
if ( (this.m_classname = classname) == null )
throw new NullPointerException( "You must specify a fully-qualified class name" );
}
/**
* Sets the fully-qualified class name to load.
* @param classname is the new class name.
* @see #getClassName()
*/
public void setClassName( String classname )
{
if ( (this.m_classname = classname) == null )
throw new NullPointerException( "You must specify a fully-qualified class name" );
}
/**
* Obtains the fully-qualified class name that this instance works with.
* @return the class name.
* @see #setClassName( String )
*/
public String getClassName()
{ return this.m_classname; }
/**
* Dynamically instantiates a class from a contructor. You must have
* set the class name before invoking this method. Please note that
* any exceptions thrown by the constructor will be wrapped into a
* <code>InvocationTargetException</code>.
*
* @param arguments are arguments to the constructor of the class
* to load. Please use "new Object[0]" for the argumentless default
* constructor.
* @return an instance that must be cast to the correct class.
*
* @exception ClassNotFoundException if the driver for the database
* cannot be loaded. You might want to check your CLASSPATH, too.
* @exception NoSuchMethodException if the driver's constructor interface
* does not comply with the database driver API.
* @exception InstantiationException if the driver class is an abstract
* class instead of a concrete implementation.
* @exception IllegalAccessException if the constructor for the driver
* class it not publicly accessible to this package.
* @exception InvocationTargetException if the constructor of the driver
* throws an exception while being dynamically loaded.
* @exception SQLException if the driver for the database can be
* loaded, but faults when initially accessing the database
*
* @see #setClassName( String )
*/
public Object instantiate( Object[] arguments )
throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
// generate class array and populate with class of each argument
Class[] temp = new Class[ arguments.length ];
for ( int i=0; i<arguments.length; ++i )
temp[i] = arguments[i].getClass();
// load class into memory and obtain an instance of it
return Class.forName(m_classname).getConstructor(temp)
.newInstance(arguments);
}
/**
* Dynamically instantiates a class from a contructor. You must have
* set the class name before invoking this method. Please note that
* any exceptions thrown by the constructor will be wrapped into a
* <code>InvocationTargetException</code>.<p>
* This method should be invoked, if the constructor declares
* interface types as formal arguments, but the actual arguments
* are implementation classes.
*
* @param classes is a vector of the classes involved. Each item
* in the classes vector matches the item in the arguments vector.
* The classes vector will be used to select the correct constructor.
* Please use "new Class[0]" for the argumentless default ctor.
* @param arguments are arguments to the constructor of the class
* to load. Please use "new Object[0]" for the argumentless default
* constructor.
* @return an instance that must be cast to the correct class.
*
* @exception ClassNotFoundException if the driver for the database
* cannot be loaded. You might want to check your CLASSPATH, too.
* @exception NoSuchMethodException if the driver's constructor interface
* does not comply with the database driver API.
* @exception IllegalArgumentException is thrown, if the number of
* arguments do not match the number of types, ie the vector have
* mismatching sizes.
* @exception InstantiationException if the driver class is an abstract
* class instead of a concrete implementation.
* @exception IllegalAccessException if the constructor for the driver
* class it not publicly accessible to this package.
* @exception InvocationTargetException if the constructor of the driver
* throws an exception while being dynamically loaded.
* @exception SQLException if the driver for the database can be
* loaded, but faults when initially accessing the database
*
* @see #setClassName( String )
*/
public Object instantiate( Class[] classes, Object[] arguments )
throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
// complain on argument mismatch
if ( classes.length != arguments.length )
throw new IllegalArgumentException( "vector sizes must match" );
// load class into memory and obtain an instance of it
return Class.forName(m_classname).getConstructor(classes)
.newInstance(arguments);
}
/**
* Dynamically instantiates a class from a static method which
* constructs the resulting object. You must have set the class name
* before invoking this method. Please note that any exceptions thrown
* by the constructor will be wrapped into a
* <code>InvocationTargetException</code>.
*
* @param method is the name of the static method to call.
* @param arguments are arguments to the constructor of the class
* to load. Please use "new Object[0]" for the argumentless default
* constructor.
* @return an instance that must be cast to the correct class.
*
* @exception ClassNotFoundException if the driver for the database
* cannot be loaded. You might want to check your CLASSPATH, too.
* @exception NoSuchMethodException if the driver's constructor interface
* does not comply with the database driver API.
* @exception InstantiationException if the driver class is an abstract
* class instead of a concrete implementation.
* @exception IllegalAccessException if the constructor for the driver
* class it not publicly accessible to this package.
* @exception InvocationTargetException if the constructor of the driver
* throws an exception while being dynamically loaded.
* @exception SQLException if the driver for the database can be
* loaded, but faults when initially accessing the database
* @exception SecurityException if you are not permitted to invoke the
* method, or even list the methods provided by the class.
* @exception NullPointerException if the method name is
* <code>null</code>.
* @exception IllegalArgumentException if the number of actual and
* formal parameter differ, unwrapping a primitive type failed, or
* a parameter value cannot be converted to the formal argument type.
*
* @see #setClassName( String )
*/
public Object static_method( String method, Object[] arguments )
throws ClassNotFoundException, SecurityException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException,
NullPointerException, IllegalArgumentException
{
// generate class array and populate with class of each argument
Class[] temp = new Class[ arguments.length ];
for ( int i=0; i<arguments.length; ++i )
temp[i] = arguments[i].getClass();
// load the class into memory, and find the method, and invoke it as
// a static method
return Class.forName(m_classname).getDeclaredMethod( method, temp )
.invoke( null, arguments );
}
/**
* Converts an exception from the class loader into an error message.
*
* @param classname is the name or some other class signifier.
* @param e is the exception thrown by the class loader.
* @return a string that tries to describe what went wrong.
*/
static public String convertException( String classname, Exception e )
{
String result = null;
// check exceptions
result = convertExceptionToString( classname, e );
//Commented out, as defined in convertToString(String,Exception) function
//Karan April 25, 2006
// if ( e instanceof ClassNotFoundException ) {
// result = "Unable to dynamically load " + classname;
// // do cause
// } else if ( e instanceof NoSuchMethodException ) {
// result = "Unable to dynamically invoke the constructor of " + classname;
// // no cause
// } else if ( e instanceof InstantiationException ) {
// result = "The dynamically loadable class " + classname + " is either " +
// "abstract or an interface";
// // no cause
// } else if ( e instanceof IllegalAccessException ) {
// result = "Unable to access appropriate constructor in " + classname;
// // no cause
// } else if ( e instanceof InvocationTargetException ) {
// result = "Class " + classname + " threw exception " +
// e.getClass().getName() + " during construction";
// // do cause
// } else if ( e instanceof IllegalArgumentException ) {
// result = "Class " + classname + " threw exception " +
// e.getClass().getName() + " during method invocation argument " +
// "list construction";
// } else if ( e instanceof NullPointerException ) {
// result = "Invalid static initializer method name for " + classname;
// // no cause
// } else if ( e instanceof SecurityException ) {
// result = "Prohibited access to " + classname;
// // ?? cause
// } else {
// result = classname + " caugth " + e.getClass().getName();
// // ?? cause
// }
// append complete cause chain
int i = 0;
for ( Throwable cause=e.getCause(); cause != null; cause=cause.getCause() ) {
result += " [" + Integer.toString(++i) + "]: " + cause;
}
// done
return result;
}
/**
* Converts an exception from the class loader into an error message.
*
* @param e is the exception thrown by the class loader.
* @return a string that tries to describe what went wrong.
*/
public String convertException( Exception e )
{ return DynamicLoader.convertException( m_classname, e ); }
/**
* Converts an exception from the class loader into an error message.
* Note: It does not convert any cause messages.
*
* @param classname is the name or some other class signifier.
* @param e is the exception thrown by the class loader.
* @return a string that tries to describe what went wrong.
*/
static public String convertExceptionToString( String classname, Throwable e )
{
String result = null;
// check exceptions
if ( e instanceof ClassNotFoundException ) {
result = "Unable to dynamically load " + classname;
// do cause
} else if ( e instanceof NoSuchMethodException ) {
result = "Unable to dynamically invoke the constructor " + e.getMessage() ;
// no cause
} else if ( e instanceof InstantiationException ) {
result = "The dynamically loadable class " + classname + " is either " +
"abstract or an interface";
// no cause
} else if ( e instanceof IllegalAccessException ) {
result = "Unable to access appropriate constructor in " + classname;
// no cause
} else if ( e instanceof InvocationTargetException ) {
result = "Class " + classname + " threw exception " +
e.getClass().getName() + " during construction";
// do cause
} else if ( e instanceof IllegalArgumentException ) {
result = "Class " + classname + " threw exception " +
e.getClass().getName() + " during method invocation argument " +
"list construction";
} else if ( e instanceof NullPointerException ) {
result = "Invalid static initializer method name for " + classname;
// no cause
} else if ( e instanceof SecurityException ) {
result = "Prohibited access to " + classname;
// ?? cause
} else {
result = classname + " caught " + e.getClass().getName() + " " + e.getMessage();
// ?? cause
}
// done
return result;
}
}