[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[equinox-dev] Question about uses constraints in a constellation involving fragments

Hi,

I've come across a weird constellation involving uses constraints and
fragments and need an OSGi expert to double-check my logic. I hope this
is the right mailing list. If not, please accept my apologies.

I've just had fun making sense of the following bug report [1] (which
exhibits similar symptoms as these other reports [2,3,4]).

>     java.lang.IncompatibleClassChangeError: Class ch.qos.logback.classic.LoggerContext does not implement the requested interface org.slf4j.ILoggerFactory
>     at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:270)
>     at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:281)

The situation in which this occurs seems to be like this:

> osgi> ss slf4j
> "Framework is launched."
> 
> id    State       Bundle
> 5    RESOLVED    ch.qos.logback.slf4j_1.0.7.v20121108-1250
>                 Master=836
> 836    RESOLVED    org.slf4j.api_1.7.2.v20121108-1250
>                 Fragments=5
> 908    RESOLVED    jcl.over.slf4j_1.7.2
> 924    ACTIVE      org.sonar.ide.eclipse.slf4j.pde_3.4.0.20140404-0949-RELEASE
> 927    RESOLVED    slf4j.api_1.7.2

So, we have two SLF4J API bundles (836, 927). The former comes from
Orbit, the latter is shipped with the SonarQube Eclipse plugin. The
plugin from Orbit uses a neat implementation trick where a fragment (5)
is used to give the org.slf4j.api bundle (836) access to the package
org.slf4j.impl and thereby make it use Logback as an API implementation.

osgi> h 5
Bundle headers:
 Bundle-Localization = fragment
 Bundle-ManifestVersion = 2
 Bundle-Name = Logback Native SLF4J Logger Module
 Bundle-RequiredExecutionEnvironment = J2SE-1.5,JavaSE-1.6,JavaSE-1.7
 Bundle-SymbolicName = ch.qos.logback.slf4j
 Bundle-Vendor = Eclipse.org
 Bundle-Version = 1.0.7.v20121108-1250
 Eclipse-SourceReferences =
scm:cvs:pserver:dev.eclipse.org:/cvsroot/tools:org.eclipse.orbit/ch.qos.logback.slf4j;tag=v20121108-1250
 Export-Package = org.slf4j.impl;
uses:="org.slf4j.spi,ch.qos.logback.classic.util,org.slf4j.helpers,ch.qos.logback.classic,ch.qos.logback.core,ch.qos.logback.classic.selector,ch.qos.logback.core.joran.spi,org.slf4j,ch.qos.logback.core.util";version="1.7.2"
 Fragment-Host = org.slf4j.api;bundle-version="[1.7.2,1.7.3)"
 Manifest-Version = 1.0
 Require-Bundle =
ch.qos.logback.core;bundle-version="[1.0.7,1.0.8)",ch.qos.logback.classic;bundle-version="[1.0.7,1.0.8)"

Now, my explanation for the IncompatibleClassChangeError is that whoever
calls org.slf4j.LoggerFactory.getLogger(...) is for some reason wired to
the slf4j.api (927) shipped with SonarQube. Now, that bundle's manifest
looks like this:

> osgi> h 927
> Bundle headers:
>  Archiver-Version = Plexus Archiver
>  Build-Jdk = 1.6.0_23
>  Built-By = ceki
>  Bundle-Description = The slf4j API
>  Bundle-ManifestVersion = 2
>  Bundle-Name = slf4j-api
>  Bundle-RequiredExecutionEnvironment = J2SE-1.3
>  Bundle-SymbolicName = slf4j.api
>  Bundle-Vendor = SLF4J.ORG
>  Bundle-Version = 1.7.2
>  Created-By = Apache Maven
>  Export-Package = org.slf4j;version=1.7.2, org.slf4j.spi;version=1.7.2, org.slf4j.helpers;version=1.7.2
>  Implementation-Title = slf4j-api
>  Implementation-Version = 1.7.2
>  Import-Package = org.slf4j.impl;version=1.6.0
>  Manifest-Version = 1.0

And here's where I think things can go wrong. Instead of getting wired
to the org.slf4j.impl package from org.sonar.ide.eclipse.slf4j.pde
(924), the slf4j.api bundle (927) gets wired to the org.slf4j.api bundle
(836) + ch.qos.logback.slf4j fragment (5) from Orbit. And the
ch.qos.logback.classic.LoggerContext from the fragment of course
implements the interface org.slf4j.ILoggerFactory exported from its host
bundle, not from the conflicting slf4j.api bundle (927). Hence the
IncompatibleClassChangeError.

So far for my theory. There is one thing, however, that puzzles me. The
fragment dutifully exports org.slf4j.impl with a uses constraint:

> org.slf4j.impl; uses:="...,org.slf4j,...";version="1.7.2"

Shouldn't this constraint prevent the slf4j.api bundle (927) from wiring
to the org.slf4j.api bundle (836) + fragment in the first place? Or does
the fact that the slf4j.api bundle (927) itself *defines* the org.slf4j
package (rather importing another, conflicting instance) does not
constitute a *use* that would violate the constraint and hence prevent
the two bundles from being wired together?

Can someone more knowledgeable of OSGi please explain to me what's going
on? Is this a constellation where uses constraints just don't help? Or
is my theory simply wrong and there exists another possible wiring that
explains the IncompatibleClassChangeError?

Any help is greatly appreciated.

Best wishes,

Andreas

[1] <https://bugs.eclipse.org/bugs/show_bug.cgi?id=446167>
[2]
<http://sonarqube.15.x6.nabble.com/SonarQube-Eclipse-3-4-0-not-compatible-with-Eclipse-Luna-4-4-0-td5026057.html>
[3] <https://bugs.eclipse.org/bugs/show_bug.cgi?id=438161>
[4] <https://issuetracker.springsource.com/browse/STS-1620>

-- 
Codetrails GmbH
The knowledge transfer company

Robert-Bosch-Str. 7, 64293 Darmstadt
Phone: +49-6151-276-7092
Mobile: +49-170-811-3791
http://www.codetrails.com/

Managing Director: Dr. Marcel Bruch
Handelsregister: Darmstadt HRB 91940