1

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.

Community
  • 1
  • 1
Hank
  • 4,597
  • 5
  • 42
  • 84
  • I would go with #1. We use that sort of solution all the time. A *fourth* option is to run the hk2-inhabitant-generator over the finished jar at the end. This fourth option is especially good if instead of just putting everything into one jar you are picking and choosing classes and you want a guaranteed-to-be-correct metadata file for that jar – jwells131313 Jul 06 '16 at 14:20

2 Answers2

2

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

jwells131313
  • 2,364
  • 1
  • 16
  • 26
2

You can also go with a fourth option which is to run hk2-inhabitant-generator over the finished jar as the last step, something like:

java org.jvnet.hk2.generator.HabitatGenerator --file *my-uber.jar*

When you run the HabitatGenerator like that it'll scan through all the files in the jar and will add the META-INF/hk2-locator/default file in the jar with all of the hk2 services.

This is a better option if your uber jar is parts and pieces of other jars rather than the entire thing because it is guaranteed to be more accurate (it won't contain services that aren't in the uber jar)

Hank
  • 4,597
  • 5
  • 42
  • 84
jwells131313
  • 2,364
  • 1
  • 16
  • 26