Archive for category Reflection

Reflection Tip – Dynamically calling methods

There are often times when you need to call a method but you do not know the name of the method until runtime.  I came across this issue during my Thesis.    Our problem was as follows:

How do we dynamically map between an object type and a method in the sub class of PublishableServer that processes that type?

Here is some background to the problem:
PublishableServer is an abstract class that can connects to our distributed system and receives requests for lists of PublishedData objects. A subclasses of PublishableServer must implement methods to process various PublishedData objects it receives.

The Central Server Connecting to a JDBC sub server

If we look at the JDBCServer illustrated above.  This class must implement methods to search for PublishedData objects in the database that it is connected to.  Each method must map between attributes of the PublishedData object and the columns in the database.   As the getPublishedList(PublishedData publishedData) method of PublishableServer is called we must map this publishedData object to the correct method in the sub class.  

One option could be to declare the methods as abstract in the PublishableServer class and then use the instanceof operator to select the correct method.

    public ArrayList getPublishedList(PublishedData publishedData)
    {
        if (publishedData instanceof Student)
        {
            return getPublishedList((Student) publishedData);
        } else if (publishedData instanceof Course)
        {
            return getPublishedList((Course) publishedData);
        }
        return new ArrayList(0);
    }

    public abstract ArrayList getPublishedList(Course publishedCourse);
    public abstract ArrayList getPublishedList(Student publishedStudent);

The issue with using this approach is that we need to update the PublishableServer class every time a new type of PublishedData is added to the framework. All PublishableServer implementations would also have to implement all the search methods, even if they did not want to.

The solution to this issue is to use Reflection to dynamically call the methods in the sub class.  To allow this, the methods in the subclass must have a known name.  We prepend "getPublished" to the name of the PublishedData class that was passed to the  getPublishedList() method.  So all we need to do is get the name of the class that has been passed, append that to "getPublished" and we have the name of the method that implements that search.  The publishedData object implements a mehtod called getClassName() that will return the name of a class without the package name.  To create a method to search for Student objects we simple implement the following method in the JDBCServer class that extends PublishableServer.

public ArrayList getPublishedStudent(PublishedData publishedData)

The body of this method should implement a search for Student objects in the servers persistent store using the properties of the Student object passed to narrow the search results.

So how do we call this method once we have its name?

The getClass() method that all java objects inherit will return the runtime class of the current object.  In our case when we call this method inside the PublishableServer class we will get the runtime class that extended our PublishableServer class.
The Class object contains a method called getMethod() which takes the name of the method and an array of Class objects which represents the list of parameters for the method.  If the method does not exist a NoSuchMethodException is thrown.  If this is thrown in our server we simple return an empty ArrayList as this server does implement that search.

If the method does exist then we call invoke() passing the current object and PublishedData parameter.  This will dynamically call our method and return an Object class.  We must cast this to an ArrayList and return it.  The implementation of this can be seen below.

    public ArrayList getPublishedList(PublishedData publishedData)
    {
        ArrayList result = new ArrayList(0);
        String className = publishedData.getClassName();
        String methodName = "getPublished" + className;
        Class[] argTypes = new Class[]
        { PublishedData.class };
        Object[] args = new Object[]
        { publishedData };
        try
        {
            Method m = this.getClass().getMethod(methodName, argTypes);
            result = (ArrayList) m.invoke(this, args);
        } catch (NoSuchMethodException noSuchMethod)
        {
            logger.error("ERROR: " + serverName
                    + " does not support searches of type: "
                    + publishedData.getClass().getName());
            logger.error("ERROR:"
                    + this.getClass().getName()
                    + " needs to implement the follwing method to preform search:");
            logger.error("public ArrayList " + methodName
                    + "(PublishedData publishedData){");
            logger.error("  ArrayList result = new ArrayList();");
            logger.error("  //Preform Search");
            logger.error("  return result;");
            logger.error("}");
        } catch (IllegalAccessException illegalAccess)
        {
            logger.error(serverName + " Does not support searches of type"
                    + publishedData.getClass().getName(), illegalAccess);
        } catch (InvocationTargetException invocTarget)
        {
            logger.error(serverName + " Does not support searches of type"
                    + publishedData.getClass().getName(), invocTarget);
        } catch (Exception general)
        {
            logger.error(serverName + " Does not support searches of type"
                    + publishedData.getClass().getName(), general);
        }

        return result;

    }

No Comments