[ImageJ-devel] [LOCI ImageJ] IJ2 Plugin Service
warrick at wisc.edu
Wed Apr 30 19:49:27 CDT 2014
Thanks for all these replies folks. Digging around yesterday afternoon and evening essentially painted a picture much like what Johannes indicated in his email. The getResource() method works just fine and works roughly the same whether you do A) jars-in-a-jar or B) jars (reference libraries) in a folder next to a jar (my program), which are two of the 3 possible modes of exporting runnable jars. The third option, C), actually extracts all the class files and reorganizes them into a single flat jar (i.e., not jar-in-jar but just a single uber-jar of classes in folders), which I haven't used in the past for other reasons as well.
Instead, it seems (I say seems because I only dabble in ClassLoaders) their is no way to compile a list of my program's class files for indexing annotation using just the method getResource() or getResources() without knowing the name of at least a single resource in my program's jar. The getResources() method just gives a list of the referenced jars and not the main program's jar itself. However, if I want sci-java to work agnostic of who's using it, I can't assume within the sci-java code that I ever know of a specific resource to look for. Even if getResource() does give a path to my program's jar, the jar does not have the META-INF/json/<package>.Plugin file that contains the results of indexing. The key to the EclipseHelper is that it actually uses the getURLs() method of the URLClassLoader instead of the getResources(). When running from eclipse, this method gives the path to my referenced jars and the programs bin folder. When running from jar, the getURLs() method returns both the referenced libraries and my programs jar file (yay). However, since the jar file is not a directory, the EclipseHelper method does not index it and write the META-INF/json/<package>.Plugin file. The iteration method it uses requires directories, which is fine and dandy when running from within Eclipse but my program's JarFile doesn't look like a directory so the exact same mechanism doesn't work. However, it looks like a method analogous to this approach but for JarFiles might work., right? I think, to work, it would have to iteratively index the contents of the JarFile and add a META-INF/json/<package>.Plugin file as a new Jar entry (as far as I can see anyway, I could definitely be wrong). I am trying to hack something together to show whether this would work at all but if any of you think this approach would benefit others, I'd welcome suggestions and would try to clean it up as much as I can for a pull request. It seems like the EclipseHelper is used when the class loader is an instance of URLClassLoader (plus some other tests...) which happens to occur both when running from eclipse and when exported as a runnable jar. So, on the surface at least, it looks like the EclipseHelper class could be used for both scenarios but am happy to change tacks if you think otherwise.
However, I have a couple specific questions, that if answered would help me to know if this might work at all and move forward.
0) Does my take on the situation seem right?
1) If I edit the programs jar file during run time by adding the META-INF/json/<package>.Plugin jar entry, will that majorly hose things?
2) The Index class still uses the getResources() method to find things and it will still only link to the referenced jar files. In this case we can't assume that the ClassLoader is a URLClassLoader to be able to use the getURLs method. I don't recall noticing when or how the indexing mechanism looks for these META-INF/json/<package>.Plugin files to compile all the annotation information. I assume it is via the getResource() method because I believe by knowing what to look for (i.e., a META-INF/json/<package>.Plugin file), we can just ask for the resource directly? In other words, if I can get the META-INF/json/<package>.Plugin written to my program's jar analogous to the other referenced jars, then everything will be OK?
Thanks thanks thanks,
On Apr 30, 2014, at 12:51 PM, Johannes Schindelin <Johannes.Schindelin at gmx.de> wrote:
> On Wed, 30 Apr 2014, Curtis Rueden wrote:
>>> when I export the application as a runnable jar, ij.plugin()service
>>> returns all the Command.class plugins but 0 plugins of type
>> This is an issue we have discussed before: Eclipse creates uberjars using a
>> "jar-in-jar" approach, and SciJava Common's plugin mechanism does not read
>> the metadata out of a jar-in-jar.
> Please note that the jar-in-jar poses no problem, unless the ClassLoader
> used to access them is broken: it needs to support the getResources() call
> properly and find the resource files contained in the nested .jar files.
> However, in the reported case I believe it is not triggered by the uber
> jar or jar-in-jar scenario.
> Background: The internal technique behind the plugins uses annotation
> processors run at compile time. They basically look at each file that has
> a @Plugin annotation and write out index files that get included into the
> .jar files.
> Except that Eclipse -- violating the Java specification -- does not run
> annotation processors. At least not by default, and even if you switch it
> on (manually, for each and every project you maintain, one by one), it
> *still* only runs them on full builds (i.e. after Project>Clean).
> So it looks to me that in the reported case, the annotation processor is
> never run, and as a consequence, the index file is never written, and
> therefore it cannot be found at runtime.
> Of course, Eclipse being such a prevalent platform to develop in, we tried
> to come up with a workaround: whenever the annotation indexes are read, a
> class called "EclipseHelper" tries to detect whether it needs to create
> the index files because Eclipse failed to run the annotation processors.
> This works amazingly well because many developers have written unit tests
> and run them before bundling .jar files manually. These unit tests verify
> that plugins work, of course, which is why the EclipseHelper works around
> the problem successfully in most cases.
> Also, here is a lesson for everybody choosing to learn from our past
> mistakes and experiences: any possible convenience of uber jars is
> outweighed multiple times over by the disadvantages it incurs to users: it
> makes updating really costly (every time it's time to update, a new
> monster .jar needs to be downloaded), it makes collaboration between
> projects difficult at best, and it certainly asks for version skew.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the ImageJ-devel