Ticket #931 (closed defect: fixed)

Opened 7 years ago

Last modified 7 years ago

Compile and run plugin fails

Reported by: bdezonia Owned by: bdezonia
Priority: major Milestone: imagej2-b1-initial
Component: Legacy Compatibility Version:
Severity: serious Keywords:
Cc: Blocked By:
Blocking:

Description

The plugin below was meant to be run after loading Boats. It works in IJ1. In IJ2 it seems to do nothing. Investigate why and address.

There used to be a tickets related to compile and run that were addressed by a number of edits. But something is now amiss. See #760 and #688.

Plugin looks like this:

import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.*;
import ij.plugin.frame.*;

public class ChangeCurrImpToMultichannelColorImpPlugin implements PlugIn {

private final int X = 720;
private final int Y = 576;

public void run(String arg) {

ImagePlus currImp = WindowManager.getCurrentImage();
ImageStack stack = new ImageStack(X,Y,null);
stack.addSlice("0",plane(0));
stack.addSlice("1",plane(1));
stack.addSlice("2",plane(2));
currImp.setStack(stack);
System.out.println("is color = " + (currImp.getType()==ImagePlus.COLOR_RGB));
System.out.println(" num chan = " + currImp.getNChannels());

}

private int[] plane(int channel) {

int[] plane = new int[X * Y];
int pixelVal = (0xff << 24) | (0xff << (channel*8));
for (int i = 0; i < plane.length; i++)

plane[i] = pixelVal;

return plane;

}

}

Change History

comment:1 Changed 7 years ago by bdezonia

The main issue with Compile & Run is that it is a plugin that calls other plugins. These plugins run in their own thread.

So the compile and run plugin returns quickly since it does no work. LegacyPlugin::run() thus completes. The other thread that actually runs the plugin chosen by the user can take as much time as it needs.

This activity is not detected by LegacyPlugin::run()'s harmonization unless the plugin terminates very quickly. In general there is a race condition on whether the user plugin can affect the output Datasets (with it very likely not making changes but worse it is theoretically possible it could do so partially).

The user plugin will change the parallel ImagePluses but those changes usually don't make it back to the Dataset. And the next time a legacy plugin is run that ImagePlus data is lost due to pre-run() harmonization.

Some possible solutions to this problem:

  • use Javassist to capture the (numerous) runPlugin*() methods in IJ1's IJ class and IJ1's ImageJ class and do something smart. Ideally IJ1 runPlugin*() calls would delegate to our legacy harmonizing version of code contained in LegacyPlugin.
  • Write our own single threaded implementation of the compiler plugin.

Note that the first option might be too complex. It's not immediately clear how we would fashion this. The second option may not be adequate either. Any user plugin that ran other plugins would suffer similar issues. So we likely need some code from the first approach no matter what.

comment:2 Changed 7 years ago by bdezonia

Just noting also that the current waitForThreads() method doesn't really work. LegacyPlugin::run() waits 50 milliseconds before it checks for newly hatched threads. But the compiler could take a long time before it has a class that it can instantiate in a new thread. So the new thread goes unnoticed and we don't wait for it. I will investigate ways to notice new threads and wait on them also. Perhaps this would fix the problem.

comment:3 Changed 7 years ago by bdezonia

An additional note: I cannot verify that the user chosen plugin ever runs. Simple plugins (i.e. change all the pixel values to 99) that run fine in IJ1 don't actually change any pixels in IJ2. The ImagePlus passed to the plugin is unchanged upon return from plugin (even waiting 10 seconds before harmonizing in LegacyPlugin::run()). Putting IJ.log() calls in the plugin's run() method does not open a log window in IJ2 when run. That may be a separate issue though. Also tried writing a file to /tmp upon entry to plugin. In IJ1 file is written. In IJ2 it is not.

Last edited 7 years ago by bdezonia (previous) (diff)

comment:4 Changed 7 years ago by bdezonia

I've verified the plugins don't run. In IJ.runPlugin() the ClassLoader cannot find the class the user has chosen. This could explain why results never show and implies that LegacyPlugin::waitForThreads() does not need to be made more complex.

Determine why class is not found. What kind of class path setting has to take place and where? I thought Compile And Run could run and load a class from any path.

comment:5 Changed 7 years ago by bdezonia

The failure to run any plugin is an IJ1 limitation. It requires the plugin be sitting in the plugins directory of the installation. Not our issue.

It rears its head here because I am running from eclipse and the actual correct relative plugins path is somewhere else.

Not sure this is a valid ticket anymore but it has important information. Keep for now as we debug waitForThreads() and plugins that call other plugins.

comment:6 Changed 7 years ago by bdezonia

Setting classpath in Eclipse to include the plugin dir gets the plugin to actually run. Results are consistent with IJ1.

Note that it leaves two copies sitting around of the composite multichannel image. One correctly reacts to slider changes. One of them throws a lot of exceptions when interacting with it and sliders eventually disappear.

comment:7 Changed 7 years ago by bdezonia

  • Status changed from new to closed
  • Resolution set to fixed

In 70fbbb7e1f49187c09e6721a35fe6d6618db1285 made changes that close temporary windows. The waitForThreads() code actually works and you can now successfully run Compile and Run provided plugin is sitting in the app's plugins directory.

Note: See TracTickets for help on using tickets.