Sunday, January 4, 2015

Maven inheritance aggregation

Maven inheritance aggregation

Challenge

The challenge is to create a build system (based on maven of course) that will be delivered to the next layer of development. The difficulty here is that the core team will continue to change the delivery system, and release new versions. We do not want (or would like to limit) the adoption teams to always have to merge when there is a new version.

 

Solution

The solution needs to be implemented in more than one single solution and way.  The standard way of maven is inheritance. Any dependency or plugin that you want to add you put it in the parent pom and everyone can inherit this. The problem is that we do not always want to inherit, and we don’t always want the same functionality for all children. To solve this issue you can put any dependencies or plugins in profiles and then activate them in a child module. This solution is not clean since it is very cumbersome and makes the parent pom very big and hard to manage with all the profiles.

BOM (Bill Of Materials)

Since our project consists of multiple jars and versions we do not want the adoption team to have to update all versions for every new release. In addition if we add a new jar we do not want the adoption team to have to add the new jar to their parent pom.
By creating a bom file (more a less a parent pom), the adoption parent pom does not have to inherit your pom, but can import it (http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html). This will allow the adoption to manager the core product with one import on the dependency.
For example (this is put in the dependencyManagement section):
                                                <dependency>
                                                                <groupId>com.xxx.wsf.buildtools</groupId>
                                                                <artifactId>wsf-bom</artifactId>
                                                                <version>${wsf.tools.version}</version>
                                                                <type>pom</type>
                                                                <scope>import</scope>
                                                </dependency>
This will import all the dependency managements from the wsf-bom file.
Just a reminder in the BOM you only have dependencyManagement and not the dependencies themselves. In addition BOM’s are only for dependency management and not plugin management.
Note: When you have a multi module project, you parent pom needs to be the bom so that it is installed first, else no one is installing it since it is imported in the dependencyManagment and not dependencies.

DepChain

A BOM file is a good start, but what do I do if I have multiple projects that need the actual dependencies and not just the versions (dependency management). If it is only one dependency then we can manually add it to the module, but if it is more than one, or even one but in the future it will grow we would like to import the dependencies themselves and not just the dependency management.
A depchain is a pom file that has the actual dependency list and not just the management. Once you have an artifact that is a depchain you can then import it into any module (this is put in the dependencies section:
                                <dependency>
                                                <groupId>com.xxx.wsf.buildtools</groupId>
                                                <artifactId>wsf-depchain-design</artifactId>
                                                <type>pom</type>
                                                <scope>provided</scope>
                                </dependency>              
This will bring in the actual dependency list into your module, notice that the scope is provided.

 

Mixins

Mixin is an object oriented paradigm to add functionality to a class but not by inheritance (http://en.wikipedia.org/wiki/Mixin). Maven has toyed with the idea of adding mixins, currently it is on the roadmap for maven 4 (https://cwiki.apache.org/confluence/display/MAVEN/Roadmap). All thought it is not released by maven, maven allows you to create plugins that are extensions to maven. So a developer by the name of ohad david has written a mixin extension for maven (https://github.com/odavid/maven-plugins).
What this extension allows you to do is to create an external pom that will be merged into your pom. So with this you can write a set of plugins that will give you added value, and then you import it into any module you need this functionality (you also have the option to move the content of the pom to a mixin.xml so that when you install the mixin it does not run the plugins).
For example we would like to write a set of plugins that depending on the OS it will change the permissions of a file in a folder. The pom that does this is fairly simple. What we would like is the option to move this functionality from the parent pom into anther pom that other projects can use. Just like the BOM allows you to import all dependencies version of a project, a dep chain will allow you import the dependencies them self’s.

Example of mixin: permission per os (the value of the properties will be defined in the calling pom).
The pom of the mixin might look something like:
<properties>
<folder.location/>
<windows.permissions>-r /s</windows.permissions>
<linux.permissions>777</linux.permissions>
</properties>
<profiles>
<profile>
  <id>windows</id>
  <activation>
      <os>
         <family>windows</family>
      </os>
   </activation>
       <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                       <artifactId>maven-antrun-plugin</artifactId>
                      <executions>
                          <execution>
                             <id>run app maven</id>
                                <phase>validate</phase>
                                <configuration>
                                    <target>
                                        <exec dir="${folder.location}" executable="attrib" failonerror="true">
                                            <arg line="${windows.permissions} *.*"/>
                                        </exec>
                                    </target>
                                </configuration>
                                <goals>
                                    <goal>run</goal>
                                </goals>
                         </execution>
                     </executions>
                </plugin>
         </plugins>
    </build>
</profile>
<profile>
     <id>linux</id>
         <activation>
             <os>
                    <family>unix</family>
                </os>
         </activation>
        <build>
            <plugins>
                   <plugin>
                       <groupId>org.apache.maven.plugins</groupId>
                       <artifactId>maven-antrun-plugin</artifactId>
                       <executions>
                            <execution>
                                    <id>set permission</id>
                                    <phase>process-test-resources</phase>
                                    <goals>
                                         <goal>run</goal>
                                   </goals>
                                  <configuration>
                                       <target>
                                            <chmod dir="${folder.location}" perm="${linux.permissions}"/>
                                       </target>
                                </configuration>
                          </execution>
                     </executions>
             </plugin>
        </plugins>
    </build>
</profile>

The usage of this plugin in another pom would then be:
<properties>
                <folder.location>${basedir}/src/test/resources/asm</folder.location>
</properties>
<plugin>
                <groupId>com.github.odavid.maven.plugins</groupId>
                <artifactId>mixin-maven-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                                <phase>process-classes</phase>
                                <mixins combine.children="append">
                                                <mixin>
                                                                <groupId>com.abc</groupId>
                                                                <artifactId>wsf-mixin-files-permission</artifactId>
                                                                <type>mixin</type>
                                                </mixin>
                                </mixins>
                </configuration>
</plugin>                           

Why mixins still don’t make it:

Although mixins do give us a lot of functionality, they do not really wrap a full set of functionality to be used in other poms. For example is you write an assembly mixin, all the extra files for the assembly cannot be put in the mixin but need to be put into the calling pom. So we don’t actually have a full encapsulation solution, but a step in the right direction.

For more information on this see: