Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a Java library to conditionally handle an input class w/o causing unconditional runtime dependency on that class

I have what seems like a tricky Java library task.

I need to write an adapter/helper class for working with JTables that has some additional functionality if the JTable is a JXTable. But I don't want to add a runtime dependency on swingx-core-1.6.2.jar unless my application actually uses JXTable (in which case it already requires having the SwingX jar file on the classpath)

How can I decouple my code to accomplish this? I don't even know how I can test for JXTable; if I try to use instanceof JXTable as a test, that means my code already has an unconditional runtime dependency on JXTable.

I have written Java libraries before that have "optional" runtime linkage dependencies: if I have this in my library:

package com.foobar.foolib;

// import from whizbang.jar
import com.whizbang.BloatwareThingy;

public class SuperObject
{
    /* ... uses a BloatwareThingy ... */
}

and SuperObject is the only class that uses whizbang.jar, then as long as my end application doesn't use SuperObject, then there's no runtime dependency on whizbang.jar; if my end application does want to use SuperObject, then it needs to include whizbang.jar on the classpath. Optional from the standpoint of the application. Works great.

How can I write a method to test for a given JTable being an instance of JXTable, without requiring a dependency on the SwingX jar file if the application only uses JTable?

like image 838
Jason S Avatar asked Dec 02 '11 15:12

Jason S


1 Answers

You can test with:

Class cls = null;
try {
    cls = Class.forName( "org.jdesktop.swingx.JXTable" );
} catch( Throwable ex ) {}

if( cls != null )
    // have JXTable

After that you would have to use Reflection exclusively for all accesses to classes, methods, constructors and fields from that external library.

As this can become very clumsy if you need to access a large API this way you could write helper classes that can use JXTable directly but get created via Reflection and invoked via interfaces or abstract classes:

public interface MyTableHandler
{
    void doSomethingWithTable( JTable table );
}

public class JXTableHandler implements MyTableHandler
{
    void doSomethingWithTable( JTable table )
    {
        JXTable jxt = (JXTable) table;
        // use JXTable API directly ...
    }
}

public class StdTableHandler implements MyTableHandler
{
    void doSomethingWithTable( JTable table )
    {
        // do without JXTable
    }
}

public class MyIndependentClass
{
    static final MyTableHandler handler;

    static {
        try {
            Class.forName( "org.jdesktop.swingx.JXTable" ); // force exception
            handler = (MyTableHandler) Class.forName( "my.pkg.JXTableHelper" )
                                            .newInstance();
        } catch( Throwable ex ) {
            handler = new StdTableHandler();
        }
    }
    public void treatTable( JTable table )
    {
        handler.doSomethingWithTable( table );
    }
}

The Java VM has no problems with the usage of nonexisting API in classes that are not used themselves but are only present in jar files. With this approach you would use JXTableHandler only if org.jdesktop.swingx.JXTable is available and StdTableHandler otherwise.

like image 73
x4u Avatar answered Sep 30 '22 00:09

x4u