[ImageJ-devel] Testing the HelloWorld Plugin

Johannes Schindelin Johannes.Schindelin at gmx.de
Wed Jul 3 13:57:25 CDT 2013


Hi Mohamed,

On Wed, 3 Jul 2013, Mohamed Tleis wrote:

> On 07/02/2013 07:49 PM, Johannes Schindelin wrote:
> >
> > On Tue, 2 Jul 2013, Johannes Schindelin wrote:
> >
> > > On Tue, 2 Jul 2013, Mohamed Tleis wrote:
> > >
> > > > I wanted to test the HelloWorld Plugin from the tutorials. I named
> > > > it HelloWorld_Plugin and copied the compiled class to the plugins
> > > > folder.  I have Hello World Plugin in the Plugins Menu now, but it
> > > > does nothing upon clicking.
> > > >
> > > > [...]
> > > >
> > > > 2. Why Hello World dialog message doens't display upon accessing
> > > > from the menu?
> > >
> > > Did you have a look at the console output?
> >
> > Just to make sure that nothing silly is happening, I did this (you
> > might want to explain in as much detail what you did in the future):
> >
> > 1) I made sure that my imagej-tutorials is up-to-date
> >
> > 2) I used Maven to build (this is by far the easiest thing to do):
> >
> >  mvn
> >
> >     You could use your development environment of choice (Eclipse,
> >     Netbeans, IntelliJ, etc) to build it, too.
> >
> > 3) I copied the generated .jar file (which is called
> >     simple-command-1.0.0-SNAPSHOT.jar) from simple-command/target/ to my
> >     ImageJ.app/plugins/
> >
> > 4) I started ImageJ2 via ImageJ.app/ImageJ-linux64
> >
> > 5) I started the command launcher by hitting the 'L' key
> >
> > 6) I typed "Hello" in the search box
> >
> > 7) I called the "Hello, World!" plugin
> >
> > It did show a message box, greeting the whole world. So basically I have
> > no clue what went wrong on your side, but I am sure we can find out
> > together if you provide every bit of useful information you can extract
> > from your trial.

FWIW in the future you could spare everyone reading your reply a couple of
minutes by culling the part from the quoted mail that you did not address
in your reply. If you think about the number of readers, multiply that by
those couple of minutes, you get the idea how much time you asked others
to spend on your mail. If you can spend less than that to reduce that
overall time, well, maybe you want to do that?

> I followed the exact steps as you did, and noticed the path of
> HelloWorldPlugin is in the Help Menu. and it is working fine now,

Good.

> although I am wondering how to change the menu path.

As so often, the answer is in the source code. Asking the web browser to
search for "Help" in the page on GitHub found me this gem:

	https://github.com/imagej/imagej-tutorials/blob/master/simple-command/src/main/java/HelloWorldPlugin.java#L39

It is the "menuPath" attribute of the @Plugin annotation.

> Building the HelloWorldPlugin.java alone, and copying the
> HelloWorldPlugin.class into the Plugins folder did nothing.

Yes. ImageJ2 does not support bare .class files in the plugins/ folder
(except insofar necessary for backwards-compatibility).

Allowing that encourages sloppy development, and it is all-too-easy for
infrequent programmers to forget the intricacies: the name must contain an
underscore, the class must be in the default package, or in a one-level
package and the file in a subdirectory, but not two-level package, or the
class must be in the default package and the file in a one-level
subdirectory of plugins/, but not in a two-level directory, etc.

Also, it was quite often the case that you got single .class files that
were missing crucial other .class files to run. A completely inefficient
and annoying back-and-forth had to ensue until everything was in place to
finally run the plugin. This is particularly true for .java files
defining more than one class. I am not talking about theoretical problems
here, have a look at famous plugins such as TurboReg.

Therefore, ImageJ2 only supports fully self-contained .jar files. They
contain everything that you need to run the plugin, can contain resources
such as icons, information that is put there at compile time, and a lot
more. Also, .jar files are much more space-efficient than unpacked .class
files will ever be.

And once you have more than two plugins in your plugins/ folder, it should
be a no-brainer to see that two .jar files are much easier to organize
than a couple of possibly-interrelated .class files, possibly scattered
over multiple directories (because you cannot import classes from the
default package).

> However compiling it as HelloWorld_Plugin.class did indeed show the
> HelloWorld Plugin in the Plugins Menu but clicking it performs nothing,
> and not even in the console output (by running ImageJ-linux64 from
> terminal).

Well, ImageJ 1.x does not look at the contents of the .class file -- ever!
-- to determine whether it is a plugin or not. All it does is to look at
the underscore. If it is present, it must be a plugin, right?

And since ImageJ2 aims to be as backwards-compatible as possible, it
inherits that plugin via the legacy layer.

Now, there is some very, very obscure feature in ImageJ 1.x: if you have a
class whose name contains an underscore but which does not implement the
PlugInFilter nor the PlugIn interface, it is *still* constructed. In fact,
quite a few "plugins" use that feature to perform all their computation in
the constructor!

So ImageJ2 handed off to ImageJ 1.x in your case, which faithfully applied
that obscure feature and constructed the plugin. And that was that. It
just constructed it and then did nothing with it.

It is safe to say that this feature -- encouraging all the computation of
the plugin to happen in the constructor (which is intended to *initialize*
the plugin in preparation for the computation) -- contradicts pretty much
everything you will learn about object-oriented programming and best
practices, and for a very good reason: it opens a can of problems such as
the absence of proper error handling (there is only one way the plugin can
fail: it throws an exception, but of course from then on every internal
state of the plugin is lost!), the plugin's complete inability to
interact with the ImageJ core (once the constructor is done, it cannot
tell ImageJ what it did, and the plugin cannot do anything else from now
on), ImageJ's complete absence of knowledge what the plugin is expected to
do (Does it work on images? Does it create new windows? Is it supposed to
perform a computation?), basically every technique developed in the
object-oriented paradigm to help establishing a structure that helps
develop robust software, fast, cannot be applied.

Another problem it causes is that you cannot tell whether the plugin
did something or not because the constructor's purpose is to *prepare* the
plugin for execution, not to execute it.

Needless to say, it is not our intention to encourage such bad practices
(because after all, we will be stuck with helping scientists debug their
code which gets really boring really fast when the code makes exactly that
debugging hard).

Another rather big down-side of bare class files in plugins/ is that you
could not easily put it into a different location (for example, to
unclutter the already overloaded Plugins menu). Sure, you could put it
into a submenu of Plugins, but not, say, into Help. This is different with
ImageJ2 plugins where the @Plugin annotation determines the menuPath, but
it is easy to see how confused people would be if the menu location did
not change when they moved a .class into a subdirectory of plugins/, no?

Your case is probably the best demonstration for yet another reason why it
would be a bad idea to support ImageJ2 plugins as bare .class files in the
plugins/ directory: Due to above-mentioned obscure ImageJ 1.x' feature,
ImageJ2 would have to spend an substantial amount of time at startup to
determine whether it is an ImageJ2 or ImageJ1 plugin.

Let's keep things simple and easy instead: all ImageJ2 plugins require are
@Plugin annotations, and that they are bundled in .jar files. That's it.

Ciao,
Johannes



More information about the ImageJ-devel mailing list