Tuesday, February 27, 2007

Supporting Plug-In Dependency In OpenJUMP - Plan "A"

We’ve been discussing support for plug-in dependency on the JPP Developers Mailing List the last couple of days. It’s been an active discussion, with at least 2 or three different solutions to the problem suggested, and some questions asked. I wanted to take some more time to explain how I thought plug-in dependency could be supported in OpenJUMP. I’ll call this proposed solution to the problem of supporting plug-in dependency “Plan A”.

I want to answer these questions:

Why is it important to support plug-in dependency?
How will support for plug-in dependency work in Plan A?
Will Plan A “break” existing plug-ins or require that we rewrite them?
What if a plug-in developer doesn’t want to deal with plug-in dependency? Aren’t we adding some unnecessary layers of complexity?
Would this system require changes to OpenJUMP’s core?

Why is it important to support plug-in dependency?

The short answer to this question is more flexibility for OpenJUMP’s plug-in developers, and to provide for a leaner core as we move towards more collaborative development. OpenJUMP’s current plug-in system is very simple and easy to use. However, it makes it difficult to write plug-ins that work together with each other. There is really no way to know if the plug-in you want to use has been successfully loaded and initialized in OpenJUMP. There is also no easy way to access a plug-in that has been loaded and initialized. As a consequence plug-ins must be [1] limited in their functionality or [2] contain all of the class and interface definitions they need to function.

As we move to integrate changes and improvements from other JUMP “brands” into OpenJUMP we want to minimize additions to OpenJUMP’s core. This means we will want to implement as much of the changes and improvements we will be integrating as plug-ins rather than as modifications to the core. (One possible explanation for the various “brands” of OpenJUMP would be that the limits of the current plug-in system require changes to the core.) A system to support plug-in dependency would make it possible to implement more of these changes and improvements as plug-ins.

How will support for plug-in dependency work in Plan A?

Plan A would introduce a single new interface to OpenJUMP’s core. This would be the IPlugInDependency interface. Plan A would also add a Java Collection as a member variable of the PlugInManager class, would modify the loadPlugInClasses() method of that class, and would add the sendInitializationCompleteEvent() method to that class.

Let’s consider an example of what would happen during plug-in loading and initialization in OpenJUMP under “Plan A”:

Landon decides to write a plug-in that will create a “catalog” for data sources that can be read into, and be written out of an OpenJUMP project. He also wants to also create an interface and abstract class for to represent data sources that can be managed by the catalog. He will extend the existing AbstractPlugIn class from OpenJUMP in both cases. In the first case he defines the net.surveyos.sourceforge.openjump.Catalog class that extends the AbstractPlugIn class. He also defines a net.surveyos.sourceforge.openjump.DataSource class that extends the AbstractPlugIn class. Both of these classes he defines also implement the new IPlugInDependency interface, because Landon wants the catalog plug-in to work with data source plug-ins. (The data source plug-ins will “depend” on the catalog plug-in.)

The IPlugInDependency interface declares a single method. This method is named plugInsInitializationComplete() and accepts a Java Collection as its only argument. This method is called on each plug-in that is successfully loaded and initialized by OpenJUMP, and that also declares the IPlugInDependency interface. This method is called only after an attempt has been made to load and initialize all of the plug-ins. The Java Collection passed to this method contains an object reference to each successfully initialized plug-in that implements the IPlugInDependency interface.

Le’ts walk through the path of execution under Plan A.

OpenJUMP is started by the user. The loadPlugInClasses() method is called during this start-up process. The loadPlugInClasses() method loads the class for each plug-in and creates an instance from that class. This instance is then tested to see if it implements the IPlugInDependency interface. If the interface is implemented an extra action is preformed by the loadPlugInClasses method. It adds a reference to the instance of the plug-in class to a Java Collection stored as one of its member variables. When plug-in initialization has been completed on each plug-in, the loadPlugInClasses method calls a new method of the PlugInManager class. This method is named sendInitializationCompleteEvent().

The sendInitializationCompleteEvent() method sends a reference to the Java Collection filled in the loadPlugInClasses() method to each of the plug-in instances stored within the collection by calling the plug-in instance’s plugInInitializationComplete() method. This means that each successfully initialized plug-in that implements the IPlugInDependency method is has its plugInInitializationComplete() method.

Let’s talk about what this would allow Landon to do with the to plug-ins he is designing.

Landon can now add a method to his catalog plug-in that allows data source plug-ins to register themselves with the catalog. This method is named registerDataSource() and will accept as arguments the information needed to support that particular type of data source in the catalog plug-in. He then adds within the plugInsInitializationComplete() method of each data source plug-in the logic that registers the data source plug-in with the catalog plug-in. To do this, each data source plug-in would obtain the reference to the instance of the net.surveyos.sourceforge.openjump.catalog stored in the Java Collection passed to the plugInsInitializationComplete() method. After the reference was retrieved from the collection the data source could call the catalog plug-in’s registerDataSource() method, registering itself with the catalog. (Note that this logic could not be placed in the initialization() method, because there is no guarantee that the catalog plug-in has been loaded when the initialize method of a data source plug-in is called. This is why a system to support plug-in dependency is needed.)

Let’s extend this example a little further, to demonstrate why I think it is important to allow the programmer to decide what to do when a dependency has not been met, rather than having OpenJUMP just shut down the plug-in or take some other default and hard coded action.

Landon has created a group of data source plug-ins that depend on some helper or utility classes that he has decided to encapsulate in a single plug-in. Each of the data source plug-ins in this group would then depend on the plug-in that contained these helper classes. In the plugInsInitializationComplete() method of each of these plug-ins Landon would place logic to verify that the plug-in with the helper classes was loaded. (He would do this by checking for the presence of reference to an instance of the helper classes plug-in in the Java collection passed to the plugInsInitializationComplete() method.) If the plug-in with the helper classes had not been successfully loaded and initialized for some reason, Landon could then disable the specific functionality in the data source plug-ins that depended on the helper classes plug-in. (For each data source plug-in, different functionality might be disabled, depending on how the data source plug-in used the helper classes plug-in.)

Will Plan A “break” existing plug-ins or require that we rewrite them?

Nope. The type of cooperation between new plug-ins and existing plug-ins shouldn’t be necessary, as these existing plug-ins were never designed with this type of system in mind. The existing plug-ins will be loaded and initialized as they have always been. The extra logic described in the previous section will not take place for these plug-ins, because they do not implement the IPlugInDependency interface.

What if a plug-in developer doesn’t want to deal with plug-in dependency, aren’t we adding some unnecessary layers of complexity?

If a developer wants to create a plug-in that doesn’t need support for plug-in dependency he doesn’t have to worry about it. He just implements the PlugIn interface or extends the AbstractPlugIn class and doesn’t bother implementing the IPlugInDependency interface. His plug-in will be loaded just like the existing OpenJUMP plug-ins.

Would this system require changes to OpenJUMP’s core?

Yes it would. We would need to add the IPlugInDependency interface. We would also need to modify the loadPlugInClasses() method of the PlugInManager class. We’d have to add the sendInitializationCompleteEvent() method and the Java Collection member variable that stores references to instances of plug-ins that implement the IPlugInDependency interface.

Remember, none of these changes will “break” existing plug-ins, or change how they are loaded.
Posted on 7:30 AM | Categories: