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

Flexible transitive dependencies JavaEE or JakartaEE 8 (while binary compatible) "Two poms one jar" #134

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

hutchig
Copy link
Member

@hutchig hutchig commented Oct 14, 2019

This pull request illustrates how a pom can support the
'two poms one jar' idea i.e. Jakarta/Java EE can be easily toggled.

See the Oct 8th Architecture call minutes action item.

This idea is not technically necessary, and should not be merged, if at all,
until MicroProfile is based on Jakarta officially. It serves only to help
demonstrate that Jakarta EE8 is a no-change-required switch by showing
easy switching with absolutely no changes to the jar used.

This idea is that, as a jar built with Java EE 8 or Jakarta EE 8 dependencies
is an identical binary (as all package and class names are identical - the maven
group and artifact ID is purely a build concept and does not leek into the jar file
(at least for Java/Jakarta EE 8), then one can use the same JAR for building
against regardless of if one wishes to get EE8 dependencies (and their transitive
dependencies) from a Java EE 8 or Jakarta EE 8 based project.

We can use dependency management in the top level MicroProfile project
to control this for all MicroProfile projects centrally.

A user can control the transitive dependancies being JakartaEE or JavaEE
by setting or not setting the "jakarta" SystemProperty ("mvn -Djakarta clean package").

try
"rm -rf ~/.m2/repository/jakarta/ ~/.m2/repository/javax && mvn -Djakarta clean package"
or
"rm -rf ~/.m2/repository/jakarta/ ~/.m2/repository/javax && mvn clean package"
to see what it does.

Profiles are multi-dimensional so it is possible to have {release, jakarta} as both
active etc.

Of course, the user is not rebuilding our jar nor the individual spec's jars and to some
extent, that is the point.

@kenfinnigan
Copy link

Does this mean a user project would need to inherit from this pom?

How does the profile become accessible to a user application?

@hutchig
Copy link
Member Author

hutchig commented Oct 14, 2019

@kenfinnigan my intention was that:
1 - a user would include an individual api-jar as a dependency in their build
2 - mvn would pull the api-jar from maven central and its pom
3 - mvn would consult the api-jar's pom.xml to resolve transitive dependencies
4 - the api-jar's pom would delegate to its parent pom (this one) for dependencyManagement
5 - the parent pom (this one) would have the appropriate profile activated
6 - the users choice of transitive dependencies for the MP api-jar (i.e. Java EE 8 or Jakarta EE 8) would be pulled in

Of course that is build time, run time (for example in Open Liberty) may bundle an api-jar and replace its pom.

I do not believe the users's project pom.xml would have to inherit from this one.

@hutchig
Copy link
Member Author

hutchig commented Oct 14, 2019

By the way @kenfinnigan , I originally had all the jakarta-*-versions defined to be the equivalent ${javax-*-version} values but having something that resolves to:

<jakarta-cdi-version>2.0</jakarta-cdi-version>

did not work here as the 'earliest' jakarta CDI release is 2.0.1 whereas the javax
equivalent is currently 2.0.

@hutchig hutchig changed the title Two poms one jar Flexible transitive dependencies JavaEE or JakartaEE 8 (while binary compatible) "Two poms one jar" Oct 15, 2019
@hutchig
Copy link
Member Author

hutchig commented Oct 15, 2019

I actually get a much cleaner

mvn  dependency:tree
mvn -Djakarta dependency:tree

If I put the 'dependencyManagement' stanza nested within the javax/jakarta profiles too.

So I will drop a commit that does that...as I have defined the javax and jakarta profiles
as being active on 'jakarta' being defined or not - by definition at exactly one of them will
be active for any build so the dependencyManagement will still be equally active for child
projects. (One can break this of course by not setting '-Djakarta' - which will turn the javax profile
on, and then adding '-Pjakarta' which will turn the jakarta profile on too! I have not thought up
a neat way to fix that.)

$ mvn  -Djakarta clean  dependency:tree 
...
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ microprofile-spec ---
[INFO] org.eclipse.microprofile:microprofile-spec:pom:3.2-SNAPSHOT
[INFO] +- org.eclipse.microprofile.config:microprofile-config-api:jar:1.3:compile
[INFO] |  \- org.osgi:org.osgi.annotation.versioning:jar:1.0.0:compile
[INFO] +- org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-api:jar:2.0:compile
[INFO] +- org.eclipse.microprofile.health:microprofile-health-api:jar:2.1:compile
[INFO] |  \- javax.inject:javax.inject:jar:1:compile
[INFO] +- org.eclipse.microprofile.metrics:microprofile-metrics-api:jar:2.1.0:compile
[INFO] +- org.eclipse.microprofile.jwt:microprofile-jwt-auth-api:jar:1.1:compile
[INFO] +- org.eclipse.microprofile.openapi:microprofile-openapi-api:jar:1.1:compile
[INFO] +- org.eclipse.microprofile.rest.client:microprofile-rest-client-api:jar:1.3:compile
[INFO] +- org.eclipse.microprofile.opentracing:microprofile-opentracing-api:jar:1.3:compile
[INFO] +- jakarta.enterprise:jakarta.enterprise.cdi-api:jar:2.0.1:compile
[INFO] |  +- jakarta.el:jakarta.el-api:jar:3.0.2:compile
[INFO] |  \- jakarta.interceptor:jakarta.interceptor-api:jar:1.2.3:compile
[INFO] |     \- jakarta.ejb:jakarta.ejb-api:jar:3.2.3:compile
[INFO] |        \- jakarta.transaction:jakarta.transaction-api:jar:1.3.1:compile
[INFO] +- jakarta.ws.rs:jakarta.ws.rs-api:jar:2.1.6:compile
[INFO] +- jakarta.json.bind:jakarta.json.bind-api:jar:1.0.2:compile
[INFO] +- jakarta.json:jakarta.json-api:jar:1.1.6:compile
[INFO] \- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for MicroProfile 3.2-SNAPSHOT:
[INFO] 
[INFO] MicroProfile ....................................... SUCCESS [  1.139 s]
[INFO] MicroProfile Specification ......................... SUCCESS [  0.048 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.636 s
[INFO] Finished at: 2019-10-15T18:52:57+01:00
[INFO] ------------------------------------------------------------------------
~/git/microprofile
$ mvn  clean  dependency:tree 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ microprofile-spec ---
[INFO] org.eclipse.microprofile:microprofile-spec:pom:3.2-SNAPSHOT
[INFO] +- org.eclipse.microprofile.config:microprofile-config-api:jar:1.3:compile
[INFO] |  \- org.osgi:org.osgi.annotation.versioning:jar:1.0.0:compile
[INFO] +- org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-api:jar:2.0:compile
[INFO] +- org.eclipse.microprofile.health:microprofile-health-api:jar:2.1:compile
[INFO] |  \- javax.inject:javax.inject:jar:1:compile
[INFO] +- org.eclipse.microprofile.metrics:microprofile-metrics-api:jar:2.1.0:compile
[INFO] +- org.eclipse.microprofile.jwt:microprofile-jwt-auth-api:jar:1.1:compile
[INFO] +- org.eclipse.microprofile.openapi:microprofile-openapi-api:jar:1.1:compile
[INFO] +- org.eclipse.microprofile.rest.client:microprofile-rest-client-api:jar:1.3:compile
[INFO] +- org.eclipse.microprofile.opentracing:microprofile-opentracing-api:jar:1.3:compile
[INFO] +- javax.enterprise:cdi-api:jar:2.0:compile
[INFO] |  +- javax.el:javax.el-api:jar:3.0.0:compile
[INFO] |  \- javax.interceptor:javax.interceptor-api:jar:1.2:compile
[INFO] +- javax.ws.rs:javax.ws.rs-api:jar:2.1:compile
[INFO] +- javax.json.bind:javax.json.bind-api:jar:1.0:compile
[INFO] +- javax.json:javax.json-api:jar:1.1:compile
[INFO] \- javax.annotation:javax.annotation-api:jar:1.3:compile
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for MicroProfile 3.2-SNAPSHOT:
[INFO] 
[INFO] MicroProfile ....................................... SUCCESS [  0.778 s]
[INFO] MicroProfile Specification ......................... SUCCESS [  0.033 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.233 s
[INFO] Finished at: 2019-10-15T18:55:25+01:00
[INFO] ------------------------------------------------------------------------
~/git/microprofile

Spot also above how MicroProfile Health is still pulling in javax version of CDI under the Jakarta profile...I will investigate...

@hutchig
Copy link
Member Author

hutchig commented Oct 15, 2019

https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
"Although transitive dependencies can implicitly include desired dependencies, it is a good practice to explicitly specify the dependencies you are directly using in your own source code. "

@hutchig
Copy link
Member Author

hutchig commented Oct 15, 2019

TODO: Gordon to investigate if profiles are active in pom's which are not parent poms when a jar is used as a dependency - i.e. can a profile be used to control a 'transitive' dependency being present or not.

@hutchig
Copy link
Member Author

hutchig commented Oct 16, 2019

To answer a question from the MP Community hang out I did a
mvn clean install with the PR's pom.xm to get

Installing /Users/hutchig/git/microprofile/spec/pom.xml to /Users/hutchig/.m2/repository/org/eclipse/microprofile/microprofile-spec/3.2-SNAPSHOT/microprofile-spec-3.2-SNAPSHOT.pom

Then can show that an application can effect the transitive dependancies with the system
property even after the build:

~/git/mpUser
$ cat pom.xml 
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>my-groupId</groupId>
  <artifactId>my-artifactId</artifactId>
  <version>my-version</version>
  <dependencies>
    <dependency>
      <groupId>org.eclipse.microprofile</groupId>
      <artifactId>microprofile-spec</artifactId>
        <version>3.2-SNAPSHOT</version>
        <type>pom</type>
        <scope>provided</scope>
    </dependency>
  </dependencies>
</project>
~/git/mpUser
$ mvn  dependency:tree 
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< my-groupId:my-artifactId >----------------------
[INFO] Building my-artifactId my-version
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ my-artifactId ---
[INFO] my-groupId:my-artifactId:jar:my-version
[INFO] \- org.eclipse.microprofile:microprofile-spec:pom:3.2-SNAPSHOT:provided
[INFO]    +- org.eclipse.microprofile.config:microprofile-config-api:jar:1.3:provided
[INFO]    |  \- org.osgi:org.osgi.annotation.versioning:jar:1.0.0:provided
[INFO]    +- org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-api:jar:2.0:provided
[INFO]    +- org.eclipse.microprofile.health:microprofile-health-api:jar:2.1:provided
[INFO]    |  \- javax.inject:javax.inject:jar:1:provided
[INFO]    +- org.eclipse.microprofile.metrics:microprofile-metrics-api:jar:2.1.0:provided
[INFO]    +- org.eclipse.microprofile.jwt:microprofile-jwt-auth-api:jar:1.1:provided
[INFO]    +- org.eclipse.microprofile.openapi:microprofile-openapi-api:jar:1.1:provided
[INFO]    +- org.eclipse.microprofile.rest.client:microprofile-rest-client-api:jar:1.3:provided
[INFO]    +- org.eclipse.microprofile.opentracing:microprofile-opentracing-api:jar:1.3:provided
[INFO]    +- javax.enterprise:cdi-api:jar:2.0:provided
[INFO]    |  +- javax.el:javax.el-api:jar:3.0.0:provided
[INFO]    |  \- javax.interceptor:javax.interceptor-api:jar:1.2:provided
[INFO]    +- javax.ws.rs:javax.ws.rs-api:jar:2.1:provided
[INFO]    +- javax.json.bind:javax.json.bind-api:jar:1.0:provided
[INFO]    +- javax.json:javax.json-api:jar:1.1:provided
[INFO]    \- javax.annotation:javax.annotation-api:jar:1.3:provided
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.208 s
[INFO] Finished at: 2019-10-16T09:40:59+01:00
[INFO] ------------------------------------------------------------------------
~/git/mpUser
$ mvn -Djakarta dependency:tree 
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< my-groupId:my-artifactId >----------------------
[INFO] Building my-artifactId my-version
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ my-artifactId ---
[INFO] my-groupId:my-artifactId:jar:my-version
[INFO] \- org.eclipse.microprofile:microprofile-spec:pom:3.2-SNAPSHOT:provided
[INFO]    +- org.eclipse.microprofile.config:microprofile-config-api:jar:1.3:provided
[INFO]    |  \- org.osgi:org.osgi.annotation.versioning:jar:1.0.0:provided
[INFO]    +- org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-api:jar:2.0:provided
[INFO]    +- org.eclipse.microprofile.health:microprofile-health-api:jar:2.1:provided
[INFO]    |  \- javax.inject:javax.inject:jar:1:provided
[INFO]    +- org.eclipse.microprofile.metrics:microprofile-metrics-api:jar:2.1.0:provided
[INFO]    +- org.eclipse.microprofile.jwt:microprofile-jwt-auth-api:jar:1.1:provided
[INFO]    +- org.eclipse.microprofile.openapi:microprofile-openapi-api:jar:1.1:provided
[INFO]    +- org.eclipse.microprofile.rest.client:microprofile-rest-client-api:jar:1.3:provided
[INFO]    +- org.eclipse.microprofile.opentracing:microprofile-opentracing-api:jar:1.3:provided
[INFO]    +- jakarta.enterprise:jakarta.enterprise.cdi-api:jar:2.0.1:provided (version selected from constraint [2.0,2.1))
[INFO]    |  +- jakarta.el:jakarta.el-api:jar:3.0.2:provided
[INFO]    |  \- jakarta.interceptor:jakarta.interceptor-api:jar:1.2.3:provided
[INFO]    |     \- jakarta.ejb:jakarta.ejb-api:jar:3.2.3:provided
[INFO]    |        \- jakarta.transaction:jakarta.transaction-api:jar:1.3.1:provided
[INFO]    +- jakarta.ws.rs:jakarta.ws.rs-api:jar:2.1.6:provided (version selected from constraint [2.1,2.2))
[INFO]    +- jakarta.json.bind:jakarta.json.bind-api:jar:1.0.2:provided (version selected from constraint [1.0,1.1))
[INFO]    +- jakarta.json:jakarta.json-api:jar:1.1.6:provided (version selected from constraint [1.1,1.2))
[INFO]    \- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:provided (version selected from constraint [1.3,1.4))
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.212 s
[INFO] Finished at: 2019-10-16T09:41:04+01:00
[INFO] ------------------------------------------------------------------------
~/git/mpUser

Note that mvn -Djakarta help:active-profiles still only shows the profiles active for the immediate project even though we can see an effect on transitive dependencies from
different profile activations in the dependancy's poms, interesting.

@hutchig
Copy link
Member Author

hutchig commented Oct 16, 2019

A further test would be for the mpUser project to just refer to a specific
mp spec jar as a dependency where that specific mp spec project was a maven child
project of the main pom here.

@hutchig
Copy link
Member Author

hutchig commented Oct 16, 2019

I can confirm that it works @kenfinnigan :

1 - change a microprofile spec pom.xml to a have the parent and delete
one of the dependencies in order to pick it up from the parent:

I chose RM as that is one I had to hand...

~/git/microprofile-reactive-messaging
$ git diff
diff --git a/pom.xml b/pom.xml
index 0effc66..4162415 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,6 +23,13 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
+ <parent>
+    <groupId>org.eclipse.microprofile</groupId>
+    <artifactId>microprofile-spec</artifactId>
+    <version>3.2-SNAPSHOT</version>
+  </parent>
+
+
     <groupId>org.eclipse.microprofile.reactive.messaging</groupId>
     <artifactId>microprofile-reactive-messaging-parent</artifactId>
     <version>1.1-SNAPSHOT</version>
@@ -154,12 +161,6 @@
                     </exclusion>
                 </exclusions>
             </dependency>
-            <dependency>
-                <groupId>javax.annotation</groupId>
-                <artifactId>javax.annotation-api</artifactId>
-                <version>1.3.2</version>
-                <scope>provided</scope>
-            </dependency>
             <dependency>
                 <groupId>org.testng</groupId>
                 <artifactId>testng</artifactId>

Have a user project that depends on the api:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>my-groupId</groupId>
  <artifactId>my-artifactId</artifactId>
  <version>my-version</version>
  <dependencies>
    <dependency>
      <groupId>org.eclipse.microprofile.reactive.messaging</groupId>
      <artifactId>microprofile-reactive-messaging-api</artifactId>
        <version>1.1-SNAPSHOT</version>
    </dependency>
  </dependencies>
</project>

Test if the -Djakarta flag on the user's project build has an effect on which transitive
dependencies are pulled in:

~/git/mpUser
$ mvn dependency:tree | grep annot
[INFO]    |  \- org.osgi:org.osgi.annotation.versioning:jar:1.0.0:compile
[INFO]    \- javax.annotation:javax.annotation-api:jar:1.3:compile
~/git/mpUser
$ mvn -Djakarta dependency:tree | grep annot
[INFO]    |  \- org.osgi:org.osgi.annotation.versioning:jar:1.0.0:compile
[INFO]    \- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile (version selected from constraint [1.3,1.4))

So yes - the "two poms one jar" idea is implementable as long as the
jakarta/javax APIs have not diverged.

So as this is mostly a demonstration to show how compatible Jakarta and Java EE 8
are and how easy it is to switch, whether to do it or not I guess comes down to
our community and motivation etc. @kwsutter ?

@kenfinnigan
Copy link

If I understand correctly, the BOM needs to be imported in the <dependencies> section of a user project and not <dependencyManagement>. Is that correct?

@hutchig
Copy link
Member Author

hutchig commented Oct 16, 2019

@kenfinnigan You can see in this example that the bom or parent project is not referenced directly in the user project at all but only transitively via the child MP api spec jar's pom.

The minimal set of information for matching a dependency reference against a dependencyManagement section is actually {groupId, artifactId, type, classifier}.
(type and classifier have defaults) so it is not possible to be flexible on the groupid and
artifactid if the transitive dependency is referenced directly in the user project
but managed in dependencyManagement of a parent (in this
example I deleted references to the transitive dependency in the MP child project and did
not have it in the user project at all).

I think the sweet spot is to have a user project just reference a MP api jar <dependency>
as they would do now and the scheme 'just works' (as here) because of the MicroProfile pom.xmls rather than getting the user project to look different in any way.

Of course some MP platforms rebundle anyway and then user will tend to have something like
<scope>provided</scope> so this is mostly a build/test time trick.

@kenfinnigan
Copy link

I thought it was, as the user app example earlier has:

  <dependencies>
    <dependency>
      <groupId>org.eclipse.microprofile</groupId>
      <artifactId>microprofile-spec</artifactId>
        <version>3.2-SNAPSHOT</version>
        <type>pom</type>
        <scope>provided</scope>
    </dependency>
  </dependencies>

@hutchig
Copy link
Member Author

hutchig commented Oct 16, 2019

@kenfinnigan it was initially but then I modified it, see above the text which has:

Have a user project that depends on the api:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>my-groupId</groupId>
  <artifactId>my-artifactId</artifactId>
  <version>my-version</version>
  <dependencies>
    <dependency>
      <groupId>org.eclipse.microprofile.reactive.messaging</groupId>
      <artifactId>microprofile-reactive-messaging-api</artifactId>
        <version>1.1-SNAPSHOT</version>
    </dependency>
  </dependencies>
</project>

Apologies that this was not clear.

pom.xml Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants