Mixin Maven Plugin - A New Way of Reusing Conventions

7 comments

Friday, September 12, 2014

Introduction

One of the major concepts in Maven is the "Convention Over Configuration" concept.

When we say Convention Over Configuration, we mean that if we follow some rules, then we don't need to repeat configuring these rules again and again within our project modules. But still, these rules must be defined somewhere in order to be applied within the project modules.
In Maven, we call the document that defines these rules a"Parent POM".
The mechanism for applying these rules in child modules is called "POM Inheritance". Maven's default conventions are defined within the "Super POM", and all modules inherit from it implicitly.

When dealing with a multi module project, we use a "Project Parent POM" that defines the project's conventions. The project parent pom inherits from Maven's super pom and adds more conventions that are specific to the relevant project. When developing multiple projects for the same Organization, usually there is a  "Corporate POM" that defines the corporate's conventions and being used as a middle tier between the "Maven Super POM" and the "Project Parent POM".

Types of Conventions

In Maven we can distinguish between several types of conventions. Each type is declared in its own section within the POM.
  • Dependency Management - The dependency management section is used to enforce dependency versions that will be used by all sub modules
  • Plugin Management - The plugin management section responsibility can be divided into 2 different aspects: 
    • Like the dependency management, it enforces the plugin versions that will be used within the project's modules
    • The Plugin Management section also defines the default configuration of the plugins within your project. Example for that could be adding Jar Manifest entries into all the jar artifacts of the project
  • Properties - properties are usually used as a way to define default values that are used within the plugins' configuration and can be overridden by sub modules without the need to write the whole plugin configuration section again. 
  • Profiles - Profiles are basically conditional set of behaviours that can be applied externally or if some conditions are met. Most of the time, profiles are used to define rules that should be applied in special events or different environments, such as CI Server Environment, the Release Event, IDE Workspace Creation Event, etc... 

Problems

The single inheritance nature of Maven, gives a good way to define conventions in the context of a project or a corporate, but lacks the ability to reuse conventions that are defined out of these contexts. Also, it is complex to combine different conventions for different projects' needs.
An example for that would be when there are several projects that uses a common framework or SDK and need to use the same conventions for that framework. If we put these convention rules within the Corporate POM, then we will need a way to disable these rules when there are projects that don't use the same framework.
Usually for these cases, the Corporate POM stays thin, and each Project POM will declare the same rules by "Copy/Paste" of code snippets.
Another common approach is to define multiple Parent POM files which define these rules. The problem with this approach is that there are projects who share different frameworks and do not share other frameworks. In that case we usually end up with a "deep inheritance tree" and still there will be some "Copy/Paste" sections in our POM files.

Another problem with the "Parent POM" as a container of rules, is that in many cases, it gets very big and it becomes hard to maintain it.

When dealing with the "actual build code", Maven introduced us the concept of plugins. Plugins are a good way to encapsulate and reuse small portions of code to do the actual build tasks, such as compilation, packaging, source generation, and much more. While there are many plugins out there that actually do most of the work needed to be done, there is a problem with "glueing" them all together and reuse their orchestration as a "new Plugin" . Let us say that we have few jar modules that require the following build lifecycle:
  1. Unpack a common resources dependency
  2. Use a code generator such as JavaCC or Antlr
  3. Compile
  4. Package
Many times, we find ourselves pasting the same plugin execution code within all of these modules. If we wanted to declare a common "Function" to do this job, we would need write a plugin that will orchestrate these steps. The problem with writing a plugin, is that it is too complex, requires a different knowledge than writing POM files and also there is no native orchestrator API for doing that. (If you still want to do that though, there is a nice project that I've been using called mojo-executer)

In "Object Oriented Paradigm", single inheritance may be the main tool for reuse, but it is not the only tool. There is also the Object Composition concept that enables us to reuse functionality without the need to inherit it. In modern OO Languages, we also have the Mixin concept that enables us to combine multiple methods from different classes into one class.

It would have been great if we had the ability to define or import conventions based on their functionality scope into the project scope. It would be nice to state that my project is "Project-X" developed in "Corporate Y" and is mixture of "Spring MVC", "JavaCC" and "Hibernate" and by that we'll get all the required conventions into our sub-modules, without copy/paste them into the Parent POM or the sub-module POM files.

Current Solutions & Practices

Some convention types can be reused today by using the following concepts and practices:
  • For "Dependency Management", Maven introduced the "Bill Of Material" (a.k.a BOM) concept. Instead of stating all the dependencies' versions in the Parent POM, you can import smaller POM files called BOM by using the "import" scope. Using BOM files is a great way to reduce the Parent POM or a project, and also a good practice of managing the dependencies based on their functionality. BOM files are released by a lot of frameworks such as Spring, JBoss, Jersey and many more.
  • Grouping common dependencies according to their functionality can be achieved by using the "Dependency Chain" (a.k.a depchain) concept. A depchain is a special pom file that gather common dependencies into one POM file. The depchain concept is the equivalent of BOM to the dependencies section within the POM. Using a depchain is a good practice to reduce the amount of repeated code that defines the dependencies for a framework or SDK.
Still, there is no good solution for encapsulating all conventions based on their functionality into a single component. So we still need to have a deep inheritance tree and/or copy paste code within sub-modules.

Proposed Solution

It seems that in Maven 4, hopefully, there will a native Maven solution for these types of problems (MNG-5102), but until then, I wanted to have a solution that will work without moving to a new build tool and without patching or changing Maven core.
I recently released a Maven Extension called mixin-maven-plugin to Maven Central. The plugin introduces a new concept called a "Mixin POM", which is a supplementary concept to the "BOM" and the "Depchain".
The Mixin POM contains all required Plugin Management, properties and profiles that are grouped by functionality. So if you want to include a specific functionality conventions within a module, you just need to import its BOM, use the Functionality Depchain and import the Functionality Mixin. Note that, the BOM, Depchain and Mixin, can be the the same actual POM file, but still they are 3 supplementary concepts.

The mixin-maven-plugin


How Does it Work

The mixin-maven-plugin is a Maven extension that runs before the build is executed. Within the plugin configuration we declare a list of POM files we want to import into our project. It reads all the imported POM files and merge them to the POM file in the same manner Maven calculates the effective-pom. The plugin merges only the build, properties and profiles sections and does not merge any other elements of the pom such as dependencies, repositories, etc…

Since it is running before the build and enriches the model, you can see all the changes done to your project POM by running the “mvm help:effective-pom”. Also, because the plugin is using the Maven Model Merger, you can override the configuration the same way you do it when you inherit from a parent pom.

Using the Plugin

In order to use the plugin, you first need to have some mixin pom files. I usually tend to have them released in a different lifecycle from my project and by that make them available to other projects I use.

A mixin pom file is a pom artifact (packaging = pom), which contains only properties, build and/or profiles sections, and does not deal with dependencies.

Echo-Message-Mixin


In order to use the mixin pom files within your pom file, you need to declare them within your consumer pom. This is done by using a regular plugin declaration within the build section. Note that the mixin plugin is an extension, and therefore you need to use <extensions>true</extensions>

Mixin Consumer


Example

A working example can be downloaded from here

Things That Are Good to Know About the Plugin

The order of the POM model analysis is as follows:
  • In the first phase, Maven reads the project POM and merges its parent hierarchy
  • After the POM was read by Maven, the mixin-maven-plugin is running and merges all mixin pom files according to their order in the tree
  • Profiles injections are calculated first for every mixin and only if a profile should be activated in the current POM, then they will be injected to the current POM
Since the first phase of building the POM model is executed by Maven, then it is important to have all plugins versioned either by the parent pom Plugin Management section or by defining the version within the plugin execution. Otherwise, Maven will warn you that the plugins are not versioned. As a best practice, I use a common parent pom that declares the Plugin Management section to define the plugins I am using with only their versions declared.

Conclusions

Code Reuse is a powerful concept in Software Development. Open Source projects changed the way we develop software. Maven and Dependency Oriented Development, changed the way we consume Reusable Code. Convention Over Configuration changed the way we manage our software projects. I think that the Mixin within Maven introduces a new level of software reusability. It introduces the ability to reuse and organize conventions and by that reduces the amount of configurations within our projects.

Some References

Here are some references to relevant stuff:
  • Tim Moore Mojo Executer - A simple API to execute plugins within the context of other plugins
  • Mixin POM Fragments - A feature request for Maven to introduce the Mixin Concept natively within Maven
  • Maven Tiles - I've came across this one after releasing the mixin-maven-plugin. It seems to be dealing with the same issues

↑
Top