Multiple plugins in one jar

Hi all, this is Chris from Open Imaging (a.k.a. the new caretakers of the Micro-Manager project). We’re making use of the SciJavaPlugin and @Plugin annotations to handle our plugin loading, which is great. We are, I admit, manually scanning jars and loading plugins based on the JSON we find there rather than using the plugin service (mostly because I don’t want to add the plugin jars to the classpath). This is still much cleaner and orders of magnitude faster than our old plugin loading system.

I do have a concern, though. We’d like our plugins to be able to have multiple annotated classes in the same jar. For example, if a given plugin jar exposes both a data-processing plugin to add metadata to the image, and an overlay plugin to draw on the image based on the metadata, those are two separate classes that both need an @Plugin annotation. Currently, something like that results in entries in the META-INF that look like this:

{“class”:“org.micromanager.imageflipper.FlipperPlugin”,“values”:{“type”:“org.micromanager.data.ProcessorPlugin”}}{“class”:“org.micromanager.imageflipper.FooPlugin”,“values”:{“type”:“org.micromanager.display.OverlayPlugin”}}

This is not valid JSON – there’s two separate dictionaries, and they aren’t contained in a list, just sort of crammed next to each other. Consequently, we can’t correctly parse this kind of META-INF file. and find all of the classes that we should know about.

Would it be possible to get the annotation logic to generate a valid JSON metadata in the event that the jar contains multiple annotated classes? Ideally it would contain a list of dictionaries.

Thanks for your time!

2 Likes

Use a URLClassLoader instead, and pass it to the PluginFinder.

And will that be able to load multiple plugins from the same jar?

Yeah, you’ll be reusing the scijava-common plugin metadata parsing logic that way. It supports the bastardized almost-JSON format.

The reason for that format, by the way, is that it makes concatenation of META-INF/json/org.scijava.plugin.Plugin files much simpler. This situation arises, e.g., if you are making an uber-JAR out of multiple already-existing JARs, each with their own plugin metadata cache. (Which we don’t recommend you do—but if you need to do it, then the AnnotationCombiner is there for you.)

final PluginFinder finder = new PluginFinder(myAwesomeClassLoader);
final PluginIndex index = new PluginIndex(finder);
index.discover();
final List<PluginInfo<?>> soManyPlugins = index.getAll();
final List<PluginInfo<Command>> justCommands = index.getPlugins(Command.class);

Got it, thanks. And I can believe that the current system makes generating the metadata easier. It’s a pity JSON isn’t a more flexible format. Oh well.

Thanks for the example. I had to replace PluginFinder with DefaultPluginFinder for it to compile, but that’s pretty minor. Then I had to call the loadClass() method on the PluginInfos to force them to actually load the class, since they’re lazy. The weird thing is that two of our plugins were consistently working without the loadClass() call, I guess because they’re comparatively simple plugins?

In any event, it works, it appears to be equivalently fast as our manual method, and I’m not responsible for manually parsing plugin metadata any more, so that’s a win. Thanks again!

-Chris

1 Like