Search code examples
javamaven-assembly-pluginhk2

hk2 inhabitant files are being overwritten by uber-jar assembly


I'm trying to build an executable jar (uberjar) using the maven-assembly-plugin. The project uses HK2 as the provider for dependency injection.

@Services are defined in the project as well as in some of the dependencies. The HK2 service locator is populated from inhabitant files at META-INF/hk2-locator/default, generated at compile/build time. I'm using the hk2-metadata-generator.

My problem is that the default assembly strategy jar-with-dependencies unpacks everything before building the jar. This overwrites META-INF/hk2-locator/default with whatever was unpacked last... As a result, the service locator cannot find all services.

I have explored different solutions and am looking for guidance which one is the best.

1. Aggregate different inhabitant files during assembly

I've created an assembly descriptor that combines all inhabitant files into one:

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>uberjar</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <containerDescriptorHandlers>
        <!-- remove this element and the following file-aggregator generates an empty file -->
        <!-- see https://issues.apache.org/jira/browse/MASSEMBLY-815 -->
        <containerDescriptorHandler>
            <handlerName>metaInf-services</handlerName>
        </containerDescriptorHandler>

        <containerDescriptorHandler>
            <handlerName>file-aggregator</handlerName>
            <configuration>
                <filePattern>META-INF/hk2-locator/default</filePattern>
                <outputPath>META-INF/hk2-locator/default</outputPath>
            </configuration>
        </containerDescriptorHandler>
    </containerDescriptorHandlers>
    <dependencySets>
        <dependencySet>
            <unpack>true</unpack>
            <scope>runtime</scope>
            <useProjectArtifact>true</useProjectArtifact>
        </dependencySet>
    </dependencySets>
</assembly>

Apart from the silly bug in the assembly plugin, this results in a combined inhabitant file that looks good. However, when I ask the Service locator to dump its descriptors, my project's services are detected several times. I don't know if that is a problem, particularly for singleton services.

2. Don't unpack the dependencies

The idea behind this was to leave the dependencies in their own jar-files, not touching their META-INF-files. I followed this post to build the jar, but I had classloading issues: dependencies could not be loaded despite the MANIFEST.MF specifying the jars in the classpath.

This feels like the cleanest solution, if only I could fix the classloading.

3. Use runtime discovery instead of inhabitant files

This solution is also promising. Problem is that it doesn't work for my tests using hk2-testng (and I don't understand what is different here).


If you have advice on the individual solutions or how best to proceed, I'd be very happy to hear from you.


Solution

  • HK2 inhabitant files are designed to work properly when concatenated together so option #1 above works. We use that option when putting full modules together