bcds.tools
Class DynLoader

java.lang.Object
  extended by bcds.tools.DynLoader
Direct Known Subclasses:
PmRegistry

public class DynLoader
extends java.lang.Object

Loads classes using both qualified and unqualified names and mantains an ordered list of the classes currently loaded.

This class is a wrapper on the default (system) class loader and adds a few services that facilitate dynamically loading classes, namely:

This is not intended as a general class loader; instead, it it designed to simplify dynamically loading classes in a specific application domain.

Basic example of usage. In the following (ficticious) example, a class whose name is provided as a string must be loaded. That class must implement an interface called MyInterface<T>. If successful, a new instance must be returned.

    MyInterface<T> newObject(String klass)
    {
       DynLoader ld = new DynLoader();
       MyInterface<T> rv = null;
       
       // ld.setNamePrefixes(...);
       try {
          Constructor<MyInterface> ctor
             = ld.getConstructor(klass, null, MyInterface.class);

          rv = Utils.silentCast(ld.createInstance(ctor)); // (*)
          //rv.setup(...);

       } catch (AnyException ex) {
         // Possibly not necessary (just to personalize the message).
         if ( ex.getCause() instance of ClassCastException ) {
            throw new IllegalArgumentException(String.format(
                       "Class %s does not implement MyInterface;"
                      +" it cannot be used for...", klass));
         } else {
            throw new IllegalArgumentException(ex.getMessage());
         }
       }

       return rv;
    }
 
If only one instance per class is allowed, the following approach can be used:
   try {
      Constructor<MyInterface> ctor
         = ld.getConstructor(klass, null, MyInterface.class);
      rv = Utils.silentCast(ld.getInstance(ctor.getDeclaringClass()));
      if ( rv == null ) {
         rv = Utils.silentCast(ld.createInstance(ctor));
         ld.register(rv);
      }
      ...
  }
 
A simple cast can be used at the line marked with (*) above, but the compiler might display an "unchecked cast" warning. The function Utils.silentCast(Object) just uses annotation to suppress that warning. The cast is safe because ld.getConstructor() already checked that the class implements the required interface.

Author:
Juan Segovia S.

Constructor Summary
DynLoader()
          Instantiates a new DynLoader with the name prefix set to the empty string.
 
Method Summary
 java.lang.Object createInstance(java.lang.reflect.Constructor ctor, java.lang.Object... args)
          Creates a new instance via ctor.newInstance().
<T> java.lang.reflect.Constructor<T>
getConstructor(java.lang.String className, java.lang.Class[] ctor_args, java.lang.Class<T> required_type, java.lang.Class... other_types)
          Load a class and returns a reference to one of its constructors.
<T> T
getInstance(java.lang.Class<? extends T> cls)
          Returns a reference to the instance corresponding to class cls, or null if cls is not registered.
 java.util.List<java.lang.Object> getInstances()
          Returns the list of classes, in the order in which they have been registered.
 java.lang.String getNamePrefixes()
          Returns the current name prefixes.
 java.lang.Object load(java.lang.String className)
          Loads a class named className, registers it and returns a new instance of that class.
<T> T
load(java.lang.String className, java.lang.Class<T> required_type, java.lang.Class... other_types)
          Loads a class named className, makes sure it implements certain types, creates a new instance and registers it.
 void register(java.lang.Object instance)
          Puts the object instance at the end of the objects list and registers it as the instance for the class instance.getClass().
 void setNamePrefixes(java.lang.String prefixes)
          Sets the name prefixes.
 void unregister(java.lang.Object instance)
          Unregisters the given object and class.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

DynLoader

public DynLoader()
Instantiates a new DynLoader with the name prefix set to the empty string.

Method Detail

setNamePrefixes

public void setNamePrefixes(java.lang.String prefixes)
Sets the name prefixes.

Parameters:
prefixes - is a colon-separated list of "package names" that are tried during loadClass(). If it is null, an empty string is used instead.
An example of this parameter is: bcds.phison.sim:org.some.name The empty prefix is always prepended to prefixes (unless it already starts with the character :) so that the first name that is tried when loading a class is directly its name without any prefix.

getNamePrefixes

public java.lang.String getNamePrefixes()
Returns the current name prefixes.


load

public <T> T load(java.lang.String className,
                  java.lang.Class<T> required_type,
                  java.lang.Class... other_types)
Loads a class named className, makes sure it implements certain types, creates a new instance and registers it.

The class registration is performed by calling register(Object) with the newly instantiated object as parameter. This means that the class is registered for the concrete class of this object, not for the class given in required_type. Class loading is tried prepending each prefix (including the emtpy prefix) to className until one succeeds.

Example of use:

    DynLoader ld = new DynLoader();
    MyClass b = ld.load("MySuBlass", MyClass.class);
    MyClass x = ld.load("MyOtherSubClass", MyClass.class,
                        ThisInterface.class, ThatInterface.class);
 

Parameters:
className - the name of the class to be loaded, either a simple name (for example "MyClass"), a partially qualified name (for example "subpackage.MyClass") or a fully qualified name.

required_type - the class to be loaded must implement this interface, or be of that class or one of its descendants. That is, required_type need not be the specific class of className; it simply establishes a "minimum" type requirement.

other_types - just like required_type; there can be zero or more additional types. The difference between these additional types and required_type is that the latter helps define the return type.
Throws:
AnyException - wrapping an ClassCastException if the class found does not fulfill all the type requirements.
AnyException - wrapping other exceptions. See getConstructor(java.lang.String, java.lang.Class[], java.lang.Class, java.lang.Class...) and createInstance(java.lang.reflect.Constructor, java.lang.Object...).

load

public java.lang.Object load(java.lang.String className)
Loads a class named className, registers it and returns a new instance of that class. This method throws the same exceptions as load(String, Class, Class...), excluding the wrapped ClassCastException.

See load(String, Class, Class...) for further details.


getConstructor

public <T> java.lang.reflect.Constructor<T> getConstructor(java.lang.String className,
                                                           java.lang.Class[] ctor_args,
                                                           java.lang.Class<T> required_type,
                                                           java.lang.Class... other_types)
Load a class and returns a reference to one of its constructors. This is functionally similar to load(String, Class, Class...), without the instatiation and registration steps.


createInstance

public java.lang.Object createInstance(java.lang.reflect.Constructor ctor,
                                       java.lang.Object... args)
Creates a new instance via ctor.newInstance(). Exceptions are trapped and turned into runtime exceptions (AnyException). If the exception is thrown by/within the constructor (that is, InvocationTargetException), the cause of the error is wrapped instead.


getInstances

public java.util.List<java.lang.Object> getInstances()
Returns the list of classes, in the order in which they have been registered.


getInstance

public <T> T getInstance(java.lang.Class<? extends T> cls)
Returns a reference to the instance corresponding to class cls, or null if cls is not registered.

In principle, it should not be possible to circumvent the type checking performed by the compiler, so that no type error should ever result from a call to this method. Nevertheless, an explicit check is performed to assert that the object registered for cls is an instance of that class.

Throws:
AnyException - wrapping a ClassCastException if the object registered for class cls is not an instace of that type.

register

public void register(java.lang.Object instance)
Puts the object instance at the end of the objects list and registers it as the instance for the class instance.getClass(). If a class has already been claimed by another object, that object is replaced by this one.

Throws:
java.lang.IllegalArgumentException - if instance is null.

unregister

public void unregister(java.lang.Object instance)
Unregisters the given object and class.