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

Support for both javax & jakarta versions of JAXB in the same [Java SE] classpath #263

Open
rbygrave opened this issue Aug 10, 2022 · 8 comments

Comments

@rbygrave
Copy link

rbygrave commented Aug 10, 2022

Edit: ... Currently we can't support both javax & jakarta versions of JAXB in the same classpath. This looks like a migration issue for library developers.

As a library developer with a test classpath/module-path dependency on JAXB I'm looking to bump that from javax.xml.bind to jakarta.xml.bind.

However, applications often at the moment still have some dependencies using javax.xml.bind - it's going to take time for all the dependencies to update to jakarta.xml.bind (and as a library author I have no control over what apps depend on and how quickly other libraries will update to jakarta jaxb).

As the maven groupId and artifactId are the same for both javax.xml.bind and jakarta.xml.bind, we can't support both the old and the new versions of JAXB in the classpath at the same time. I believe we are going to want to support both versions of JAXB during the transition phase as apps and libraries update to jakarta JAXB.

That is, we can't have the below, as the only difference is the version, the maven groupId and artifactId is the same.

   <!-- some things need javax.xml.bind -->
    <dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>2.3.3</version>  <!-- javax version -->
    </dependency>

   <!-- other things need jakarta.xml.bind -->
    <dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>4.0.0</version>  <!-- jakarta version -->
    </dependency>

This makes me think this is close to a catch 22 situation. Libraries can't update to jakarta.xml.bind [4.0.0] without breaking any apps that have a dependency on javax.xml.bind [2.3.3]. Apps can't update to jakarta.xml.bind until all their library dependencies have updated.

Upgrading to jakarta JAXB requires a "big bang" style upgrade where absolutely all JAXB use needs to go from javax to jakarta in that single upgrade as both jakarta and javax JAXB can't co-exist on the classpath. This is going to be tough for larger apps that have more and older dependencies.

Is there thought to releasing the new jakarta.xml.bind-api with a different maven artifactId in order to support both javax.xml.bind and jakarta.xml.bind in the classpath/module-path while libraries and apps go through a transition phase?

@rbygrave rbygrave changed the title Same maven groupId and artifactId for jakarta.xml.bind:jakarta.xml.bind-api ... means I can't have both Same (javax | jakarta) maven groupId and artifactId for jakarta.xml.bind-api ... means I can't have both (javax & jakarta) Aug 10, 2022
@rbygrave rbygrave changed the title Same (javax | jakarta) maven groupId and artifactId for jakarta.xml.bind-api ... means I can't have both (javax & jakarta) Support for both javax & jakarta versions of JAXB in the same classpath Aug 10, 2022
@lukasj
Copy link
Contributor

lukasj commented Aug 11, 2022

with jaxb, they can co-exist on the same classpath as long as they use jaxb-ri. Ie in Metro, the problem there was xmlsec dependent on javax with GlassFish having jakarta. The solution I used was to repackage javax versions in an extra artifact (see here). Another option I used in a different project is to use artifacts from org.glassfish.jaxb group id for the jakarta package namespace and artifacts from com.sun.xml.bind groupId for the javax package namespace - this works for the runtime, since packages were renamed in order to fix split package problem for JPMS. Should xjc (or tools in general) be required, one needs to use the latest one with -target 2.3 option for having javax in generated sources.

also note that tooling now supports usage of old namespace in schema binding files, so even though one should update them to jakarta xml ns, he is not required to do so. (see the ri release notes)

@rbygrave
Copy link
Author

rbygrave commented Aug 11, 2022

The solution I used was to repackage javax versions in an extra artifact

If I understand correctly, repackaging javax versions allowed those apps to have both in the classpath. That works but in my ideal world as a library I don't want to force people to change their existing dependencies to avoid this clash. Ideally my library could adopt jakarta JAXB and that wouldn't come with caveats like "you must also change this dependency / javax JAXB implementation ..." and if they didn't read that in release notes then the app breaks.

Forcing people to change their existing javax JAXB implementation and/or dependencies is a really big ask for a library.

use artifacts from org.glassfish.jaxb group id for the jakarta package namespace

I don't understand how that avoids the clash for jakarta.xml.bind:jakarta.xml.bind-api? Looking at the glassfish bom https://search.maven.org/artifact/org.glassfish.jaxb/jaxb-bom/4.0.0/pom ... it uses jakarta.xml.bind:jakarta.xml.bind-api so the maven groupId and artifactId still match the javax one. Did I miss something here? Is there a bundle of jakarta.xml.bind-api in maven central under a different maven groupId?

If there was a bundling of jakarta.xml.bind-api under a glassfish groupId that would work (or any other bundling of jakarta.xml.bind-api really just so that the groupId + artifactId were different). I could for example release my own bundle of jakarta.xml.bind-api just so that the maven groupId + artifactId were different but that would be a fairly terrible plan (I'd see it as a short term hack, it would work but isn't a great long term plan).

@lukasj
Copy link
Contributor

lukasj commented Aug 12, 2022

The solution I used was to repackage javax versions in an extra artifact

If I understand correctly, repackaging javax versions allowed those apps to have both in the classpath.

No. Take Metro as an end-user app built on top of JAXB with a dependency on Santuario which also uses JAXB, yet the former was updated to jakarta while the latter was not. GlassFish, like few other servers, is a consumer of Metro which does not (and should not) care about Metro-specific needs and dependencies. So in order to resolve that, Metro has both versions of JAXB on its classpath; packaging all Santuario's dependencies (including JAXB for javax package namespace) into an extra jar/artifact is just a minor detail which makes it easier for users to consume it ie in non-maven world, say ant (which is btw very often not being taken into account) and which also resolves OSGi problem with slf4j library required by Santuario.

That works but in my ideal world as a library I don't want to force people to change their existing dependencies to avoid this clash. Ideally my library could adopt jakarta JAXB and that wouldn't come with caveats like "you must also change this dependency / javax JAXB implementation ..." and if they didn't read that in release notes then the app breaks.

Forcing people to change their existing javax JAXB implementation and/or dependencies is a really big ask for a library.

JAXB as all other specs needs to live with the decisions made by the community now a days. Decision made was to invent some "transformer" which, when put on the classpath, does all the magic and in the end makes this transition easier. I personally have never believed the transformer approach is the way to go in pure Java SE world and I expressed that few times, yet I can agree it can help vendors at the platform level (app servers/EE managed environments). Unfortunately, SOAP stack as well as XML world as such is seen by many as a legacy technology not being used outside of the container these days, which EE platform vendors does not have to implement (as SOAP is optional), JAXB was part of Java SE for a long time, so the input from these ends is not being taken into account as seriously as it should.

It is also very important to note that I believe all jakarta apis are suffering from this, not just jaxb.

use artifacts from org.glassfish.jaxb group id for the jakarta package namespace

I don't understand how that avoids the clash for jakarta.xml.bind:jakarta.xml.bind-api? Looking at the glassfish bom https://search.maven.org/artifact/org.glassfish.jaxb/jaxb-bom/4.0.0/pom ... it uses jakarta.xml.bind:jakarta.xml.bind-api so the maven groupId and artifactId still match the javax one. Did I miss something here?

the bigger picture of what this bom says would be similar to the one here from around a decade ago - basically the bom exports two streams of artifacts: internal ones targeted for developers under the org.glassfish.jaxb groupid and public ones for end users/upstream under the com.sun.xml.bind groupid allowing both to coexist and/or be used in different versions to certain extent at the same time.

...so in the other project of mine, I used com.sun.xml.bind:jaxb-impl:2.3.6 and org.glassfish.jaxb:jaxb-runtime:4.0.0; if one does not care about JPMS, com.sun.xml.bind:jaxb-impl:4.0.0 and org.glassfish.jaxb:jaxb-runtime:2.3.6 can be used as well. It is not a bullet-proof solution as there are minor conflicts in a lower level dependencies but most users should not be affected by them (some of these deps differ in version number only and/or are not dealing with APIs). Just compare dependency trees of these two artifacts.

Is there a bundle of jakarta.xml.bind-api in maven central under a different maven groupId?

If there was a bundling of jakarta.xml.bind-api under a glassfish groupId that would work (or any other bundling of jakarta.xml.bind-api really just so that the groupId + artifactId were different). I could for example release my own bundle of jakarta.xml.bind-api just so that the maven groupId + artifactId were different but that would be a fairly terrible plan (I'd see it as a short term hack, it would work but isn't a great long term plan).

no, I'm not aware of such artifact. That does not necessarily mean there is none. IMHO that would be the easiest, yet also the worst solution to this problem. Another probably slightly better alternative would be to let jaxb-ri and/or moxy artifacts include the runtime for jaxb2 in their own artifacts (like what did the jaxb-ri to support JAXB1 and JAXB2 back in EE 5/6/7 days)

@lukasj
Copy link
Contributor

lukasj commented Aug 14, 2022

If there was a bundling of jakarta.xml.bind-api

no, I'm not aware of such artifact.

I mean apart from Java EE 8 versions of those artifacts obviously, which are supposed to be the same as Jakarta EE 8 ones. The mapping is shown at https://wiki.eclipse.org/Jakarta_EE_Maven_Coordinates, versions differ in the micro number (so for jaxb it is 2.3.2 vs 2.3.3)

@rbygrave
Copy link
Author

rbygrave commented Aug 15, 2022

Thanks Lukas for all the answers, I really appreciate it.

com.sun.xml.bind:jaxb-impl:4.0.0 and org.glassfish.jaxb:jaxb-runtime:2.3.6 can be used as well. Just compare dependency trees of these two artifacts.

I see the conflict on jakarta.xml.bind-api - (jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.0:compile - omitted for conflict with 2.3.3). ... it's this conflict that is providing the pain point. This conflict is what this ticket is about - that dealing with this conflict in Java SE is a problem.

[INFO] +- org.glassfish.jaxb:jaxb-runtime:jar:2.3.6:compile
[INFO] |  +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile
[INFO] |  +- org.glassfish.jaxb:txw2:jar:2.3.6:compile
[INFO] |  +- com.sun.istack:istack-commons-runtime:jar:3.0.12:compile
[INFO] |  \- com.sun.activation:jakarta.activation:jar:1.2.2:runtime
[INFO] \- com.sun.xml.bind:jaxb-impl:jar:4.0.0:compile
[INFO]    \- com.sun.xml.bind:jaxb-core:jar:4.0.0:compile
[INFO]       +- (jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.0:compile - omitted for conflict with 2.3.3)
[INFO]       \- org.eclipse.angus:angus-activation:jar:1.0.0:runtime
[INFO]          \- jakarta.activation:jakarta.activation-api:jar:2.1.0:runtime

IMHO that would be the easiest, yet also the worst solution to this problem.

I'm interested to understand why do we say this would be the worst solution? They are 2 incompatible API's/modules with the same maven groupId/artifactId (so for Java SE this is a major problem - EE providers can work around the problem but in Java SE we can't get around it without republishing the API artifact). I understand it would be bad for myself or every library to produce their own maven GAV artifact for jakarta.xml.bind-api but I'd be interested to know why this is bad from the perspective of the JAXB project / official supplier of the Jakarta JAXB API maven dependency? It surely wouldn't hurt any EE container runtime as they have full control over what dependencies they use and they can modify their maven artifactId just as easily as they can their maven version number?

That is, a different maven artifactId removes the problem for Java SE users but doesn't really make it harder for any EE runtime providers so I don't see why this is the "worst" solution?

decisions made by the community now a days. Decision made was to invent some "transformer"

Right, I get that. To state the obvious, that is going to hurt all the Java SE apps with third party libs depending on javax.xml.bind (Sardine, PdfBox etc).

if one does not care about JPMS

Just to say my library does care. In fact virtually all my open source libraries are now Java 11 with module-info and that was one of the things that made me look at this "upgrade" to jakaka jaxb.

jakarta apis are suffering from this, not just jaxb

Yes although I think with JAXB it is going to hurt a lot more relative to the likes of servlet & persistence as an application will typically have one core implementation of servlet or persistence where as with XML/JAXB there is going to be lots of Java SE third party libraries that use "XML binding". It's all those third parties in the classpath in Java SE that makes it desirable to support both versions in a Java SE classpath.

Anyway, as a Java SE library using JAXB I'm pretty confident that upgrading to Jakata JAXB is effectively a breaking change for Java SE applications unless someone releases the API with a different maven groupId and artifactId (which is what is required in order to support having both javax and jakarta versions of JAXB in the same Java SE classpath).

@rbygrave rbygrave changed the title Support for both javax & jakarta versions of JAXB in the same classpath Support for both javax & jakarta versions of JAXB in the same [Java SE] classpath Aug 15, 2022
@rbygrave
Copy link
Author

Just to add - Artifacts using JAXB API (5,251) ... according to https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api/usages

@lukasj
Copy link
Contributor

lukasj commented Aug 16, 2022

I'm interested to understand why do we say this would be the worst solution?

since:

   <!-- some things need javax.xml.bind -->
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.3.2</version>  <!-- javax version -->
    </dependency>

   <!-- runtime for javax -->
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>2.3.6</version>
    </dependency>

   <!-- other things need jakarta.xml.bind -->
    <dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>4.0.0</version>  <!-- jakarta version -->
    </dependency>

   <!-- runtime for jakarta -->
    <dependency>
      <groupId>org.glassfish.jaxb</groupId>
      <artifactId>jaxb-runtime</artifactId>
      <version>4.0.0</version>
    </dependency>

is valid configuration, what benefits would another deployment of jakarta.xml.bind-api:2.3.3 artifact to different gId/aId to allow the same bring (apart from probably different license)?
There is also ripple effect on dependencies (ie activation), runtimes used within jaxb impl (ie mail, validation) as well as other vendors - ie MOXy is another implementation of JAXB/XML Binding which does not support this scenario, one can use either 2.7.x (for javax) or 3+ (for jakarta) but not both at the same time. Bringing the same level of support to MOXy would mean bringing it to EclipseLink, which in turn means the need for having ie jpa-api, jca-api (and many others).

One could then also be asking: if jaxb/xml binding can do this, why json processing and/or binding can not? They are also usable in pure Java SE, so the consistency is another point (actually introduction of the parsson project allowed similar support for co-existence of javax/jakarta runtimes for jsonp but not for jsonb).

I do get your point, yet I think current artifacts and set up provide good enough while also (relatively!) cheap to maintain transition path from Java SE 8 up to the latest SE 18/19 - be it direct transition from the javax to jakarta or step by step through SE 11 where jaxb-api:2.3.2/jakarta.xml.bind-api:2.3.3 more or less correspond to the version removed from Java SE at that time (the tricky part in this step-by-step move can be figuring out the right combination of activation api/runtime but that should not matter as long as one does not require handling of specific mime-types (ie SOAP stuff, mail attachments)).

@rbygrave
Copy link
Author

rbygrave commented Aug 17, 2022

what benefits would another deployment of jakarta.xml.bind-api:2.3.3 artifact to different gId/aId to allow

Well I would say little benefit. To be clear, that is not what I am asking for.

What I'm asking for is a different gId/aId for the Jakarta JAXB API ... the jakarta.xml.bind-api:4.0.0 one is the problem for the library author that wants to upgrade to Jakarta JAXB (this is the potentially conflicting artifact that the library maintainer is choosing/controlling/introducing in an upgrade to Jakarta).

That is, as a library maintainer, the library has no knowledge nor control over an Applications use of Javax JAXB (the app classpath). If the library upgrades to Jakarta JAXB and hence now has a dependency on jakarta.xml.bind-api:4.0.0 then that can break apps (due to the potential gId/aId conflict). As a library maintainer, I don't want thousands of devs upgrading my library and then complaining that it has broken their app and having all that work and noise to go through dependency trees for apps to resolve the classpath issue (that the library introduced via the upgrade to Jakarta JAXB. The library introduced the conflict and so the library is going to get the blame for that). That is potentially a lot of pain for a library maintainer to take on.

good enough while also (relatively!) cheap to maintain transition path

For devs controlling the entire application classpath that is probably reasonable (as they can use the different gId/aId provided by javax.xml.bind:jaxb-api to resolve the conflict and have an understanding of what is going on as they initiated a change to Jakarta JAXB themselves). For a library maintainer that has no such control this to me sounds very painful and not cheap. Picture potentially thousands of unhappy devs who will just say "I upgraded the library you provide and it broke my app".

As I see it, javax.xml.bind:jaxb-api provides that different gId/aId for the case when the conflict means that javax.xml.bind is omitted from the classpath (due to gId/aId conflict), and as a library maintainer wanting to upgrade to jakarta jaxb I'd prefer instead for the different gId/aId on the Jakarta side as that is the dependency that the library is looking to choose and the library doesn't control anything on the Javax side at that point. A different gId/aId on the Jakarta side means that a library is not going to introduce that conflict.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants