Multi-module projects usually have rather flat structure with a single top-level pom.xml file. That file than lists sub-modules and defines versions of dependencies and/or plugins inherited in these sub-modules. This is well-known thing - what is less known is that these roles of pom.xml can be separated.
Aggregator
A top-level module serving for joining multiple modules under one roof is called aggregator. Its purpose is only represent more or less independently existing modules as a parts of a greater whole.
Example of aggreator pom.xml:
<project xmlns="..."> <modelVersion>4.0.0</modelVersion> <groupId>org.bithill</groupId> <artifactId>aggregator</artifactId> <packaging>pom</packaging> <version>1.0</version> <name>Project Aggregator</name> <modules> <module>project1</module> <module>project2</module> </modules> </project>
Parent
As you see, aggegator does not include any information about dependencies. The source of to-be-inherited information about libraries and plugins is known as a parent POM. It includes all the properties, dependencyManagement and pluginManagement sections stating versions of projects dependencies and plugins and some plugin configurations when it comes handy. Ideally this information should be de-duplicated and inherited by plugins is sub-modules, but that does not apply to reporting plugins.
Example of parent pom.xml:
<project xmlns="..."> <modelVersion>4.0.0</modelVersion> <groupId>org.bithill</groupId> <artifactId>parent</artifactId> <packaging>pom</packaging> <version>1.0</version> <name>shared parent</name> <properties> <java.version>1.6</java.version> <spring.version>3.0.2.RELEASE</spring.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>${java.jdk.version}</source> <target>${java.jdk.version}</target> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
Using Aggregator and Parent POM Together
So we showed that we can have two different artificial POM files serving two different roles - aggregation and inheritance.
Diagram of relationships in a project consisting of two sub-modules:
The last missing thing in the picture is an example of sub-module's pom.xml. As you see, no dependency or build plugin need to define their version - that is inherited from parent POM. Parent's pom.xml is deployed in Maven repository, but Maven's default relative path to parent is ".." - to avoid aggregator being used as parent, property relativePath must be set empty, this is probably the only trick here.
<project xmlns="..."> <modelVersion>4.0.0</modelVersion> <groupId>org.bithill</groupId> <artifactId>project1</artifactId> <packaging>pom</packaging> <version>1.6-SNAPSHOT</version> <name>Project #1</name> <parent> <groupId>org.bithill</groupId> <artifactId>parent</artifactId> <version>1.0</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> </plugins> </build> </project>