Overriding Managed Dependency Versions for Patch Releases

Overview

Because the parent POM is shared by so many projects, it is common for it to be left alone when patch releases of some but not all our projects have to be issued. This is also because when multiple versions of the parent POM are referenced by different projects in a build, Maven can’t rely on a single version of the parent when it computes the dependency tree, causing mismatches and even producing the wrong results. As an example if we need to issue an IdP patch and update a third party dependency, changing the parent will not work reliably because opensaml, spring-extensions, and java-support may still reference the older parent.

As a rule, the parent tends to be left alone through the lifecycle of a minor version of a product and so cannot be the method by which a third party dependency version is updated. This has implications because of how Maven parses and handles properties. Overriding a property macro referring to the version of a third party dependency does not cause Maven to retrospectively update its definition of the managed dependency in the parent POM, and so in turn will not cause the right updated version to appear in a distribution.

Instead, though unfortunate, the procedure below needs to be followed when a component needs to be updated in between parent POM releases.

Procedure

The managed dependency declarations for the third party component must be duplicated in the project’s own POM (or parent POM, in the case of a multi-module project like the IdP or OpenSAML). We have also found that the use of BOMs doesn’t work properly when multiple versions of a BOM are in the overall dependency tree, and so the entire contents of a BOM used as a managed dependency in the “root” parent POM have to be expanded into the updated projects’s POM or parent POM. (This is perhaps a bug in Maven and might be something to monitor and check periodically for a fix.)

As an example, consider a self-contained component such as Guava. To “patch” the version of Guava so that an IdP patch will include the updated version, idp-parent/pom.xml must be updated:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... <dependencyManagement> <dependencies> ... ... <!-- Note the change for posterity with a TODO to remove when the parent POM is updated. --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>(updated Guava version)</version> </dependency> ... </dependencies> </dependencyManagement> ...

If the original managed dependency contained exclusions, these must be copied from the parent POM to fully duplicate the original dependency.

In the case of a more complex dependency like Spring, the process is the same, but because the original dependency is based on the BOM, the set of duplicated dependencies is very long since the entire list of potential Spring dependencies has to be spelled out.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 ... <properties> <!-- TODO: Remove when parent POM updated. --> <spring.version>5.3.9</spring.version> </properties> ... <dependencyManagement> <dependencies> ... ... <!-- Note the change for posterity with a TODO to remove when the parent POM is updated. --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-indexer</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jcl</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-r2dbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webflux</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring.version}</version> </dependency> ... </dependencies> </dependencyManagement> ...