So. I'm having an issue.

I'm building an application that loads class files as extensions during runtime. I'm doing this, becuase I want my application to be able to grow in the future without having to recode and recompile the main source.. So, I plan to make seperate class files that can be loaded at runtime, depending on their existance.

I'm having trouble with loading the classes and calling their methods..

Every extension class will have the following function in common, for example:

public void SetRefs ([ClassName] theobj) {
	// Set Object References
	myobj = theobj;
}

And my application loads the extensions like so:

private interface ExtensionInf {
	public void SetRefs ([ClassName] theobj);
}

.....

try {

	String className = classFileName.substring ( 0,
		classFileName.lastIndexOf (".")).trim () );

	Class loaded = Class.forName (className);
	// Try to load class object into Java

	ExtensionInf loadedclassobj = (ExtensionInf)
		loaded.getConstructor().newInstance();

	loadedclassobj.SetRefs(MyObject);

} catch (Exception ex) {
	// Invalid Extension
	AppDebug (ex);
}

I know this doesn't work. I get a ClassCast exception.
So, I can't use the interface like that.
And I tried using *.getClass().getMethod(...).invoke(...);
...but I got an some other error, like, the object wasn't an instance or whatever.

So my question is, as I've searched google for about a total of 2 hours now, tried several different tactics and have grown tired... How, do I load class files using their name, instance them, and access their methods..?

:-|

Thankie!

Here's an example I wrote that works, but it's primitive.

TestExtension.java

public class TestExtension {
	
	public static String retClassName () {
		return "Class Name: TestClass";
	}
	
}

Tester.java

public class Tester{

public static void main (String[] args) {
	try {
		Class loaded = Class.forName ("TestExtension");

		System.out.println (
		loaded.getMethod("retClassName").invoke(loaded)
		);

	} catch (Exception ex) {
		System.err.println (ex);
	}

} // END MAIN

}

Running Tester.class (after compiling both) outputs:

Class Name: TestClass
Press any key to continue...

You can actually do pretty much just that, you've almost succeeded in creating your own plugin architecture.

Congratulations on that, it takes many people years to figure out (if they succeed at all).

What you're looking at is an abstract factory to create adapters.

What you need is to create an interface (abstract baseclass would work as well but is far less flexible) which defines the methods that you need for your plugins to function.
Then you can just cast the result from the class instantiation to that interface and you can call any of those methods.

:eek: OH - MY - GOSH.

:cheesy: I figured it out.

I think it's been about a total of.. hmm.. 10 hours, now, trying to get this?
I've been playing with it all morning, and it finally compiled and ran as expected.
All I needed to do was IMPLEMENT the interface -- something I overlooked. Gah.
Thanks, jwenting. Your comment gave me hope and prevented me from deleting everything and trying something completely different. It's your fault that I figured it out! Well, maybe "fault" isn't the word. Anyhow, here's my working code...

Here is the interface I implemented:

public interface CastPlugin {
	public void SetPapa(Object PeutEtrePapa); // Set reference to parenting class
	public String pluginName(); // Return plugin's short name
	public String pluginDescription(); // Return plugin's long description
	public String pluginReturnState(); // Return plugin's current condition
	public JPanel pluginReturnGUI(); // Return GUI for tab use
	public void pluginDie(); // Tell plugin to save data and shut down
}

Here's the contents of a test plugin file: TestPlugin_NWP.java

package plugins;

/* NOTE: ALL PLUGINS MUST HAVE A CLASS NAME ENDING WITH "_NWP"
 * AND MUST ALSO BE A PART OF THE "plugins" PACKAGE. */

// When I design future plugins and compile, I should just be able
// to distribute the .class files, and the user can place them in
// the "plugins" package directory.. No?? I'll test this theory..

import javax.swing.JPanel;

/**
 * This is a test plugin to be used with the
 * NetWorker application by Matthew Cudmore.
 * 
 * @author Matthew Cudmore
 *
 */
public class TestPlugin_NWP implements brainFiles.GUIBoss.CastPlugin {
	
	// NOTICE: I implement the Interface that is defined in the file
	// GUIBoss in the package brainFiles. I have to implement the
	// interface so that I can cast this class in the future.
	
	////////// CONSTANTS
	static final String plugin_author = "Matthew Cudmore";
	static final String plugin_version = "1.0 06/19/06";
	static final String plugin_title = "Test Plugin";
	static final String plugin_description = "Description Here";

	////////// PRIVATE VARIABLES
	private static String plugin_state = "Ok.";
	
	public static void main () {
	// Dunno why, but can't get this to run under SuSE 10.1
	// No matter how I enter the args to the 'java' command,
	// "Exception in thread "main" java.lang.NoClassDefFoundError"
		System.out.println (plugin_title + " [Version: "
			 + plugin_version + "]" );
		System.out.println ("By: " + plugin_author);
		System.out.println ();
		System.out.println (plugin_description);
		System.out.println ();
		System.out.println ("This Plugin cannot run "
			+ "independently. Exiting..." );
	}
	
	// Plugin Constructor(s)
	//==============================================================
	public TestPlugin_NWP () {
		System.out.println ("Plugin loaded: " + plugin_title);
	}
	
	// Implemented Method Definitions
	//==============================================================
	public void SetPapa (Object PeutEtrePapa) {
		// Use this method to set references...
	}
	
	public String pluginName() {
		// Return plugin's name for tab
		return plugin_title;
	}
	
	public String pluginDescription() {
		// Return plugin's long description
		return plugin_description;
	}
	
	public String pluginReturnState() {
		// Return plugin's functioning condition
		return plugin_state;
	}
	
	public JPanel pluginReturnGUI() {
		// Return GUI for tab use
		JPanel plugin_panel = new JPanel();
		return plugin_panel;
	}
	
	public void pluginDie() {
		// Tell plugin to close sockets, save data, etc
	}

	// Private Methods
	//==============================================================

	// None: This is a test
	
}

And here are the methods I used to load the plugin:

private static boolean PluginsAlreadyLoaded = false;

public void LoadPlugins () {
	if (PluginsAlreadyLoaded) return; 
	File pluginDir = new File ("plugins");
	if (pluginDir.exists() && pluginDir.isDirectory()) {
		String[] filelist = pluginDir.list();
		for (int i = 0; i < filelist.length; i++) {
			if (filelist[i].endsWith("_NWP.class")) {
			// Only read the file if it is a plugin class
			// (the whatever$subclass.class files are ignored)
				LoadPlugin (
				  filelist[i].substring ( 0,
				     filelist[i].lastIndexOf (".class")
				  )
				);
			}
		}
		PluginsAlreadyLoaded = true;
	}
}
	
private void LoadPlugin (String pluginName) {
	try {
		
		Class lodc = Class.forName("plugins." + pluginName);

		/* Note: Can't yet cast the Class to an Interface,
		 * because Interfaces don't have Constructors */
		
		Class[] theParams = lodc.getConstructors()[0]
			.getParameterTypes();
		
		// Assuming there is a constructor...
		Object lp = lodc.getConstructor(theParams)
			.newInstance(nullParams(theParams));

		CastPlugin lplugin = (CastPlugin)lp;
		
		// Load the plugin's GUI into the JTabbedPane 'jbp_jtp'...
		jbp_jtp.add(
			lplugin.pluginName(),
			lplugin.pluginReturnGUI()
		);
	
	} catch (Exception ex) {
		// IGNORE: Bad Plugin??
		NetWorker.LogError(ex); // Log the error elsewhere
	}
}

private Object[] nullParams (Class[] ptypes) {
	// return an Object array of null values of ptypes types 
	/* This is for compatability -- incase a plugin constructor
	 *	desires random arguments */
	Object[] obs = new Object[ptypes.length];
	for (int i = 0; i < ptypes.length; i++) obs[i] = null;
	return obs;
}

It won't let me edit my previous post, so..

Incase anyone noticed in my code, in the main function I said...

public static void main () {
// Dunno why, but can't get this to run under SuSE 10.1
// No matter how I enter the args to the 'java' command,
// "Exception in thread "main" java.lang.NoClassDefFoundError"

Well I didn't have String[] args :o
And the problen I had originally is that I wasn't entering the command properly.
Correct command: java -classpath . plugins.TestPlugin_NWP

................................................. (Delete this -- Accident Post) .................................................

What you're looking at is an abstract factory to create adapters.

What you need is to create an interface (abstract baseclass would work as well but is far less flexible) which defines the methods that you need for your plugins to function.

I used a 2-interface-version, which made everything much more flexible and comfortable.

The first interface describes the plugin manager, called PluginManagerInterface. This interface publishes functions like adding menu items, getting program version, adding items to the status bar and so on.

The second interface is the plugin interface. Here I added functions like "init(), start(), stop(), deinit(), getPluginName(), getPluginVersion()"... These functions are called by the plugin manager class (which also implements the plugin manager interface).

The trick is, that the init() function of the plugin looks like this:

public void init(PluginManagerInterface manager)

Now, I can store the manager in a static variable and I am able to access the published manager functions whenever I need to.

The main benefit of this architecture is, that I can publish both interface classes and every customer is able to develop own plugins without knowing anything about my own code, as I don't need to pass a class (which may change) to the plugin's init function.

This article has been dead for over six months. Start a new discussion instead.