Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Package uses conflict: Import-Package on startup of a bundle

Tags:

osgi

I am getting the following error when trying to install the htmlunit bundle:

com.springsource.com.gargoylesoftware.htmlunit_2.6.0 [370] could not be resolved.
  Reason: Package uses conflict: 
  Import-Package: org.apache.commons.logging.impl; version="1.1.1"

I have followed the diagnostic procedure for this type of problem on this blog.

And here are my findings: The bundle com.springsource.com.gargoylesoftware.htmlunit_2.6.0 has the following instructions:

Import-Package: \
  org.apache.commons.logging;version="[1.1.1, 2.0.0)",\
  org.apache.commons.logging.impl;version="[1.1.1, 2.0.0)"

The only bundle that has this in its use constraint in my OSGi is com.springsource.org.apache.commons.logging, which has these instructions:

Export-Package: \
  org.apache.commons.logging;version="1.1.1",\
  org.apache.commons.logging.impl;version="1.1.1";\
    uses:="javax.servlet,
           org.apache.avalon.framework.logger,
           org.apache.commons.logging,
           org.apache.log,
           org.apache.log4j"

Import-Package: \
  javax.servlet;version="[2.1.0, 3.0.0)";resolution:=optional,\
  org.apache.avalon.framework.logger;version="[4.1.3, 4.1.3]";resolution:=optional,\
  org.apache.log;version="[1.0.1, 1.0.1]";resolution:=optional,\
  org.apache.log4j;version="[1.2.15, 2.0.0)";resolution:=optional   

At this point I am stuck as I can't figure out what the problem is and how to resolve it although from what I provided above it should be clear, but not to me :(

Any ideas...?

like image 334
Joly Avatar asked Mar 31 '11 17:03

Joly


2 Answers

Detecting (Eclipse) OSGI error “uses-conflict” Solving a large bundle tree which has a conflict is a tedious job. The most easy error to solve is “Missing Constraint” . This is simply a missing bundle or class and the error explicitly says which java package/bundle is missing. What I want to focus on is the cryptic OSGI error message called “uses-conflict”. Equinox OSGI tools doesn’t provide much information about the package name/s that creates the version conflict. There are many papers out there explain how to solve the conflict , however, none of them show how to find the package that conflicts quickly. Here is a method to identify which package+version causes the conflict . We shall start with some imaginary project: A developer wrote an eclipse plugin + few bundles (JARs). The plugin contains mainly UI code and the JARs contain mainly shared/reusable logic. The developer packed them all into an Eclipse feature and tries to install it. Due the “uses-conflict” the plugin fails to start.

You got a plugin. You install but it doesn’t start due to a “uses-conflict” close eclipse. re-run eclipse via command line(linux):

   ./eclipse -console -consoleLog -data /tmp/eclipseTest/workspace/ -clean 

(win32 users should invoke eclipse.exe ofcourse) This command will bring on the osgi console which we need later. when eclipse load finish , switch back to the eclipse command line window. you should see there the “osgi>” command prompt (if not press Enter twice) find the Java packages of your plugin/bundles . lets assume it is named com.A , B, C , D and E run in the osgi console:

    osgi> ss com.A

    id    State       Bundle
    7    INSTALLED  com.A
    osgi> start 7

org.osgi.framework.BundleException: The bundle "com.A [7]" could not be resolved. Reason: Package uses conflict: Require-Bundle: com.B; bundle-version="0.0.0" at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolverError... more stack here...

Now we know that the bundle that com.A depends on bundle com.B and that com.B has a conflict. The reason is of course the nutritious uses conflict. (side comment: the actually conflict may be hidden even further: in some other bundle which com.B depends on but let us ignore this for now) The ideal state we wish to get to is :

   osgi> ss com.A
   Framework is launched.
   id    State       Bundle
   7    ACTIVE      com.A

but we are not there yet.

so we know bundle com.B is problematic. we can see the import list it tries to load (but fails) by running

   osgi> bundle 10   

(10 is the random bundle id our com.B bundle got from the OSGI container. simply run in osgi console: “ss com.B” to find its bundle id)

  • focus on the “Import-Package” section. copy it somewhere . When installing a plugin to eclipse framework it is actually unzipped into the eclipse’ plugin directory. Open a file explorer and go to /plugins . Find the directory of the “bad” bundle (the one that cases the “uses-conflict”) It will probably be named com.B.[version_or_timestamp].

  • drill down to the META-INF directory. edit the manifest.mf: start by cleaning the Import-packages section (cut-paste that part to a notepad)

  • restart the eclipse (Ctrl+C to exit running eclipse in osgi console then re-run) Now eclipse should say that bundle is “ACTIVE”. if it didn’t start in active mode ,it may be hitting a Missing-Constraint error . That is easy to solve since the error is pretty much informative , but that’s not the focus here.

  • Until the uses-conflict re-pop: start re-populating the Import-packages section in that manifest to its original state. Do it one java package at a time so you can pin-point which change cause the “uses-conflict”

  • when editing manifest.mf in notepad make sure you press Enter at column 71 and that next line starts with a single space. As you may have noticed , the manifest has that unique new-line structure you must follow.

  • restart eclipse (Ctrl+C in osgi console then re-run) .

  • Re-test the bundles state by running the osgi command “ss com.A” try to “start com.A” and see if you get any error.

  • restart eclipse after each manifest.mf change as stated above.

Once you add the bad java import to your manifest.mf you will will see the bundle is only marked as “RESOLVED” (rather than active). Lets assume the first java package you found to break the bundle is javax.xml.bind. run at the OSGI console : “packages javax.xml.bind” and you should see the list of resolved OSGI bundles+versions+usage for that namespace. if you got more than one provider (i.e. more than one version - its a tree. look at its roots) you might need to change your original bundle and force it to import a specific version (by using the “version” directive in the Import-Package section. for example javax.xml.bind;version="[2.1,3)" this is telling osgi to search for a range) In general if you write an OSGI bundle or an Eclipse plugin the best practice is to specify an Import version range explicitly when possible (and not use the default , which is 0.0.0 or any version ) in the Import-package headers. Same rule also apply to the uses:= directive which is used sometimes inside Import-Package header.

Same applies to Require-Bundle header: its best to use the “bundle-version” OSGI directive to make sure you get the version you expect . If Maven is used in the build process its even more important since it allows maven to correctly calculate the version dependencies (especially if using Felix maven-bundle-plugin)

Another issue is sometimes you do demand in your bundle’s manifest: Import-Package: javax.xml.bind;version=”[2.1.9,3)” but for some reason this can’t be satisfied. You may extend your version range to a wider one if possible.

When you resolved your OSGI conflict don’t forget to edit your original build manifests/scripts accordingly.

side comment: you may also try to run “eclipse.exe -clean” which may re-calculate the bundle dependencies correctly and will let your plugin start but this is not a long term solution.

like image 88
taitelman Avatar answered Nov 10 '22 02:11

taitelman


This most likely has to do with 'import-what-you-export'.

A uses constraint states "if you want to import this package, better make sure you're using the same packages that I am", which not only means the version of the package, but the identical package, exported by the same bundle. Your second bundle exports the logging and logging.impl package, stating that "if you want to use logging.impl, use the same logging that I do", and it also happens to export one. This means that anyone who wants to import logging.impl must also import logging from your bundle, since it cannot be wired to any other logging package. This leads to problems if there is another copy of logging around in the framework, which may get resolved earlier.

Probably the easiest way to solve this is to add logging to the Import-Package of your second bundle. This way, you leave the choice of exactly which package to use up to the framework.

On an unrelated note, get rid of all those ";resolution:=optional statements, unless you're code can really work with the situation in which some package is not available. I would be highly surprised of your bundle can work without having javax.servlet available.

like image 31
Angelo van der Sijpt Avatar answered Nov 10 '22 00:11

Angelo van der Sijpt