Pages

Thursday, September 17, 2015

Maven Shade Plugin with Spring Applications

I recently came across multiple projects that use the Maven Shade Plugin to package Java applications into one big jar. Not that I think this is a particularly good idea, in fact I believe it's a terrible idea that may lead to somewhat unpredictable results. However, you sometimes have to live with what's already in place...

When I started using Spring in my applications another downside of this approach emerged: While all the tests passed just fine the packaged application failed to even start throwing somewhat unexpected exceptions like this:

org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from URL location [appContext.xml]
Offending resource: class path resource [appContext.xml]; 
[...]
Offending resource: class path resource [appContext.xml]; nested exception is org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/context]

This is surprising at first as the handler and schema definitions are bundled with the various Spring modules. The issue is, that each Spring module contains META-INF/spring.handlers and META-INF/spring.schemas files, all of which are read by Spring at runtime. However, if you use the Maven shade plugin, all these files overwrite each other and you end up with one random set of handler and schema definitions in your final jar file. In my example the "context" namespace handler is missing, but it could be any namespace depending on the order Maven decides to package the files in.

In order to avoid that you can tell the shade plugin to append files if they re-appear instead of overwriting them by adding two transformers to the execution configuration of the plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.3</version>
    <configuration>
       [...]
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                </transformers>
            </configuration>                        
        </execution>
    </executions>
</plugin>

Again, I think it's a terrible idea to package your apps into one big jar, so don't do it if you don't have to.

No comments:

Post a Comment