Ads Top

Apache Maven Best Practices

Maven Best Practices




Make the build reproducible
  - Always specify a version for Maven2 plugins
  - Minimize number of SNASPHOT dependencies
  - Use dependency management section
  - Beware of relocation in maven repo
  - After a dependency modification, double check the produced artifacts
Use and abuse of modules
  - more “technical/layered”
  - more business oriented
Make the build maintainable
  - Prefer default directory layout
  - Avoid duplication by moving common tags to parent pom
  - Always specify a version of dependencies in a parent pom
  - Use Properties Liberally
  - Minimize the number of Profiles
Make the build portable
  - Don’t commit eclipse and maven artifacts
  - Don't modify pom/artifactsin your "enterprise" repository

Make the build reproducible

Always specify a version for Maven2 plugins

Wrong way
1
2
3
4
<plugin>
 <groupid>org.apache.maven.plugins</groupid>
 <artifactid>maven-surefire-plugin</artifactid>
</plugin>
Correct way
1
2
3
4
5
<plugin>
 <groupid>org.apache.maven.plugins</groupid>
 <artifactid>maven-surefire-plugin</artifactid>
 <version>2.3</version>
 </plugin>
This matters because if you leave out the version, maven2 defaults to LATEST  available.
So this can mean that
  • the result of the plugin can become unpredictable if the implementation of the plugin has changed (new/removed feature, …)
  • your project unknowingly became automatic beta testers for third party plugins if the latest version is a SNAPSHOT
Newer version fo m2clipse will this show as warnings in the console. See also the maven enforcer plugin

Minimize number of SNASPHOT dependencies

It’s strictly unrecommended to use SNAPSHOT version in your project dependencies since it’s never guaranteed that a SNAPSHOT version is available in any repository. This can lead to well-know build errors due to missing dependencies . So for projects that don’t belong to ‘you’… it’s preferable to use a real version.

Use dependency management section

Transitive dependencies is great feature… but in the end you want to be sure of the version you are using and shipping to production
Dependency Management allows to consolidate and centralize the management of dependency versions without adding dependencies which are inherited by all children. This is especially useful when you have a set of projects (i.e. more than one) that inherits a common parent.
Another extremely important use case of dependencyManagement is the control of versions of artifacts used in transitive dependencies. This is hard to explain without an example. Luckily, this is illustrated in the documentation.
1
2
3
    <dependencyManagement>
        <dependencies>
                 ...

Beware of relocation in maven repo

Relocating an artifact is changing the “maven id” (groupId:artifactId) of a project.
xstream:xstream
com.thoughtworks.xstream:xstream
One common pitfall in maven relocation is having double jars even with correct usages of dependencyManagement.
Use the m2clipse and his dependency hierarchy views, to detect and exclude the undesired artifacts.

After a dependency modification, double check the produced artifacts

A good habit is to double check your war/ear produced after the addition of a new dependency or the upgrade of an existing one.
2 greats to tools to help you in this

Use and abuse of modules

the modules can be more “technical”

1
2
3
4
5
<modules>
  <module>mymodule_api</module> <!-- service interface, value objects, exception -->
  <module>mymodule_impl</module> <!-- service & dao implementation -->
  <module>mymodule_web</module> <!-- web components, controllers, templates.. -->
</modules>
or

more business oriented

1
2
3
4
5
6
<modules>
   <module>contract_modules</module>
   <module>finance_modules</module>
   <module>time_modules</module>
   <module>agenda_modules</module>
</modules>

Make the build maintainable


Prefer default directory layout

this will make the plugin configuration more easy :
src/main/java         Application/Library sources
src/main/resources    Application/Library resources
src/main/webapp       Web application sources (for war packaging)
src/test/java         Test sources
src/test/resources    Test resources

Avoid duplication by moving common tags to parent pom

Do you really want to say that you compile for 1.5 jdk in all your projects ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    <!-- Use Java 1.5 -->
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>2.3.1</version>
        <configuration>
            <fork>${javacFork}</fork>
            <executable>${javacExecutable}</executable>
            <verbose>${javacVerbose}</verbose>
            <compilerVersion>${jdk.version}</compilerVersion>
             <source>${jdk.version}</source>
            <target>${jdk.version}</target>
        </configuration>
    </plugin>
move this in a parent pom !
for example, in
  corporate_base_pom  ->  app_basecom  ->       app_module1         -> app_sub_modules

    jdk 1.5                    technical                application                     
 enterprise repo             dependencies               dependencies
                             (spring,...)               (app_module2,app_module3,..)

Always specify a version of dependencies in a parent pom

prefer a central place for version definition.
Don’t specify version in a specific sub-modules

Use Properties Liberally

Grouping Dependencies with properties… to avoid copy/pasting the version everywhere.
and help upgrading easily to 3.0.5.RELEASE😉
And move this to a parent pom : cfr Avoid duplication move to parent pom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<properties>
    <spring.version>3.0.0.RELEASE</spring.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
         <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
         <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>

Minimize the number of Profiles

Profiles can help a lot… but will inevitability complexify the process.
So don’t use profiles if for example adding a new project with and assembly will do the trick. best practices
  • The build must pass when no profile has been activated
  • Use profiles to adapt to build-time context, not run-time context, and not (with rare exceptions) to produce alternative versions of your artifact
Remark: The support for profiles outside of the POM or the settings.xml has been removed in Maven 3.x.

Make the build portable

don’t commit eclipse and maven artifacts

to make the checkout easier… avoid commiting the following files and directory
.project
.classpath
.settings
.wtpmodules
target
these files are often :
  • referencing local settings like JRE name/path/…
  • specific to a version of plugins (wtp,…)
so let m2clipse handle this and maintain/generate the .project, .classpath,…

Don’t modify pom/artifacts in your “enterprise” repository

It’s always tempting to fix a pom or a jar in the central repository… don’t do this.
Identify it a special version in your repo ‘artefact-1.0.5.corporatepath’ or manage the correct exclusions.
see log4j example

  1. Don’t use deprecated references like ${artifactId} or ${pom.artifactId}. Use the new ${project.artifactId} syntax. Note that this syntax follows the XML document structure, which makes it easy to remember and predict the value that the reference will result in.
  2. Try to avoid using inherited properties. Developers can easily forget that a certain property is used by a child POM and change the value breaking the build in an unexpected place. Secondly, its quite annoying not to be able to easily lookup a property without having to find and examine the parent POM.
  3. Use the dependencymanagement section of the parent pom to define all dependency versions, but do not set a scope here so that all dependencies have scope compile by default.
  4. Use properties to define the dependency versions. This way you can get an overview of all versions being used without having to scroll through multiple pages of dependency sections.
  5. Use the pluginmanagement section of the parent pom to define versions for *all* plugins that your build uses, even standard maven plugins like maven-compile-plugin and maven-source-plugin. This way your build will not suddenly behave differently when a new version of a plugin is released.
  6. When using a parent POM that is not located in the directory directly above the current POM define an empty relativePath element in your parent section.
  7. Use the dependency plugin to check your project for both unnecessary dependencies and undeclared-but-used-none-the-less dependencies. The goal is called ‘analyze’, so run the following command on the console: “mvn dependency:analyze”
  8. Make sure the pom files contain all the repository references needed to download all dependencies. If you want to use a local repository instead of downloadin strait from the internet then use the maven settings file to define mirrors for the individual repositories that are defined in the poms.
  9. If you use Nexus, then do not create repository groups containing both hosted and proxied repositories. This will dramaticly reduce the responsiveness because Nexus will check the remote locations of the proxied repositories even if a hosted repository contains the requested artifact.

No comments:

Powered by Blogger.