Writing a Full Featured Maven PomErik EnglundBlockedUnblockFollowFollowingMar 22by Pixabay from Pexels under CCOMaven is a build tool with a structured build pipeline.
It alloys extensive customization for your own needs.
You can get a build going with very little in your pom.
But a good build, that follows everything you should probably do within a CI/CD pipeline takes a bit more.
We’ll go through all the various pieces and our recommended configuration.
If you want to just skip ahead the full code and example can be found at my GitRepo:efenglu/mavenExample project containing a pom with lots of plugins – efenglu/mavengithub.
comBasic InformationEvery project should describe itself.
For maven this means defining at a bare minimum:But you should really also include:Name: Human readable name for the projectDescription: More human stuff about your projectURL: Main url to go to for information about the project, typically same as scm url but not alwaysIssue Management: Where users can go if they have problemsLicenses: What License governs the use of your codeSCM: Where is the code storedExample:EncodingMaven needs to know how it should treat your text.
This is done through project enoding.
Almost always this is UTF-8:CompilerNow that we have the basics we need to get to coding, and by coding we of course mean compiling.
JavaBy default the javac compiler is basically all setup.
But, there are a few housekeeping things we need to do.
Choose which version of java the source and compiled classes we will targetInclude debugging flagsAny other javac flags or custom argumentsFor most of our projects we follow a pretty simple pattern:Further ReadingApache Maven Compiler PluginGroovyFor most of our projects we use the Spock testing framework.
This means we need to compile groovy code in order to run the tests.
We have tried all sorts of groovy compilers.
Each has their advantages and disadvantages.
For now we have chosen GMavenPlus.
Further ReadingGMavenPlusLombokLombok is a great way to reduce code complexity in Java.
We use it extensively when creating our POJO'sFor the most part, simply including lombok as a provided dependency should be enough.
Further Reading:LombokLombok MavenCode GenerationThis is more of an optional section.
There are many tools that assist you by generating code for you at build time.
There are several different examples of this.
ProtobufProtocol Buffers is a fast growing binary interchange format originally written by Google.
Protocol buffers are defined in .
These are then compiled into various languages.
Here is how we go about compiling our proto files:The os-maven-plugin will detect our build environment platform.
This is then used in determining which platform specific protoc executable to download.
The protobuf-maven-plugin then uses that executable to compile the actual .
proto files into java code.
Further Reading:Protocol BuffersMaven Protobuf PluginUnit TestsGood units tests are critical to the future, and present, of any successful project.
Thankfully, setting up units tests is fairly easy:You may have noticed the includes elements.
We enforce a naming standard for our tests.
They must end in either "Test", "Tests" for JUnit tests or "Spec", "Specs" or Spock Specifications.
Further Reading:Maven Surefire PluginCode CoverageEnsuring code is actually tested is critical.
A good metric to determine if code has been tested is Code Coverage.
It is not a perfect metric and you should probably never require 100% coverage.
But it is a great way to ensureat least some level of compliance with testing.
But then again we all follow TDD to a tee and this shouldn't be a problem, right?There are two big players when it comes to code coverage, Jacoco and Cobertura.
We use Jacoco.
We enforce code coverage of 70%.
We also filter all tests classes from coverage statistics.
Further Reading:Jacoco Maven PluginIntegration TestsSome tests take longer than others.
Some tests are more complicated and require more setup and resources.
These are great example of integration tests.
Most integration tests are run later in the build process.
Sometime integration tests are not run at all on developer workstations.
We follow a pattern of placing integration tests into a src/it/java path structure.
This separates the integration tests from the regular tests.
However, since maven doesn't really recognize the concept of integration tests it is also necessary to name the tests differently since all the testsget compiled into a single test classpath.
For our project all integration tests must end in *IT.
Static Code AnalysisStatic code analysis is another way to prevent bugs from happening.
You can scan your codebase for common programming mistakes and catch them early.
You can also ensure a level of code quality and coding standards to ensure the code is easy to read.
There are lots of different SCA tools:CheckstylePMDCPDSpotbugs (Successor to Findbugs)CodenarcetcCheckstyleCheckstyle enforces code formatting and checks for some very basic bad programming practices.
Things of NoteWe configure checkstyle in the plugin management section to ensure if the user invokes checkstyle from the command line then they will get the correct configuration.
It is also recommended that you define your own checkstyle configuration.
You should deploy this as its own artifact and consume it in the checkstyle plugin.
You can see where I defined my own version in the dependency I added to the checkstyle plugin.
This jar is available to checkstyle on the classpath and can load a resource from that jar using configLocation element.
Maven EnforcerJust like there is SCA for code the Maven Enforcer is a kind of static code analysis of your maven poms.
We use the enforcer to ensure:requireMavenVersion: 3.
0requireNoRepositories: No other repository elements in our pomrequireReleaseDeps: We don't reference a snapshot artifact in a release artifactrequireUpperBoundDeps: We don't have version conflicts in our dependenciesFurther readingMaven Enforcer PluginMaven Dependency AnalyzerAs the number of dependencies grows it becomes necessary to run static analysis on our dependencies as well.
Are we still using them?Does our code import classes from transitive dependencies?This is generally a bad idea.
If a dependent library is providing a dependency that we use transitively, what happens if that library suddenly removes that dependency?.In general, if you have a dependency you should also call it out.
PackagingMaven already adds several things to the Manifest files that are created.
But we should probably add a few others.
Add Git InformationWe like to put information of what what commit, tag/branch the code was compiled with into the jar itself.
To do this we need to harvest the git information so we can use it in the pom.
The git-commit-id-plugin adds this information into the pom as properties.
Manfiest EntriesNow that we have the all the information we need lets add it into the produced jars as MANIFEST entries.
Source JarWhen you publish your code to a artifact repository its important to also publish a source jar along with it.
This will allow consumers to view your code easily.
Further Reading:Maven Source PluginFat Jar?Sometimes it becomes necessary to rollup all your dependencies into a single jar file.
This can be useful when running scripts.
Fat jars also have dis-advantages.
You can have problems with class path conflicts, fileduplication and other files colliding,None of the tools for building fat jars are perfect but I've had the best luck with the shader plugin.
One thing it does really well is join spring, and services jars.
These jars usually have files that reside in the jar's MANIFEST folder.
But when building a fat jar they will often collide causing you to loose some necessary configuration information.
The shader plugin will avoid this by merging and combining these files as best as it can.
Sharing ConfigurationOk, so that's a lot.
Your pom is probably well over 1000 lines long now.
Not only that its very complicated and hard to reproduce.
Sure it does everything we want it to but what good is that if we can't easily maintain the configuration across lots of projects.
There are really two ways to go about doing this.
Neither is perfect but together they can get pretty close.
Parent PomsMaven support the concept of inheritance of pom configurations.
You can do this easily by specifying a parent element in your pom.
The child will now receive all the configuration from the parent.
You can override settings in the child and add additional content to your hearts content.
Parents are a great place to setup dependencyManagement and pluginManagement sections.
This ensures all your child modules will use the same version of a dependency and the same version of a plugins in their builds.
Some things to be aware of.
Its difficult to exclude something.
Meaning if the parent defines a plugin its not very easy for the child to turn that plugin off.
Therefore, be careful about adding ALL your configuration into a single monster parent.
That's where the second approach becomes usefulTilesMaven doesn't natively support a concept of composition of configuration.
But a third party tool, known as Maven Tiles brings that capability.
Maven tiles allow you to compose in project configuration:Note: You can not use tiles in a parent as a way for all the children to get the tile.
It doesn't work that way.
Tiles are meant to be used directly where they are needed.
Tiles of TilesInstead of having to list all the tiles you want in particular module you can compose the tiles together using a another tile of course.
Parent Pom's And TilesI recommend you put some common configuration in your parent poms and utilize tiles for most of the build configuration aspects.
DO include in your parent pom:Basic Project configuration (scm, url, issuement management etc)PluginManagementDependencyManagementProject encodingDO NOT include in your parent pom:DependenciesTilesDO use tiles on all your leaf projects to configure the build as needed in a repeatable way.
Finished ExampleNow that we have our tiles and some parent poms we have lots of wonderful features to help us be great programmers, active members of the community flexibilty for the future.
FlatteningOh wait there’s more!Now that you have your set of parents and all your plugins you have one last anoyance.
If someone goes to use your library as it stands they will also have to download all your parent poms.
On top of that, all your poms that you deployed to your artifact repository will still contain all your build configuration.
In order to clean up those poms and eliminate the need to download the parents we can flatten a pom.
Flattening a pom brings all the dependencies into a single pom.
It also removes a lot of unnecessary information from the pom when it is deployed.
Things like, plugins, properties, reporting, etc.
Further ReadingFlatten Maven PluginConclusionWe covered LOTS of stuff here and only scratched the surface of how you can customize the plugins we mentioned here.
Hopefully, this provides a building block for your own maven endeavors.
Don't forget to checkout all the code mentioned, along with a complete example, and tiles for each plugin, at my github repo:efenglu/mavenExample project containing a pom with lots of plugins – efenglu/mavengithub.
com.. More details