<div dir="ltr">Hi Jay,<div><br></div><div><div>> The iteration method it uses requires directories, which is fine and</div><div>> dandy when running from within Eclipse but my program's JarFile</div><div>> doesn't look like a directory so the exact same mechanism doesn't</div>


<div>> work.</div></div><div><br></div><div>EclipseHelper is intended to work when running from Eclipse, and never otherwise. That is why it does not support generating annotations from classes within JAR files.</div>

<div>
<br></div><div><div>> However, it looks like a method analogous to this approach but for</div><div>> JarFiles might work., right?</div></div><div><br></div><div>I don't understand why you would ever need to do that... the point of EclipseHelper is to generate the annotations (META-INF/json/*) as close to compile time as possible -- not later at runtime, which would be the only time your code would be stored within a JAR file.</div>


<div><br></div><div><div>> 0) Does my take on the situation seem right?</div></div><div><br></div><div>While the technical aspects of what you are saying are correct to my knowledge (NB: I did not investigate the usage of getURLs()), I do not think it makes sense to invoke an EclipseHelper-like program during the actual execution of your program. By doing that, you would lose all the performance advantages of generating the annotations in advance.</div>


<div><br></div><div>For ImageJ2 we use Maven from the command line to bundle our JARs and that generates the annotations as expected. An alternate approach, if you really really want Eclipse to do it for you instead, you might be to do it as part of Maven's generate-sources step; see the ImageJ OPS project for an example of that [1]. You'd just have to change the groovy script to invoke the annotation processor manually. But I haven't seriously explored doing things that way. And JEX would need to be structured as a Maven project for that...</div>


<div><div><br></div><div>> 1) If I edit the programs jar file during run time by adding the</div><div>> META-INF/json/<package>.Plugin jar entry, will that majorly hose</div><div>> things?</div></div><div>

<br>
</div><div>Yep, I would be surprised if that did not cause problems. I strongly advise not doing that.</div><div><br></div><div><div>> In other words, if I can get the META-INF/json/<package>.Plugin</div><div>> written to my program's jar analogous to the other referenced jars,</div>


<div>> then everything will be OK?</div></div><div><br></div><div>Yes, the goal is definitely to somehow generate your JAR file such that it contains the META-INF/json/* metadata from the get-go, without needing to pull any tricks the first time JEX is launched.</div>


<div><br></div><div>Another possibility is to add a .factorypath and .settings/org.eclipse.jdt.apt.core.prefs file, similar to:</div><div><br></div><div>.settings/org.eclipse.jdt.apt.core.prefs:</div><div><div>eclipse.preferences.version=1</div>


<div>org.eclipse.jdt.apt.aptEnabled=true</div><div>org.eclipse.jdt.apt.genSrcDir=target/classes</div><div>org.eclipse.jdt.apt.reconcileEnabled=false</div></div><div><br></div><div><div>.factorypath:</div><div><factorypath><br>


</div><div><div>  <factorypathentry kind="VARJAR" id="M2_REPO/org/scijava/scijava-common/2.18.1/scijava-common-2.18.1.jar" enabled="true" runInBatchMode="true"/></div><div></factorypath></div>


</div><div><br></div></div><div>But you'd want to ensure it matches your version of scijava-common, and also point it at wherever you have that JAR stored rather than into the Maven repository (which you presumably don't have since you aren't using Maven).</div>


<div><br></div><div>Eclipse unfortunately makes invoking annotation processors quite complicated... [2]</div><div><br></div><div>Regards,</div><div>Curtis</div><div><br></div><div>[1] <a href="https://github.com/imagej/imagej-ops/blob/imagej-ops-0.3.0/pom.xml#L169" target="_blank">https://github.com/imagej/imagej-ops/blob/imagej-ops-0.3.0/pom.xml#L169</a></div>


<div>[2] <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=280542" target="_blank">https://bugs.eclipse.org/bugs/show_bug.cgi?id=280542</a></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Wed, Apr 30, 2014 at 7:49 PM, Jay Warrick <span dir="ltr"><<a href="mailto:warrick@wisc.edu" target="_blank">warrick@wisc.edu</a>></span> wrote:<br>


<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div>Hi All,</div><div><br></div>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. <div>


<br></div><div>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.</div>


<div><br></div><div>However, I have a couple specific questions, that if answered would help me to know if this might work at all and move forward.</div><div><br></div><div><b>0) Does my take on the situation seem right?</b></div>


<div><br></div><div><b>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?</b></div><div><b><br></b></div><div><b>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?</b></div>


<div><br></div><div>Thanks thanks thanks,</div><div><br></div><div>Jay</div><div><div><div><br></div><div><div><div><div>On Apr 30, 2014, at 12:51 PM, Johannes Schindelin <<a href="mailto:Johannes.Schindelin@gmx.de" target="_blank">Johannes.Schindelin@gmx.de</a>> wrote:</div>


<br><blockquote type="cite">Hi,<br><br>On Wed, 30 Apr 2014, Curtis Rueden wrote:<br><br><blockquote type="cite"><blockquote type="cite">when I export the application as a runnable jar, ij.plugin()service<br>returns all the Command.class plugins but 0 plugins of type<br>


JEXPlugin.class<br></blockquote><br>This is an issue we have discussed before: Eclipse creates uberjars using a<br>"jar-in-jar" approach, and SciJava Common's plugin mechanism does not read<br>the metadata out of a jar-in-jar.<br>


</blockquote><br>Please note that the jar-in-jar poses no problem, unless the ClassLoader<br>used to access them is broken: it needs to support the getResources() call<br>properly and find the resource files contained in the nested .jar files.<br>


<br>However, in the reported case I believe it is not triggered by the uber<br>jar or jar-in-jar scenario.<br><br>Background: The internal technique behind the plugins uses annotation<br>processors run at compile time. They basically look at each file that has<br>


a @Plugin annotation and write out index files that get included into the<br>.jar files.<br><br>Except that Eclipse -- violating the Java specification -- does not run<br>annotation processors. At least not by default, and even if you switch it<br>


on (manually, for each and every project you maintain, one by one), it<br>*still* only runs them on full builds (i.e. after Project>Clean).<br><br>So it looks to me that in the reported case, the annotation processor is<br>


never run, and as a consequence, the index file is never written, and<br>therefore it cannot be found at runtime.<br><br>Of course, Eclipse being such a prevalent platform to develop in, we tried<br>to come up with a workaround: whenever the annotation indexes are read, a<br>


class called "EclipseHelper" tries to detect whether it needs to create<br>the index files because Eclipse failed to run the annotation processors.<br><br>This works amazingly well because many developers have written unit tests<br>


and run them before bundling .jar files manually. These unit tests verify<br>that plugins work, of course, which is why the EclipseHelper works around<br>the problem successfully in most cases.<br><br>Also, here is a lesson for everybody choosing to learn from our past<br>


mistakes and experiences: any possible convenience of uber jars is<br>outweighed multiple times over by the disadvantages it incurs to users: it<br>makes updating really costly (every time it's time to update, a new<br>


monster .jar needs to be downloaded), it makes collaboration between<br>projects difficult at best, and it certainly asks for version skew.<br><br>Ciao,<br>Johannes<br></blockquote></div><br></div></div></div></div></div>


</blockquote></div><br></div></div>