Server-side Java: Patterns for flexible initialization, Part 1

A reflective approach to initializing your server-side system

1 2 3 Page 2
Page 2 of 3

The ObjectFactory component

The engineering delicacy of that example lies solely in the

ObjectFactory

method implementation, with which the

Startup

class may instantiate a root object from any class. This is an important feature of the startup system. If your system structure should be altered in the future, you need only implement a new root class and modify the configuration file to completely revamp the system. This is an example of the usefulness of the Abstract Factory pattern (see

Design Patterns,

by Erich Gamma et al. in

Resources

). The only relevant method of the

ObjectFactory

,

createObject

performs five tasks (listed below) marked

### 1)

-

### 5)

, as shown in the

ObjectFactory.java

code listing.

  1. Load the class supplied as a string argument using the Class.forName() method.
  2. Use reflection to find all the constructors of the class loaded in (1).
  3. Use reflection to check the type and number of arguments for each constructor.
  4. If the type and number of arguments provided as a parameter to the createObject match those of the current constructor from (3), mark the constructor as an invocation candidate. If not, move to the next declared constructor and try again.
  5. Instantiate an object of the class from (1) using the invocation candidate from (4).

Listing 3. ObjectFactory.java

// Copyright (c) 2000 jguru.com

// All rights reserved.

package com.jguru.initHandler;

import java.lang.reflect.Constructor;

/**

 * Implementation of the ObjectFactory interface that

 * creates instances of a given class using provided

 * argument parameters.

 *

 * @author Lennart Jörelid, jGuru Europe

 * @version $Id$

 * @since January 2000

 */

public class SimpleObjectFactory implements ObjectFactory

{

    /**

     * Instantiates an object from the class given and with the

     * provided arguments. Primitive arguments must be sent in

     * their respective java.lang containers. (i.e., java.lang.Byte

     * for a byte argument, java.lang.Integer for an int argument, etc.)

     * <br>

     * All exceptions thrown within the createObject method are

     * type-shifted into a StartupException.

     *

     * @param className The fully qualified name of the class from which

     * an object should be created.

     * @param args The argument array to be fed to the constructor.

     * @return The fully instantiated object.

     * @exception com.jguru.initHandler.StartupException thrown if

     * anything goes wrong in creating the object.

     */

    public Object createObject(String className, Object args[])

    throws StartupException

    {

       // The provided class and the constructor that should

       // ultimately be invoked to create an object of the class.

       Class theClass = null;

       Constructor invocationCandidate = null;

       Object toReturn = null;

       // ### 1) Load the class and check sanity

       try

       {

           theClass = Class.forName(className); catch(ClassNotFoundException ex)

       {

           throw new StartupException("[Startup createObject]: Could

not load class " + className);

       }

       // ### 2) Get the constructors of the declared class.

       Constructor[] theConstructors = theClass.getConstructors();

       // ### 3) Check number and type of arguments required for each

       // found constructor in the loaded class.

outer: for(int i = 0; i < theConstructors.length; i++)

       {

           // Get the class of the arguments

           Class[] constructorArgs = theConstructors[i].getParameterTypes

();

           // If we have different argument lengths,

           // the found constructor could not be called using the

           // current parameter array. Skip to the next constructor.

           if(args.length != constructorArgs.length)

           continue outer;

           // Is this a no-argument constructor?

           if(args.length == 0)

           {

               invocationCandidate = theConstructors[i];break outer;

           }

           // ### 4) This constructor requires parameters.

           // Loop through all provided parameters to verify

           // that they match the required ones.

           for(int j = 0; j < args.length; j++)

           {

               // Check if the constructor argument might be

               // typecast into the proper class. This must be done for all

               // primitive types, which must be encapsulated in their

java.lang.XXXX

               // containers to be passed as part of an object[] array.

               boolean matched = false;

               if(constructorArgs[j].isPrimitive())

               {

                  String primName = constructorArgs[j].getName();

                  String argName = args[j].getClass().getName();

                  if(primName.equals("byte") &&

argName.equals("java.lang.Byte")) matched = true;

                  if(primName.equals("short") &&

argName.equals("java.lang.Short")) matched = true;

                  if(primName.equals("int") &&

argName.equals("java.lang.Integer")) matched = true;

                  if(primName.equals("long") &&

argName.equals("java.lang.Long")) matched = true;

                  if(primName.equals("long") &&

argName.equals("java.lang.Integer")) matched = true;

                  if(primName.equals("float") &&

argName.equals("java.lang.Float")) matched = true;

                  if(primName.equals("double") &&

argName.equals("java.lang.Double")) matched = true;

                  if(primName.equals("char") &&

argName.equals("java.lang.Character")) matched = true;

                  if(primName.equals("boolean") &&

argName.equals("java.lang.Boolean")) matched = true;

                  if(!matched) continue outer;

               }

               if( !matched && (!args[j].getClass

().equals(constructorArgs[j]))) }

           // We found the constructor to invoke.

           // Check sanity, and assign the value to

           // the invocationCandidate variable.

           if(invocationCandidate != null)

           {

               // We should never wind up here....

               throw new StartupException("[Startup createObject]:

Guru meditation; Found 2 " +

                     + "constructors with same signature in class

" + theClass.getName());

           }

           // We found an invocation candidate, whose number and type of

arguments

           // match the argument array provided to this method.

           // Assign the constructor to the invocationCandidate variable.

           invocationCandidate = theConstructors[i];

       // Check sanity

       if(invocationCandidate == null)

       throw new StartupException("[Startup createObject]: Found no

constructor matching argument " +

             + " criteria in " + theClass.getName());

       // ### 5) Sane. Invoke the constructor and return the object.

       try

       {

          if(args.length == 0)

          {

             toReturn = theClass.newInstance();

          }

          else

          {

           toReturn = invocationCandidate.newInstance(args); }

       catch(Exception ex)

       {

          // Despite all the testing, something was quite wrong here.

          // Throw the proper exception from this method.

          throw new StartupException("[Startup createObject]:

Invocation failed for object of type "

                           + className + "(" + theClass.getName()

+ "), Exception: "

                           + ex.getClass().getName() + " " +

ex.getMessage());

       }

       // All done. Return.

       return toReturn;

   }

    public Object createObject(String className)

    {

       return createObject(className, new Object[]{});

    }

}

1 2 3 Page 2
Page 2 of 3