Bug 157375 - PDE should support building against nested JARs
Summary: PDE should support building against nested JARs
Status: RESOLVED FIXED
Alias: None
Product: PDE
Classification: Eclipse Project
Component: Build (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P2 normal with 26 votes (vote)
Target Milestone: 3.7 M7   Edit
Assignee: pde-build-inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords: helpwanted
Depends on:
Blocks: 111238
  Show dependency tree
 
Reported: 2006-09-14 14:32 EDT by Erkki Lindpere CLA
Modified: 2011-03-29 16:17 EDT (History)
36 users (show)

See Also:


Attachments
patch (20.04 KB, patch)
2011-03-28 17:53 EDT, Andrew Niefer CLA
no flags Details | Diff
patch (24.52 KB, patch)
2011-03-29 16:17 EDT, Andrew Niefer CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Erkki Lindpere CLA 2006-09-14 14:32:46 EDT
For some reason that I do not understand, the fact that Mylar exports several apache packages creates compile errors in my plugins (can't resolve org.apache) when Mylar is installed.

To see the problem, import this team project set:
https://bugs.eclipse.org/bugs/attachment.cgi?id=50181

The PDE reports no errors for the manifests, but the classes that import anything org.apache give compile errors. If I run "Validate Plugin Set" in the launch configuration's dialog, it is ok, but the launch fails due to the same compilation errors.
Comment 1 Erkki Lindpere CLA 2006-09-14 14:35:42 EDT
I'm suspecting this might actually be a bug with PDE or JDT, since I didn't notice anything peculiar about the way in wich Mylar exports these packages.

If I remember correctly, the Project Set also requires Log4J, Commons Codec and Commons Logging (available from Callisto Discovery Site).
Comment 2 Mik Kersten CLA 2006-09-20 13:54:10 EDT
Have you made more progress on this Erkki?  I'm wondering if there is an change that we should make to our manifest that could address this, but as far as I can our export of those libraries should only be 'visible' to plug-ins that depend on a Mylar plug-in, and I assume that you don't have one of those.  It is possible that this is a bug with classpath resolution in PDE.  Which version of Eclipse are you running?

I tried to import the project set into a clean Eclipse but kept ketting errors on the import due to WorkspaceModifyOperation being run wrong (probably by Subclispe 1.1.6, not sure since stack did not indicate it).
Comment 3 Erkki Lindpere CLA 2006-09-21 14:55:37 EDT
There is something weird going on with this bug. The problem disappeared in my main workspace in one Eclipse session. Then I closed that Eclipse and opened the workspace that only contains the above project set and it still had the problem. Started another Eclipse (same configuration area) with my main workspace and the problem is back there too (and still remains after a few restarts).

Oh, and I'm using Eclipse 3.2

About importing the project set -- I'm using Subclipse 1.1.2 and it works so it might very well be a problem with that version of Subclipse.

I'll keep investigating.
Comment 4 Erkki Lindpere CLA 2006-09-21 15:33:33 EDT
Problem disappears if I import org.eclipse.mylar.context.core into the workspace as a project. Even if I then delete the project, the problem is gone until Eclipse is restarted.

Maybe the following could also have something to do with this:
1) if I declare Import-Package dependencies, not Require-Bundle, then I can import from the Mylar plugin even if I don't explicitly depend on it.
2) mylar exports both commons codec & httpclient
3) an org.apache.commons.codec bundle is also present (from Callisto Discovery Site). It also exports the codec packages.
4) OSGi R4 3.5.6 Exporting and Importing a Package:
"Exporting a package does not imply the import of that same package (in 
Release 3, an export did imply an import). The reason for this separation is 
that it enables a bundle to provide a package to other bundles without hav-
ing to take into account that the exported package could be substituted by the 
resolver with the same package from another bundle. ...
Bundles should import exported packages, allowing the resolver to substi-
tute packages that contain interfaces and other shared types. This substitu-
tion allows bundles to inter-operate through the service registry and other 
mechanisms. Additionally, the import should be as unconstrained as possi-
ble to allow the resolver maximum flexibility."

So maybe mylar should also import the commons.codec packages so they can be substituted by another bundle providing those packages? But still, it seems to be a PDE problem.
Comment 5 Mik Kersten CLA 2006-09-22 11:18:42 EDT
Wassim, could I ask you to take a look at the description and comment#4?  The org.eclipse.mylar.context.core plug-in is exporting some Apache libraries not (yet) available in the SDK or Orbit update site, but our expectation is that this should not affect any plug-ins that are not depending on Mylar, whereas Erkki is reporting build problems (using Eclipse 3.2).  Here's what we're doing:

Export-Package: org.apache.commons.codec,
 org.apache.commons.codec.binary,
 org.apache.commons.codec.digest,
 org.apache.commons.codec.language,
 org.apache.commons.codec.net,
 org.apache.commons.httpclient,
....
 org.eclipse.mylar.context.core,
 org.eclipse.mylar.internal.context.core,
 org.eclipse.mylar.internal.context.core.util
Bundle-ClassPath: .,
 lib/commons-codec-1.3.jar,
 lib/commons-httpclient-3.0.jar,
 lib/commons-logging.jar,
 lib/xmlrpc-client-3.0b1.jar,
 lib/xmlrpc-common-3.0b1.jar,
 lib/ws-commons-util-1.0.1.jar
Comment 6 Mik Kersten CLA 2006-10-20 00:06:23 EDT
Errki: is this still causing you problems?  Any other clues as to where the blame should be assigned?  If you haven't yet please try it with Eclipse 3.3M2.
Comment 7 Wassim Melhem CLA 2006-10-20 00:11:03 EDT
Mik, a plug-in may declare a dependency on a package via Import-Package (ie. without explicitly specifying what plug-in the package is coming from).  In which case, these plug-ins may get wired to your mylar plug-in if they are importing a package that you export.
Comment 8 Erkki Lindpere CLA 2006-10-20 09:44:23 EDT
Sadly I've been quite busy and haven't been able to spend much time on this. My suggestion in comment #4 that Mylar should maybe Import-Package the same packages as well did not help -- I hacked the bundle's manifest to try that, but it did not help. Hmm... I don't remember if I did a -clean when I tried that. Maybe that would be important.

I will try to investigate more if I have time.
Comment 9 Mik Kersten CLA 2006-10-20 16:19:48 EDT
Thanks for the clarification Wassim, I didn't realize that Import-Package did not follow the dependency chain.  So is there no way to ensure that our copy of the Apache clients is not visible to plug-ins that depend on another copy?
Comment 10 Erkki Lindpere CLA 2006-10-21 13:17:46 EDT
I might be wrong, but I think that if you could make sure that the Export-Package headers contain proper version information, then they wouldn't conflict with my bundle's exported packages that are of a slightly different version.
Comment 11 Erkki Lindpere CLA 2006-10-21 14:00:14 EDT
Actually, this doesn't seem to help. I just tried adding versions to all the relevant Export-Package and Import-Package headers (both mylar's and mine), but my httpclient 3.0.1 still isn't resolved when mylar's httpclient 3.0 is present.

This seems like an issue with how the PDE handles export/import of packages. As far as I know, the vast majority, if not all dependencies, in Eclipse and it's subprojects are based on Require-Bundle instead of Import-Package so that may be why this issue hasn't popped up earlier (or has it?).

I think the OSGi recommended way is to use Import-Package instead of Require-Bundle, but I guess that would be quite complex to manage for something the size of Eclipse and/or maybe that recommendation isn't otherwise applicable to Eclipse's case?

Note: I still haven't tried this in 3.3M2 or another 3.3 build. I will do that.

Maybe I should export these bundles and use them outside the PDE and see if they resolve. Or try them in another OSGi framework... but maybe that isn't possible because of the org.eclipse.mylar.context.core dependency on org.eclipse.core.runtime?
Comment 12 Erkki Lindpere CLA 2006-10-21 14:08:51 EDT
I can confirm that replacing the Import-Package statements in my plugin with Require-Bundle: org.apache.commons.httpclient (which is the one in my workspace that exports the httpclient packages) the errors go away. However, I do not really want to do that -- in reality I don't care which bundle provides the httpclient packages.
Comment 13 Erkki Lindpere CLA 2006-10-21 15:38:50 EDT
I tried 3.3M2 and Mylar 0.8.0 -- no change.

I'm now thinking this has something to do with binary bundles in the target platform -- if Mylar is disabled, the problem also appears when my org.apache.commons.httpclient bundle in the workspace is closed, but is present as a binary bundle in the target platform.

Note: my target platform is the same eclipse instance that I'm running.

NB! Both my httpclient bundle and the mylar bundle export packages from *.jar files in their Bundle-Classpath. Could it be that PDE is unable to properly handle Import-Package: in case the package comes from a *.jar file inside a binary bundle (that is also a *.jar file, not a directory)?

Sorry for spamming so much. I think this last idea might be getting close, though...
Comment 14 Wassim Melhem CLA 2006-10-21 20:36:43 EDT
There is nothing wrong with the way Import-Package is being handled by the framework or by PDE.   That's how it is supposed to work. 

When you use Import-Package, you effectively decouple yourself from the producer of that package.  That's the point.  So your import statement get wired to an exporter of that package.  In the case of multiple exporters and if you have extra restraints on the imported package (ie. if you don't specify a version etc.), you get wired to a random exporter of that package.

Note that it is also possible for an importer to specify which plug-in they want the package to come from.  I can't remember what that directive is though.

cc Tom as he would know and would generally be more able to shed more light on the matter.
Comment 15 Erkki Lindpere CLA 2006-10-22 03:39:39 EDT
The problem never was that I got wired to Mylar packages, but that I got compilation errors (can't resolve org.apache)
Comment 16 Erkki Lindpere CLA 2006-10-22 03:58:18 EDT
Ok, here are the steps to reproduce this problem:

1) create a Java or Plugin project named 'jar'. Create a package 'test.jar' in it, and an empty class MyClass in the package.

2) Then create a jar file from this plugin -- I created it as a plugin project and just let PDE export a deployable plugin into the same project's directory.

3) Create a "plugin from existing jar" from the abovementioned jar called 'test.jar'. Do not unzip the jar into the plugin. This 'test.jar' plugin will export the package 'test.jar'.

4) Create a new plugin, "test.jarclient" for example from the Hello OSGi Bundle template. This plugin will import the package 'test.jar'. In the activator, do something like System.out.println("Hello " + MyClass.class); (where MyClass is the class created in step 1)

At this point everything works.

5) Export the 'test.jar' bundle into your target platform and do whatever you have to do to refresh the target platform.

6) Close the workspace project 'test.jar'

Voila! Compilation errors!
Comment 17 Erkki Lindpere CLA 2006-10-27 17:22:55 EDT
Ok, here's another way to get this problem, and it doesn't even involve Import-Package this time:

1) install Mylar (0.8.0 for example)
2) check out /home/technology/HEAD/org.eclipse.mylar/sandbox/org.eclipse.mylar.sandbox

Voilà! Compilation errors where there should not be any, because org.eclipse.context.core is definitely there and exports the required packages.
Comment 18 Mik Kersten CLA 2006-10-30 14:14:13 EST
If in exploring this anyone finds fault with the Mylar plug-in's use of dependencies, please comment here to let us know.

Wassim, Tom: regarding "it is also possible for an importer to specify which plug-in they want the package to come from.  I can't remember what that directive is though", is this something that PDE will support?  It seems like it could be good idea in our case, since we only test against the particular version of httpclient that we redistribute.  
Comment 19 Thomas Watson CLA 2006-10-30 15:29:51 EST
Sorry I did not take the time to look at this earlier.  The comments in this bug about Import-Package vs Require-Bundle etc are chasing a red herring.  Comment 13 and comment 16 really demonstrate the real issue here.

I found the root problem is when the org.eclipse.mylar.core.context is a binary contained in the target platform.

The problem is this bundle contains inner library jars, one of which contains the httpclient code (commons-httpclient-3.0.jar).  The feature for mylar declares that org.eclipse.mylar.core.context should not be unpacked ...

   <plugin
         id="org.eclipse.mylar.context.core"
         download-size="0"
         install-size="0"
         version="0.8.0"
         unpack="false"/>

PDE (well actually it is JDT) does not allow jars within jars to be added to the classpath for compiling projects.  If you have an inner library in your bundle AND you use Export-Package to expose packages from the inner library jars then you must specify unpack="true" for that bundle to get your bundle laid out on disk correctly.  This will allow bundles to use your binary jars to develop against.

Note that if you do this then you will likely want to package the other mylar core context code into a library "inner" jar also instead of having '.' on your Bundle-ClassPath.  Otherwise when your bundle is unpacked you will have all of your .class files laying out on the disk in your intallation folder.
Comment 20 Erkki Lindpere CLA 2006-10-30 16:04:27 EST
What if I do not have any features, but only bundles? In that case should I avoid having jars inside jars and unzip classes from any external jars, such as commons-httpclient, into the project folder?

Is the Orbit project aware of this issue?
Comment 21 Thomas Watson CLA 2006-10-30 17:38:55 EST
CC'ing Jeff for orbit question in comment 20.

CC'ing Pascal for feature/plugin build question in comment 20.

I do not know the specifics about building expanded plug-ins but this must be possible because all bundles were shipped this way in Eclipse 3.0.  If you do not have a feature to install your bundles then you are just unzipping or copying a bunch of plug-ins into your plug-ins directory (or linking to an extensions directory etc.).  You can unzip or copy an expanded directory to the plugins directory just like normal jar'ed bundles.
Comment 22 Jeff McAffer CLA 2006-10-30 20:43:47 EST
This is not an Orbit issue for a few reasons
- In general Orbit will not need to nest JARs but rather will seek to package each library in its own JAR'd bundle.  no nesting

- For the most part this is a tooling (JDT/PDE) issue.  Equionx, for example, will run JAR'd bundles with nested bundles just fine.  Update will also deliver them and PDE Build will build/package them.  Its really just the case that Tom points out.

As for features, you may not ship the bundles in a feature but if you are using PDE build you are liklely building them using a feature.  Pascal can comment more.

In short, you should look to not nesting third party libs and start using the ones that are/will come from Orbit.
Comment 23 Mik Kersten CLA 2006-10-31 12:33:07 EST
Thanks for the clarifications.  Fyi, yes, we have always documented our nesting of those libraries in our top-level plug-in as a temporary work-around until we could consume them once something like Orbit became available.  So yes, the plan is to move to Orbit as soon as we can.  For now we'll try to figure out how soon Orbit will provide what we need, and in the interim consider pulling out the libraries into a separate bundle to avoid being forced to unpack the plug-in.
Comment 24 Jeff McAffer CLA 2006-10-31 15:25:19 EST
Of course, you could join Orbit and bundle the libs you need there. :-)
Comment 25 Mik Kersten CLA 2006-11-06 12:26:35 EST
Jeff: yes, my plan is to add the libs to Orbit as soon as we get an IP item clarified with httpclient.
Comment 26 Wassim Melhem CLA 2006-11-15 09:27:57 EST
There is nothing to do here on the PDE side since nested JARs cannot be put on the classpath.

Moving back to Mylar to track the unpacking of the bundle.
Comment 27 Mik Kersten CLA 2006-11-15 20:26:20 EST
Waiting for IP clarification which should be coming within the next 1-3 weeks, then moving dependencies to Orbit.
Comment 28 Mik Kersten CLA 2006-11-21 01:32:46 EST
Hoping for IP issues to be resolved in 1st week of December, so the move of httpclient to Orbit will have to wait until after that.

Wassim: aside from our making that change, should PDE not be handing the nested JAR case correctly (comment#16 and comment#19)?  It seems as though others 3rd party plug-in providers could easily get into this state and it is difficult to diagnose.  
Comment 29 Mik Kersten CLA 2006-12-10 20:17:17 EST
We're all set with IP approvals now so I have created the following bug Mylar bug to move our httpclient dependencies to Orbit: bug 167362

Leaving this open and moving to PDE because it describes a usage problem that I have seen several people encounter and be totally confused by, summarized in comment#16 and comment#19.  Regarding comment#26, I realize that there may not be a simple solution, and that this might require something tricky like passing a flag to JDT to provide special handling of class resolution to handle nested JARs.
Comment 30 Mik Kersten CLA 2007-07-11 18:37:15 EDT
Will this be considered for 3.4?  I have noticed plug-in developers run into this on multiple occasions, it is hard for them to diagnose, and has cause people to think that the right way to package plug-ins with nested JARs is to leave them unpacked.
Comment 31 Jeff McAffer CLA 2007-07-14 09:08:06 EDT
seems unlikely.  Indeed in many cases the most efficient way to run a bundle with nested JARs is to have it be unpacked.  Otherwise you end up with a first run hit for extraction and a continuing disk hit for the duplicated copies.  Of course, the best approach is simply to avoid nesting JARs.  in most cases this is possible.  In cases where it is not, it may be better to look at creating multiple bundles.  There are some situations like the the Ant tasks provided by bundles where the code needs to live on a different classloader.  there we have done the nesting thing but no one ever wants to compile against the nested code so this is not an issue.

On the other hand, it *seems* like it should not be horrendous to extract the JARs at build time and update the classpath to allow the compiler to see them.  So PDE build might be able to manage something (with some contributor help) but we would still have problems with PDE/JDT in the workspace.

summary, don't hold your breath and contributions are welcome here and in PDE/JDT
Comment 32 Mik Kersten CLA 2007-07-17 22:09:12 EDT
Thanks for the explanation Jeff, that makes sense.  On Mylyn we will do our part to avoid this scenario by trying to wean ourselves off including libraries with existing plug-ins (via Orbit).  However, I do think that this bug should still be considered and left open if the assumption is that PDE's build/lookup semantics should match what is supported by Equinox.  The currently do this so amazingly well that users end up getting quite confused by this one case where they don't.  Extracting JARs at build time any time that a library is added or modified sounds like a good approach for handling this case.
Comment 33 Eugene Kuleshov CLA 2007-08-17 12:50:47 EDT
What still confuses me is why it is an issue to use jars from within jar at the compile time? Equinox can access those jars and there are mechanisms like classpath containers (which PDE already uses) and EFS that should allow to abstract retrieval of those jars to make them available to JDT compiler. Also, if I am not mistaken, sun's javac even allows to specify urls in the classpath, so why can't JDT and PDE do the same?
Comment 34 Andrew Niefer CLA 2007-09-13 12:04:47 EDT
I think that pde.build could extract nested jars at build time and update the classpath accordingly.

However, as Jeff points out in comment #31, is there any point in build supporting this if UI doesn't?
Comment 35 Brian Atkinson CLA 2008-05-21 12:15:26 EDT
Anyone thinking about this anymore? I would like to work on resolving this bug; however, I have never worked directly on the eclipse projects yet so I might need a little direction to get started.  If anyone is interested in pointing me at the right projects (where this code lies) I should be able to take it from there.
Comment 36 Andrew Niefer CLA 2008-05-21 13:54:20 EDT
Brian, the project is in CVS:
dev.eclipse.org:/cvsroot/eclipse/org.eclipse.pde.build

ModelBuildScriptGenerator.generateCompilationTarget will generate a javac ant script to compile the given bundle.  It gets the classpath from ClasspathComputer3_0 (and ClasspathComputer2_0, but start with 3).

In particular, ClasspathComputer3_0.addRuntimeLibraries adds entries to the classpath that come from the given required bundle.  See in addPathAndCheck that if the basePath ends with .jar then we basically discount the nested library.

It is somewhere around here that the nested jar would need to be extracted.  The place to extract it to should probaby be under generator.getWorkingDirectory(), likely to something like  += "nested/bundle_version/lib.jar".
Comment 37 Tom Bryan CLA 2008-09-10 23:30:02 EDT
I agree with Mik: I hit this problem, and the issue was not obvious to me.  I killed a few hours trying to figure out why our build system couldn't compile my plug-in when the target platform had the required bundles that I knew provided the needed libraries at runtime.  

Any update on that possible fix for this problem that Brian was working on?  Or are you still looking for a patch?
Comment 38 ekkehard gentz CLA 2008-09-11 02:30:59 EDT
I hit the same problem while integrating an 'external' project (EasyBeans OSGI) -
life would be easier if PDE build supports nested JARs same as Equinox does running them.
Comment 39 Daniel Krügler CLA 2008-09-23 03:13:19 EDT
We also stumbled across this issue. The worst part of this bug is that you do not expect its effects, because the osgi has no problem unpacking those plugins during runtime.
This problem really hurts: Many (if not most) of our plugins contain jars, because
we try to restrict us to binary dependencies were possible.
Comment 40 ekkehard gentz CLA 2008-09-23 03:20:52 EDT
sometimes it helps to load those binary plugins into workspace,
but in some cases you have to open MANIFEST in workspace and to add "." to classpath.
then the compiler is happy - in runtime the original bundles can be used because equinox can handle it
Comment 41 Daniel Krügler CLA 2008-09-23 03:28:21 EDT
(In reply to comment #40)
All our plugins have "." added as the very first entry of the plugin class-path, so approach unfortunately this doesn't help. I'm not sure what you mean with "sometimes it helps to load those binary plugins into workspace", but our build process must be a headless one and the mentioned jar's are added to our plugins as part of a maven install.
Comment 42 ekkehard gentz CLA 2008-09-23 03:37:27 EDT
Daniel, 
I had some 3rdParty bundles causing problems until I explicitly opened MANIFEST, added "." to classpath and after saving it works well.
Comment 43 ekkehard gentz CLA 2008-09-23 03:41:51 EDT
daniel, sorry for confusing,
not "." - I had explicitly to add the jarfiles from root of the plugin (".")
Comment 44 Daniel Krügler CLA 2008-09-23 03:55:19 EDT
After recognizing this kind of bug, I really wonder why jar-packaged plugins are the recommended deployment style? 
And performing a workaround solution, that is, trying to change all the deployment styles of my affected plugins to unpacked forms has now the effect that my feature.xml is full of warnings - is it possible to get rid of this warning without switching-off other relevant warnings?
Comment 45 Daniel Krügler CLA 2008-09-26 07:21:37 EDT
I need to add a further comment here, because of this issue: Deploying library plugins (which contain additional jars) in unpacked form is *not* sufficient as
a workaround for this bug: It seems that one is supposed to change the canonical primary bundle-classpath entry "." to a named plugin library name, otherwise the
problem still persists. 

This is even worse than the first limitation, because we are now enforced to change 20+ plugin deployments of a stable and a released product. From the product side we had no problems at all since ages, because osgi just accepts the inner jars, but we cannot reuse these components as pre-builds for another depending Eclipse built - this reduces the chances for modular development a lot!

Being constrained in using explicitly named plugin libraries has also further disadvantages (beside the fact of possible naming collisions with added jar names), which I stumble across in that moment:
While a (currently) pure library plugin (without any own sources to be build) can easily accept "output.." and "bin.includes=.,\....." build.properties entries, this is not so with the named approach, because the warnings enforces one to remove the named entries from these files, leading to plugin architectures, that are less robust to latter extensions.
Comment 46 Andrew Niefer CLA 2008-09-26 10:11:38 EDT
I would not suggest changing the runtime shape of bundles for build time reasons.  On the build machine, in a pre.setup or pre.generate custom target, you could just unzip the bundles containing nested jars into folder shape.

Also, if you are changing the shape of bundles for this, conceivably you have only a few bundles with nested jars.  Those bundles could be changed to have the nested jars unzipped in the container jar and their class files placed in sub directories:

org.nested.foo.jar
         /library.jar
         /other.jar
         /META-INF/MANIFEST.MF : Bundle-Classpath: library.jar, other.jar

org.nested.foo.jar
         /library/org/..../*.class
         /other/org/..../*.class
         /META-INF/MANIFEST.MF : Bundle-Classpath: library, other

Comment 47 Jeff McAffer CLA 2008-09-28 13:24:48 EDT
wrt best practices, we actually recommend packaging each JAR as a bundle and not nesting JARs at all.  This is quite easily done with the PDE wizard and other tools such as bnd.

Of course, in some cases this is not particulraly feasible as there are many small JARs that go into making up a bundle.  Also, as the originator points out, there are some number of bundles out there with nested JARs (either legacy or from people out of your control).

Since this comes up over and over again, perhaps it is time to consider just unzipping things for people?  Andrew points out in comment 46 that doing this manually in pre.setup would do the trick.  If someone contributed a patch to do this as part of PDE build would that be an acceptable direction?  

It may be a little complicated but basically if the bundle has JARs on the classpath then unzip in a temp spot and us the temp location for classpath computations.  May need to be careful to use the original when packaging or assembling though as we want to avoid messing with the signing etc.

Adding zx to the CC as something similar might be needed in PDE UI.  Perhaps in the new spirit of sharing the support could be done once and reused...
Comment 48 Andrew Niefer CLA 2008-09-29 11:48:32 EDT
Notice comment #36 that outlines where in Build this kind of change would take place.  UI has their own classpath computer, I don't know how much of a candidate this is for sharing.
Comment 49 Jeff McAffer CLA 2008-09-29 14:20:06 EDT
Thanks Andrew. I'm not sure how often the code point you mention is called.  Is it called everytime a classpath is computed?  What happens when a nested JAR bundle is on the classpath of 200 other bundles? (that is, are the nested JARs extracted 200 times?)  Certainly in the PDE UI case they will want to cache these extracted guys and keep them up to date wrt timestamps etc.

Given these kinds of requirements, I was wondering how/if the infrastructure for this could be shared.  
Comment 50 Andrew Niefer CLA 2008-09-29 14:42:09 EDT
Yes, this code will be called for every bundle depending the nested jar.  So we will need to ensure we only extract it once.  I don't believe build has the same requirements as UI wrt to caching & timestamps.  

However, in the case of export, we would want UI to tell us where they have extracted jars if they have already done so.  (Likely via a property in the BundleDescription's user object).
Comment 51 Jeff McAffer CLA 2008-09-29 15:38:17 EDT
Where are you seeing the requirements differing? 

My thinking is that un the end there is a JAR on disk and we want to be able to access it exploded on the filesystem.  The exploded version is a "cache" of the original. If the original changes we flush the cache.  Seems like both the UI and build could use such a mechanism and share both the implementation and the data.

Of course we want to avoid over-engineering this but it feels subtle enough that implementing it once would be a win.
Comment 52 Andrew Niefer CLA 2008-09-29 15:57:26 EDT
They differ because in Build, the original does not change.  Compare it to fetch, we fetch once and never again make any attempt to verify that the original (ie CVS) still matches at some later point.

Any attempt to do such, is more of a continuous setup, the management of which is outside the scope of PDE/Build.

In UI they must keep the cache in synch with the target, which may change on user action or p2 update.
Comment 53 Mik Kersten CLA 2008-10-07 00:09:26 EDT
+1 for Jeff's very clever suggestion.  It's not perfect, but the may be the enemy of the good enough in this case.  If having it be fully automatic is not obvious enough, PDE could put up a dialog asking the user if the JARs should be expanded.
Comment 54 Daniel Krügler CLA 2008-10-07 01:46:56 EDT
(In reply to comment #52)
I agree 100% that there is no extra need for caching support due to possible invalidation of the exploded form during a headless build. There is nothing in the current build spec which implies that it needs to take care of arbitrary changes of the files from base location.

Comment 55 Mark Melvin CLA 2008-11-07 17:01:40 EST
I've just spent about an hour banging my head on this too.  I am converting our ginormous PDE build to an automated build system, building on demand at the plugin level so I am calling eclipse.buildScript, then using that script to build my plugins.  I am fetching all of my "prebuilt" binary dependencies and referencing them through the "pluginPath" attribute to eclipse.buildScript.  It looks like expanding them at build time will work, but is a little ugly to integrate.

The solution of converting each nested JAR to its own wrapper bundle sounds interesting and much cleaner, and the PDE wizard essentially inflates the class files into a directory structure inside the plugin - basically as Andrew suggests in comment #46.  Is this actually the recommended way of doing things, and what you are suggesting in comment #47, Jeff when you refer to best practices of packaging each JAR as a bundle?  What happens when one of these wrapped libraries changes?  I guess you inflate the new JAR, manually copy the folder structure over top of the old one, update the manifest to include all changed package names, and update build.properties to include the new list of sub-folders.  That or re-run the wizard and manually merge things.  Sounds like a maintenance nightmare for frequently changing wrapped libraries...is anyone doing this with good results?
Comment 56 Mark Melvin CLA 2008-11-07 17:45:28 EST
Actually, Andrew - I still can't get your suggestion to work.  I have essentially set up my plugin like so:

org.nested.foo.jar
         /com/mycompany/..../*.class
         /other/org/..../*.class
         /META-INF/MANIFEST.MF : Bundle-Classpath: ., other

And all of my plugins in Eclipse build fine, and I can build/export this plugin from the IDE.  But if I try to use it as a binary dependency in my PDE build, my build still fails to resolve everything in "other".  It finds everything in '.', but not the stuff in 'other'.
Comment 57 Andrew Niefer CLA 2008-11-07 18:13:18 EST
I don't think I really thought that suggestion through.  I believe it would work at runtime, but at compile time it suffers from a similar but different problem to nested jars.  We don't have a way to tell the java compiler that the other/ folder is a root for class files.  (Or if there is a way, we aren't using it).

So the compiler would end up interpreting it as packages like "other.org.eclipse..."
Comment 58 Mark Melvin CLA 2008-11-08 11:47:27 EST
Right.  Thanks, Andrew.  I didn't think the PDE build used Bundle-Classpath in any way but I wanted to be sure.  So I guess I could just:

a) not use sub-folders
b) expand on the fly

I'll look into them both on Monday, starting with 'a'.  I have a nice build system shaping up and I'd hate to start inserting special cases to inflate these JAR files.  Although I suppose I'd have to do it anyway for feature dependencies.  I think they need to be inflated dirs as opposed to update jars anyway...
Comment 59 Erkki Lindpere CLA 2008-11-22 17:35:45 EST
This bug is now also complicating plug-in development in Scala, since the Scala library bundle 2.7.2 ships as nested jars.
Comment 60 Jeff McAffer CLA 2008-11-24 22:21:20 EST
Mark, re comment 55.  The overall best practice is to package each JAR as a bundle.  There are exceptions of course but this works well, is natural, does not have a performance problem on startup and works for compiling.  For more information I suggest looking at the Orbit guidelines http://wiki.eclipse.org/orbit and Chapter 20 from the RCP book.  Jean-Michel and I donated that chapter to Orbit when the project was setup.  See http://www.eclipse.org/orbit/documents/RCP_Chapter20.pdf
Comment 61 Mark Melvin CLA 2009-01-19 12:14:33 EST
So I took the approach of inflating dependency JARs on plugins that declare they need certain dependencies inflated (as part of my uber continuous integration build).  Today I had a failure while configuring a new build and I noticed it was because I had inflated too many dependencies - in particular, I inflated a couple that did not have nested JARs.  This causes the PDE build to fail because it does not set up the classpath properly.  What you end up with is an entry like this:

  <pathelement path="path/to/plugins/my.plugin.id/@dot"/>

Now "@dot" is obviously undefined.  My dependency actually exists in inflated form at "path/to/plugins/my.plugin.id", and the .class files are all present in the appropriate folder structure.

Shouldn't the PDE build be smart enough to generate a classpath line without the offending "@dot" stuck on the end?  What does that even mean?  I think it only applies as a magical entry when you're dealing with JAR'd bundles, right?
Comment 62 Nick Edgar CLA 2009-10-07 20:02:50 EDT
Another interesting case that arises is for nested jars that are -not- on the plug-in's classpath, as recommended when using Eclipse's Ant support.  c.f. the Javadoc for AntRunner which states:
"...it is necessary to package
 * the classes in a jar that is not on the client plugin's classpath.
 * The jar must be added to the Ant classpath. One way to add 
 * the jar to the Ant classpath is to use the
 * <code>org.eclipse.ant.core.extraClasspathEntries</code> extension."

Is there any way in this setup to allow downstream plug-ins to compile against the nested Ant jar (whether or not the plug-in is unpacked)?
Comment 63 Darin Wright CLA 2009-10-07 21:55:11 EDT
(In reply to comment #62)
>> Is there any way in this setup to allow downstream plug-ins to compile against
> the nested Ant jar (whether or not the plug-in is unpacked)?

There is not a way to do this thay I am aware. Often nested Ant jars are an implementation detail of ant tasks that may or may not be API. The function is accessed via Ant build scripts, rather than Java APIs. Other nested jars (such as those created by java.launching and jdt.debug.ui) are also often intended for internal/private use. In JDT's case it's code for launching a VM for scrapbook evaluations and doing reflection of system properties to determine library level (execution environment).
Comment 64 Chris Aniszczyk CLA 2009-10-08 11:41:05 EDT
(In reply to comment #62)
> Another interesting case that arises is for nested jars that are -not- on the
> plug-in's classpath, as recommended when using Eclipse's Ant support.  c.f. the
> Javadoc for AntRunner which states:
> "...it is necessary to package
>  * the classes in a jar that is not on the client plugin's classpath.
>  * The jar must be added to the Ant classpath. One way to add 
>  * the jar to the Ant classpath is to use the
>  * <code>org.eclipse.ant.core.extraClasspathEntries</code> extension."
> 
> Is there any way in this setup to allow downstream plug-ins to compile against
> the nested Ant jar (whether or not the plug-in is unpacked)?

So I really think this could be solved in Ant. I had this problem with Mylyn Wikitext awhile ago...

The bug 278246 relates to Ant having an enhanced extraClasspathEntries...

See bug 273325 for what I did in Mylyn wikitext to get around this.

It essentially involved duplicating classes in the bundle jar and bundle's "ant-support" jar...
Comment 65 Darin Wright CLA 2009-10-08 17:09:54 EDT
(In reply to comment #62)
> Is there any way in this setup to allow downstream plug-ins to compile against
> the nested Ant jar (whether or not the plug-in is unpacked)?

I guess the question is why do you want to compile against the nested ant jar? OSGi can't see these jars at runtime (they are not on the bundle classpath), so how will your code see them at runtime?
Comment 66 Andrew Niefer CLA 2009-10-08 17:45:04 EDT
(In reply to comment #65)
> (In reply to comment #62)
> > Is there any way in this setup to allow downstream plug-ins to compile against
> > the nested Ant jar (whether or not the plug-in is unpacked)?
> 
> I guess the question is why do you want to compile against the nested ant jar?
> OSGi can't see these jars at runtime (they are not on the bundle classpath), so
> how will your code see them at runtime?

As an example, PDE/Build provides ant tasks that extend and reuse ant tasks provided by p2.  Similarly, p2 itself has ant tasks that extend ant tasks in other p2 bundles.

In PDE/Build we have in our build.properties
jars.extra.classpath = platform:/plugin/org.apache.ant/lib/ant.jar,\
                       platform:/plugin/org.eclipse.equinox.p2.repository.tools/lib/repository-tools-ant.jar
Comment 67 Nick Edgar CLA 2009-10-22 10:17:29 EDT
> I guess the question is why do you want to compile against the nested ant jar?
> OSGi can't see these jars at runtime (they are not on the bundle classpath), 
> so how will your code see them at runtime?

The Ant tasks are put in a nested jar to allow them to be added to the eclipse Ant classpath via extensions (as mentioned).  But during packaging of RTC, the nested jar is extracted to top level of the main plugin jar so that it can work with the plugin jar on a plain Java classpath (found via Ant's -lib arg).  So other jars on the classpath would be able to find the classes.  

However, your point is still valid in that if downstream plugins want to add their tasks to the Eclipse Ant classpath, they'll need to be able to see the referenced classes.  I tried adding the nested jar to the plugin classpath, but that caused all kinds of confusion.

I think I'll try moving the Ant tasks to their own plugin, with no nested jar, then for the Eclipse Ant classpath extension, use the platform: syntax.
Comment 68 Nick Edgar CLA 2009-10-22 10:38:34 EDT
Andrew, for the approach in comment 66, does platform: refer to the platform running PDE Build, or to the target it's compiling against?  It turns out that one of our downstream Ant task plug-ins does refer to the base Ant tasks in this way, but they're not being found.  In this case it's compiling against the binaries of the base plug-in, not compiling them all together at once.
Comment 69 Nikolas Falco CLA 2010-03-02 11:09:25 EST
Same problem.

I need to create a j2ee bundle foreach ejb, connector, jaxr javax interfaces.
Until i have these projects on workspace, compile and OSGi launch configuration work.
As install them on target platform, project that import package from platform of that interface, won't compile and in the plugin dependence's three show jars as source resources (lib folder as an empty java package and the jar childrens as zip resource).
Launching OSGi configuration can't resolve import packages.
Comment 70 Eric Jain CLA 2011-03-14 19:24:44 EDT
Just spent an hour or so getting the Apache HttpClient OSGi bundles to work in a project (using Eclipse 3.6.2), before I realized that nested jars were the culprit. Looks like the Maven Bundle Plugin (i.e. bnd) is used to generate the OSGi bundles, and it doesn't default to inlining dependencies.

If it's too difficult to handle nested jars, I wonder if Eclipse could at least issue a proper error or warning?
Comment 71 Andrew Niefer CLA 2011-03-28 17:53:07 EDT
Created attachment 192049 [details]
patch

Patch generates code to extract nested jars into ${buildDirectory}/nestedJars.  Needs some testing.
Comment 72 Andrew Niefer CLA 2011-03-29 16:17:15 EDT
Created attachment 192130 [details]
patch

I am releasing this patch
Comment 73 Andrew Niefer CLA 2011-03-29 16:17:53 EDT
.