When using OpenSAML 3, you must first load components from the opensaml-saml-impl
artifact with the following line of code:
InitializationService.initialize();
This uses java.util.ServiceLoader
to load any type which implements Initializer
.
When I write a test and run it with mvn integration-test
, this works fine, and I can see that everything has loaded:
Assert.assertTrue(
XMLObjectProviderRegistrySupport
.getUnmarshallerFactory()
.getUnmarshallers()
.size() > 400);
However, my project uses maven-shade-plugin
. The condition above is not true if I package the code into an uber-jar:
mvn package
java -jar /path/to/my.jar
In this case I observe that only 9 unmarshallers have loaded (those in opensaml-core
, as opposed to those in opensaml-saml-impl
. However, when I watch the output of mvn package
, I can see that the types are included in the shaded jar:
[INFO] Including org.opensaml:opensaml-saml-impl:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-profile-api:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-messaging-api:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-saml-api:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-xmlsec-api:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-soap-api:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-storage-api:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-security-impl:jar:3.2.0 in the shaded jar.
[INFO] Including org.opensaml:opensaml-security-api:jar:3.2.0 in the shaded jar.
I can work around this issue with the following dumb code:
private static void initManuallyInsteadOfWithInitializationServiceSoThatMavenShadePluginDoesNotRemoveThem() throws InitializationException {
new ApacheXMLSecurityInitializer().init();
new ClientTLSValidationConfiguratonInitializer().init();
new GlobalAlgorithmRegistryInitializer().init();
new GlobalParserPoolInitializer().init();
new GlobalSecurityConfigurationInitializer().init();
new JavaCryptoValidationInitializer().init();
new SAMLConfigurationInitializer().init();
new org.opensaml.core.xml.config.XMLObjectProviderInitializer().init();
new org.opensaml.xmlsec.config.XMLObjectProviderInitializer().init();
new XMLObjectProviderInitializer().init();
}
This utterly defeats the point of the plugin system, but it does allow my program to function.
For reference, here's the relevant bits of pom.xml
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.example.Server</Main-Class>
</manifestEntries>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource>
</transformer>
</transformers>
<artifactSet>
</artifactSet>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
</outputFile>
<filters>
<filter>
<!-- Fix java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
when server starts inside Docker container due to inclusion of OpenSAML and use of
uber-jar / maven-shade-plugin. See http://stackoverflow.com/a/6743609 -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
<filter>
<!-- This was one of my attempts to fix the problem.
Unfortunately, it doesn't work. -->
<artifact>org.opensaml:opensaml-saml-impl</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
This plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies.
maven-shade-plugin : It packages all dependencies into one uber-jar. It can also be used to build an executable jar by specifying the main class. This plugin is particularly useful as it merges content of specific files instead of overwriting them by Relocating Classes.
The dependency-reduced-pom. xml removes transitive dependencies which are already in your shaded jar. This prevents consumers from pulling them in twice.
Shading is a process where a dependency is relocated to a different Java package and copied into the same JAR file as the code that relies on that dependency. The main purpose of shading is to avoid conflicts between the versions of dependencies used by a library and the versions used by the consumers of that library.
When you're using the Maven Shade Plugin with dependencies using the ServiceLoader
API, you should use the ServicesResourceTransformer
, which is dedicated to merge together the files. If the plugin is relocating classes, it will also relocate properly the class names in each service file, unlike the AppendingTransformer
.
So you can just replace your current AppendingTransformer
with
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
It will make sure that every service file under META-INF/services
of your dependencies are merged, without the need to declare them all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With