15

I'm using Maven and its assembly plugin to build a distribution package of my project like this:

  • one project assembles a basic runtime (based on Felix), with the appropriate directories and bundles, in a ZIP file.
  • third-party libraries are collected in one project each and either converted to OSGi bundles or, if they are already OSGi compatible, they are just copied
  • my own project consists of several modules that are built into OSGi bundles, too.

Now, I'm adding another project that unpacks the ZIP, drops all the other JARs into the proper directories, and repackages it for distribution. Now, my bundles might contain configuration files that I want to merge into, rather than replacing, identically named ones in the runtime assembly. How do I do that?

The files are plain text (property files), but I might run into a similar situation with XML files later.

starblue
  • 55,348
  • 14
  • 97
  • 151
Hanno Fietz
  • 30,799
  • 47
  • 148
  • 234
  • I don't know anything that can do this. That said, I'm not very used to OSGI. However, is this a "regular" use case? – Pascal Thivent Oct 22 '09 at 13:37
  • @Pascal - This is not specific to OSGi, really. I'm not sure how regular this is, but I could imagine that if I wanted to merge WARs or OSGi bundles, I might want to merge web.xml or MANIFEST.MF files, respectively. I'm sure there is other tasks of the same type, but maybe there's a completely different approach to this. – Hanno Fietz Oct 22 '09 at 13:53
  • Why merging OSGI bundles? I'm a big noob with OSGI but aren't they independent units? For WARs, it's a little bit more clear but I can think of many issues with a merge (e.g. what if both wars have an `index.jsp`, what if they both rely on the same lib but with different versions, etc) and would rather consider them as independent units too. Patching a `web.xml` seems more "realist" (and can be useful, e.g. in a testing context, cargo has a goal for that). – Pascal Thivent Oct 22 '09 at 15:00
  • @Pascal - don't get too hung up on those examples. The use case for the plugin I was pointed to in the accepted answer is actually quite sound. – Hanno Fietz Oct 23 '09 at 22:28

5 Answers5

27

Expanding a bit on Juergen's answer for those who stumble on this - the containerDescriptorHandler in the descriptor can take four values (v2.3), these are metaInf-services, file-aggregator, plexus, metaInf-spring. It's a bit buried in the code (found in the package org.apache.maven.plugin.assembly.filter) but it is possible to aggregate config/properties files.

Here's an example descriptor that aggregates the META-INF/services and named property files located in com.mycompany.actions.

descriptor.xml

<assembly>

...

<containerDescriptorHandlers>

    <containerDescriptorHandler>
        <handlerName>metaInf-services</handlerName>
    </containerDescriptorHandler>

    <containerDescriptorHandler>
        <handlerName>file-aggregator</handlerName>
        <configuration>
            <filePattern>com/mycompany/actions/action.properties</filePattern>
            <outputPath>com/mycompany/actions/action.properties</outputPath>
        </configuration>
    </containerDescriptorHandler>

</containerDescriptorHandlers>

....

</assembly>

The file-aggregator can contain a regular expression in the filePattern to match multiple files. The following would match all files names 'action.properties'.

<filePattern>.+/action.properties</filePattern>

The metaInf-services and metaInf-spring are used for aggregating SPI and spring config files respectively whilst the plexus handler will aggregate META-INF/plexus/components.xml together.

If you need something more specialised you can add your own configuration handler by implementing ContainerDescriptorHandler and defining the component in META-INF/plexus/components.xml. You can do this by creating an upstream project which has a dependency on maven-assembly-plugin and contains your custom handler. It might be possible to do this in the same project you're assembling but I didn't try that. Implementations of the handlers can be found in org.apache.maven.plugin.assembly.filter.* package of the assembly source code.

CustomHandler.java

package com.mycompany;

import org.apache.maven.plugin.assembly.filter.ContainerDescriptorHandler;

public class CustomHandler implements ContainerDescriptorHandler {
    // body not shown
}

then define the component in /src/main/resources/META-INF/plexus/components.xml

components.xml

<?xml version='1.0' encoding='UTF-8'?>
<component-set>
    <components>
        <component>
            <role>org.apache.maven.plugin.assembly.filter.ContainerDescriptorHandler</role>
            <role-hint>custom-handler</role-hint>
            <implementation>com.mycompany.CustomHandler</implementation>
            <instantiation-strategy>per-lookup</instantiation-strategy>
        </component>
    </components>
</component-set>

Finally you add this as a dependency on the assembly plugin in the project you wish to assemble

pom.xml

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.2.1</version>
    <configuration>
        <descriptors>
            <descriptor>...</descriptor>
        </descriptors>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>com.mycompany</groupId>
            <artifactId>sample-handler</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>
</plugin>

and define the handlerName in the descriptor

descriptor.xml

...
<containerDescriptorHandler>
    <handlerName>custom-handler</handlerName>
</containerDescriptorHandler>
...

The maven-shade-plugin can also create 'uber-jars' and has some resource transforms for handling XML, licences and manifests.

J

jwmay
  • 331
  • 4
  • 3
  • All that just to specify `metaInf-services`? I don't need or want a `descriptor.xml` file. Is there any way to simply indicate the `metaInf-services` handler in the `` in the POM> – Garret Wilson Nov 18 '17 at 17:40
  • For reference, I think the Assemble plugin docs have caught up - https://maven.apache.org/plugins/maven-assembly-plugin/examples/single/using-container-descriptor-handlers.html – Nick Holt Mar 27 '19 at 20:30
10

Old question but stumbled over it while trying to solve similar problem: Assembly plugin 2.2 has capabilities to merge files: http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html#class_containerDescriptorHandler e.g. handlerName "metaInf-services" (will concat all META-INF/services files), "metaInf-spring" are the only ones I know of (I personally needed metaInf-services)

Juergen
  • 101
  • 1
  • 2
6

I don't know of a robust solution to this problem. But a bit of looking around shows that somebody has created a plugin to merge properties files. By the look of it you need to tell it which files to merge, which is a good thing as you don't want this applied willy nilly.

Assuming you have used dependency-unpack to unpack the zip to a known location, it would be a case of configuring the plugin to merge each pair of properties files and specify the appropriate target location.

You could extend the plugin to handle XML by using something like xmlmerge from EL4J, as described in this Javaworld article.

zellus
  • 9,617
  • 5
  • 39
  • 56
Rich Seller
  • 83,208
  • 23
  • 172
  • 177
1

Ive also created a merge files plugin, in my case i use it to merge SQL files from various projects into a single installer SQL file which can create all the schemas/tables/static data etc for our apps in a single file, http://croche.googlecode.com/svn/docs/maven-merge-files-plugin/0.1/usage.html

  • Why I see http://croche.googlecode.com/svn/docs/maven-merge-files-plugin/0.4/usage.html as a plain text and not HTML? – xmedeko May 31 '12 at 15:26
0

https://github.com/rob19780114/merge-maven-plugin (available on maven central) also seems to do the job. See below for an example configuration

 <plugin>
    <groupId>org.zcore.maven</groupId>
    <artifactId>merge-maven-plugin</artifactId>
    <version>0.0.3</version>
    <executions>
        <execution>
            <id>merge</id>
            <phase>generate-resources</phase>
            <goals>
                <goal>merge</goal>
            </goals>
            <configuration>
              <mergers>
                <merger>
                  <target>${build.outputDirectory}/output-file-1</target>
                  <sources>
                    <source>src/main/resources/file1</source>
                    <source>src/main/resources/file2</source>
                  </sources>
                </merger>
                <merger>
                  <target>${build.outputDirectory}/output-file-2</target>
                  <sources>
                    <source>src/main/resources/file3</source>
                    <source>src/main/resources/file4</source>
                  </sources>
                </merger>
              </mergers>
            </configuration>
        </execution>
    </executions>

Kai
  • 700
  • 7
  • 33