Question

I have a multi-module project, where each module is packaged as an OSGi bundle using the Apache Felix maven-bundle-plugin. The whole project is built using a parent POM that lists the above-mentioned modules. Some modules contain configuration resources (e.g. .properties files) that should not be jarred inside the bundles for deployment but rather externalized in a dedicated config folder. My goal is to create a distribution folder (possibly, a zip file) that would look something like this:

my-app-distribution
    /bundles
        module1-bundle.jar
        module2-bundle.jar
        etc.

    /conf
        external1.properties
        external2.properties
        etc.

where the properties files under the /conf directory are hand-picked files from the individual modules' /target folders. The reason the .properties files need to be picked up from the target folders vs. the src folders is that I am using Maven resource filtering, and the source property files contain ${..} placeholders for environment-specific values. Those placeholders are properly resolved during the build process - per build profiles - and the target/ folders contain actual environment-specific values.

I've done such distribution file manipulations many times - for distributions with executable JARs, etc. In this case I wanted to use the "moduleSets" configuration of the assembly descriptor - it is easy to pull all binaries/jars into a single distribution folder using moduleSet/binary descriptor. It is also easy to exclude certain files from being packaged into an OSGi bundle - in the maven-bundle-plugin. The only issue I am stuck with is creating the /conf distribution folder and collecting the necessary properties files there. I have tried to use "fileSets" inside the "moduleSet/sources" descriptor to include only specific files from **/target of each module, but that didn't seem to work.

Anyone have a suggestion/advice? There's got to be an easy way. Or should I not use at all?

Thanks,

CV


@PetrKozelka I am not sure that extracting configuration files specific to different bundles into a separate module is a good idea. The whole point of OSGi is for bundles to be independent and potentially reusable - both in development and distributions. It only makes sense that - in the source code - the functionality implementation and related configuration files are grouped together. For a particular distribution though I might need to extract some of the files - if there is a requirement for admins to have control of certain parameters. That may be different for a different distribution/application. The assembly configuration may change, but the bundles/sources would stay the same. Also, each bundle may potentially be developed and used separately, not all bundles have to always be part of the same uber project - as you seem to assume. What you are suggesting seems to fall into the same old category of packaging enterprise applications by the type of artifacts (e.g. "model", "services", "dataaccess", "config" etc.), not by functional domain/features. Such approach works ok within a single application/project, but fails on the enterprise level where there is often a need to reuse subsets of vertical components (split by functional domains).

To your point of being dependent on the file layout in the modules, I agree that there should be no such dependency. Files could be hand-picked by their explicit name or naming convention - per very specific distro requirements. (Which is exactly the case I am facing.)

Was it helpful?

Solution

I have actually figured out how to do it more or less elegantly. Posting the solution below in case someone else is looking to solve a similar problem.

SUMMARY

I am using the maven-assembly-plugin to extract the binaries (bundle JARs) from the individual modules and package them in the <my-distribution-folder>/bundles directory. In each module where some resource files should be externalized, I consolidate such files under the /src/main/resources/external directory, and use maven-resources-plugin to copy those resources during the packaging phase to the auto-generated directory in my dedicated distribution module that contains the assembly.xml descriptor file and is also built as part of the top project. I use maven-clean-plugin in the parent POM to clear the contents of the distribution staging directory during the CLEAN phase of the top-level project build.

MAVEN CONFIGURATION

Inside each bundle's module POM that contains resources that need to be externalized I add the following resource management configuration:

 <build>
    <defaultGoal>install</defaultGoal>

    <!--
    enable resource filtering for resolving ${...} placeholders with environment-specific values 
    exclude any files that must be externalized
    -->
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <excludes>
                <exclude>external/*.*</exclude>
            </excludes>
        </resource>
    </resources>
    ...

   <plugins>
        <!-- Copies contents of resources/external to dedicated folder defined by property in parent -->
        <!-- externalized resources will be packaged according to assembly instructions -->
        <plugin>
           <artifactId>maven-resources-plugin</artifactId>
           <version>2.6</version>
           <executions>
               <execution>
                   <id>copy-resources</id>
                   <phase>package</phase>
                   <goals>
                       <goal>copy-resources</goal>
                   </goals>
                   <configuration>
                       <outputDirectory>
                           ${project.parent.basedir}/${externalizableResourcesStageDir}
                       </outputDirectory>
                       <resources>
                           <resource>
                               <directory>src/main/resources/external</directory>
                               <filtering>true</filtering>
                           </resource>
                       </resources>
                   </configuration>
               </execution>
            </executions>
       </plugin>

        <!-- builds a JAR file for this bundle -->
        <plugin>
            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <extensions>true</extensions>
            <configuration>
                <instructions>
                    <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
                    <Import-Package>*</Import-Package>
                    <Export-Package>
                        ${project.groupId}.thismodulepackage*;version=${project.version}
                    </Export-Package>
                </instructions>
            </configuration>
        </plugin>
    </plugins>
</build>

where externalizableResourcesStageDir is a property defined in the top/parent POM. In the project, I include a special distribution module with the following structure:

distribution
   /ext-resources  (target auto-generated dir for external resources from modules)   
   /src
       /assemble
             assembly.xml   (assembly descriptor)

The assembly.xml file looks like this:

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2
      http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>bin</id>

<!-- generate a ZIP distribution -->
<formats>
    <format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<baseDirectory>/</baseDirectory>

<moduleSets>
    <moduleSet>
        <!-- Enable access to all projects in the current multi-module build -->
        <useAllReactorProjects>true</useAllReactorProjects>

        <!-- select projects to include-->
        <includes>
            <include>myGroupId:myModuleArtifactId1</include>
            <include>myGroupId:myModuleArtifactId2</include>
            ...
        </includes>

        <!-- place bundle jars under /bundles folder in dist directory -->
        <binaries>
            <outputDirectory>${artifactId}/bundles</outputDirectory>
            <unpack>false</unpack>
        </binaries>
    </moduleSet>
</moduleSets>

<!-- now take files from ext-resources in this module and place them into dist /conf subfolder-->
<fileSets>
    <fileSet>
        <directory>ext-resources</directory>
        <outputDirectory>${artifactId}/conf/</outputDirectory>
        <includes>
            <include>*</include>
        </includes>
    </fileSet>

</fileSets>

</assembly>

The distribution module's POM would look like this:

<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
    <groupId>myGroupId</groupId>
    <artifactId>parentArtifactId</artifactId>
    <version>...</version>
</parent>

<groupId>myGroupId</groupId>
<artifactId>distribution</artifactId>
<version>...</version>

<packaging>pom</packaging>
<name>Distribution</name>
<description>This module creates the <MyProject> Distribution Assembly</description>
<url>http:...</url>

<!-- NOTE: These dependency declarations are only required to sort this project to the
     end of the line in the multi-module build.
-->
<dependencies>
    <dependency>
        <groupId>myGroupId</groupId>
        <artifactId>myModuleArtifactId1</artifactId>
        <version>${project.version}</version>
    </dependency>
 ...
</dependencies>

<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
                <execution>
                    <id>dist-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <configuration>
                        <descriptors>
                            <descriptor>src/assemble/assembly.xml</descriptor>
                        </descriptors>
                    </configuration>
                </execution>
            </executions>
        </plugin>

    </plugins>
</build>
</project>

The parent POM would list all the bundle modules, plus the distribution module and also define the assembly plugin:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>myGroupId</groupId>
    <artifactId>myParentId</artifactId>
    <version>...</version>
    <packaging>pom</packaging>

    <properties>
        ...

        <!-- directory where build may place any sub-modules' resources that should be externalized -->
        <!-- those resources may be picked up by maven-assembly-plugin and packaged properly for distribution -->
        <externalizableResourcesStageDir>
            esb-distribution/ext-resources
        </externalizableResourcesStageDir>

    </properties>

    <!-- all project modules (OSGi bundles + distribution) -->
    <modules>
        <module>bundle-module1</module>
        <module>bundle-module2</module>
        ...
        <module>distribution</module>
    </modules>



    <dependencyManagement>
      <dependencies>
      ...
      </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <!--
                Cleans contents of the folder where the externalized resources will be consolidated
                Each module adds its own external files to the distribution directory during its own build
                -->
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>2.5</version>
                    <executions>
                        <execution>
                            <id>clean-ext-resources</id>
                            <phase>clean</phase>
                        </execution>
                    </executions>
                    <configuration>
                        <filesets>
                            <fileset>
                                <directory>${externalizableResourcesStageDir}</directory>
                                <includes>
                                    <include>*.*</include>
                                </includes>
                                <followSymlinks>false</followSymlinks>
                            </fileset>
                        </filesets>
                    </configuration>
                </plugin>


                <plugin>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>2.3</version>
                    <configuration>
                        <descriptors>
                            <descriptor>src/assemble/assembly.xml</descriptor>
                        </descriptors>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

</project>

NOTE: We've also made sure that the externalized resource files are excluded from being packaged inside the individual bundle JARs (see the resources section of the module POM.) The resulting unzipped distribution will look like this:

my-app-distribution
  /bundles
    module1-bundle.jar
    module2-bundle.jar
    etc.

  /conf
    external1.properties
    external2.properties
    etc. 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top