Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade OWLAPI to version 4.5.28. #1201

Closed
wants to merge 1 commit into from
Closed

Conversation

gouttegd
Copy link
Collaborator

This commit bumps our OWLAPI dependency to version 4.5.28.

Because the OSGI distribution of the OWLAPI has a hard requirement for SLF4J-API >= 2.x, we need to also bump our Logback-classic dependency to version 1.3.x, because the way the SLF4J-API interacts with its backends has changed in version 2.x, and Logback-classic < 1.3 is not compatible with the new way.

We also need a new dependency on the Apache Aries "SPI Fly" component (org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle). It provides an implementation of the OSGi ServiceLoader Mediator service, which – when in an OSGi context — is required by SLF4J-API 2.x to detect and load its backend providers. Without such an implementation, SLF4J-API 2.x cannot be loaded in an OSGi application.

Alas, for some reason this is not enough, and in all my tests SLF4J has been constantly unable to find and use the Logback backend. This has two consequences:

First, it prevents Protégé from actually logging anything.

Second and more importantly, this results in an almost immediate crash in protege-editor-core’s LogManager class, because in that class we forcefully cast the org.slf4j.Logger object returned by SLF4J’s LoggerFactory into a backend-specific ch.qos.logback.classic.Logger object; such a cast can only be valid if the SLF4J-API has been initialised with the Logback backend. Since it has not (for reasons unknown), the factory returns a org.slf4f.helpers.NOPLogger object instead, and the cast therefore throws an exception.

The workaround implemented here is simply to protect the cast by checking the actual runtime type of the Logger returned by the LoggerFactory, and to create a dummy Logger if it is not the expected type (which, in my tests, is always the case). This at least allows Protégé to run and work as expected, but it does not change the fact that, because of the failed initialisation of SLF4J/Logback, Protégé WILL NOT LOG ANYTHING. :(

This commit bumps our OWLAPI dependency to version 4.5.28.

Because the OSGI distribution of the OWLAPI has a hard requirement for
SLF4J-API >= 2.x, we need to also bump our Logback-classic dependency to
version 1.3.x, because the way the SLF4J-API interacts with its backends
has changed in version 2.x, and Logback-classic < 1.3 is not compatible
with the new way.

We also need a new dependency on the Apache Aries "SPI Fly" component
(org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle). It
provides an implementation of the OSGi ServiceLoader Mediator service,
which -- when in an OSGi context -- is required by SLF4J-API 2.x to
detect and load its backend providers. Without such an implementation,
SLF4J-API 2.x _cannot_ be loaded in an OSGi application.

Alas, for some reason this is not enough, and in all my tests SLF4J has
been constantly unable to find and use the Logback backend. This has two
consequences:

First, it prevents Protégé from actually logging anything.

Second and more importantly, this results in an almost immediate crash
in protege-editor-core's LogManager class, because in that class we
forcefully cast the org.slf4j.Logger object returned by SLF4J's
LoggerFactory into a backend-specific ch.qos.logback.classic.Logger
object; such a cast can only be valid *if* the SLF4J-API has been
initialised with the Logback backend. Since it has not (for reasons
unknown), it returns a org.slf4f.helpers.NOPLogger object instead, and
the cast therefore throws an exception.

The workaround implemented here is simply to protect the cast by
checking the actual runtime type of the Logger returned by the
LoggerFactory, and to create a dummy Logger if it is not the expected
type (which, in my tests, is *always* the case). This at least allows
Protégé to run and work as expected, but it does not change the fact
that, because of the failed initialisation of SLF4J/Logback, Protégé
WILL NOT LOG ANYTHING. :(
@gouttegd gouttegd self-assigned this May 10, 2024
@gouttegd gouttegd added the dependencies Pull requests that update a dependency file label May 10, 2024
@gouttegd
Copy link
Collaborator Author

About the failure to initialise SLF4J backends

From the output we get when Protégé starts, we can see that the “SPI Fly” service loader is doing its job:

May 10, 2024 1:17:20 PM org.apache.aries.spifly.BaseActivator log
INFO: Registered provider ch.qos.logback.classic.spi.LogbackServiceProvider of service org.slf4j.spi.SLF4JServiceProvider in bundle ch.qos.logback.classic

It found the implementation of the SLF4JServiceProvider service in Logback-classic, and registered it as such.

But then immediately after, when the SLF4J-API is trying to use the service, it fails:

SLF4J(E): A service provider failed to instantiate:
org.slf4j.spi.SLF4JServiceProvider: ch.qos.logback.classic.spi.LogbackServiceProvider not a subtype

As a result, the SLF4J-API remains without any backend providers, hence the complete absence of logging (and the crash in LogManager, if it wasn’t for the workaround I implemented in the PR):

SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.

At this point, I have no idea what is happening here. From my understanding, I believe it should work, and I don’t know why it doesn’t. Whether I did something wrong, whether it’s a bug in Protégé’s OSGi launcher, a bug in SPI Fly, a bug in SLF4J or in Logback… I have no idea.

If anyone knowledgeable in OSGi (especially the way SLF4J works in an OSGi context) wants to have a closer look… be my guest.

@gouttegd
Copy link
Collaborator Author

gouttegd commented May 10, 2024

Other consequences of the hard requirement for SLF4F-API 2.x

Beyond the logging problem outlined above (which, hopefully, could be fixed at some point), the fact the OSGI distribution of the OWLAPI is forcefully requiring the use of SLF4J-API >= 2.x has cascading consequences for some of our plugins. Several of them are also using the SLF4J-API, and some have a similar hard requirement for SLF4J-API < 2.0.

That means that, if we do go ahead with this PR and switch to SLF4J 2.x (and even if we somehow fix the no-logging issue later on), we’ll have a breaking point for plugins: plugins that depend on SLF4J 1.x will no longer work with newer versions of Protégé; conversely, any putative new plugin that would depend on SLF4J 2.x will not work with Protégé up to 5.6.3.

Now, such situations are not uncommon and are actually to be expected for any program that uses plugins: sometimes, you need to change something that breaks plugins.

But here, having such a breakage caused by something as trivial and as commonly used as a logging library seems utterly ridiculous.

Proposed solution

I believe there is no reason for the OSGI distribution of the OWLAPI (or any other library using SLF4J-API for that matter – including our plugins) to have a hard requirement for SLF4J-API >= 2.x.

What changed in the 2.x branch of the SLF4J-API is the way it interacts with its backend providers (such as Logback-classic). That’s what the 2.x branch is incompatible with Logback-classic < 1.3. But the client-side of the SLF4J-API 2.x is still strictly backwards-compatible with the older versions.

Just because the OWLAPI 4.5.28 is built against SLF4J-API 2.0.11 does not mean it can only be used with SLF4J-API >= 2.x. Outside of any OSGi context, it would work perfectly fine with SLF4J 1.x. It does in ROBOT, for example.

In an OSGI context, the hard requirement is solely the consequence of the following instruction in the manifest of the owlapi-osgidistribution archive:

Import-Package: […],org.slf4j;version="[2.0,3)",org.slf4j.spi;version="[2.0,3)",[…]

which basically tells the OSGi loader, “give me SLF4J 2.x or I will flatly refuse to load”, regardless of the fact that SLF4J 1.x would actually do the job just as fine.

Such a hard requirement is, most likely, not even an explicit choice of the part of the OWLAPI developers. It’s just the default behaviour of Apache Felix’s maven-bundle-plugin (used to create the OSGi distribution of the OWLAPI): when a project declares a Maven dependency to a foo-X.Y library, the plugin automatically translates that dependency into a hard requirement for foo [X,X+1) (meaning, >= X and < X + 1).

For many libraries, this is actually a sensible behaviour, because it is reasonable to expect that when a library bumps its major version number, your code may no longer be compatible with the new version. But I think this is not warranted for SLF4J-API, which has a commitment to backwards-compatibility even across major versions.

Long story short, I believe the OSGi distribution of the OWLAPI should drop its hard requirement for SLF4J-API 2.x. The entire project can still use SLF4J-API 2.x (that is, keep a dependency to 2.0.11 in the top-level POM), but the manifest for the OSGi distribution should not require version 2 at a minimum. This would allow Protégé to keep relying on Logback-classic < 1.3 (at least until we can figure out how to make SLF4J 2 and Logback-classic 1.3 work correctly in a OSGi context) and avoid any forced breakage with plugins that use SLF4J 1.x.

@ykazakov
Copy link
Contributor

@gouttegd Did you report this issue at owlapi? I too find it unusual that a 0.0.1 release increment changes the major version of the slf4j dependency.

These version changes are supposed to be for bug fixes, not changes in the API.

For reference, this is the commit that introduced these changes:

owlcs/owlapi@cd03d3f

@gouttegd
Copy link
Collaborator Author

Not yet, I am going to do that this weekend. That’s why this PR is a draft for now – if the OWLAPI developers accept to drop the hard requirement for SLF4J-API >= 2, then I’ll close this PR and replace by a simpler one that really just update the OWLAPI.

@gouttegd
Copy link
Collaborator Author

A 4.5.29 version of the OWLAPI with a relaxed dependency on SLF4J has been released, so we no longer need this PR. I’m updating to the newest OWLAPI as part of the PR to prepare the 5.6.4 release (#1203).

@gouttegd gouttegd closed this May 13, 2024
@gouttegd gouttegd deleted the test-update-to-owlapi-4528 branch May 13, 2024 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants