Maven is a build automation tool used to build primarily java projects. It addresses two aspects of a project: building and dependency management. It uses convention to accomplish its goals. For example, it expects the code to be in src/main/java, test code to be in src/test/java, so forth and so on. Similarly the output is of the build is written into the 'target' directory. You can see a sample project structure below:
Any additional steps that needs to be done for the project needs to be specified in the configuration file. The maven configuration file is the pom.xml which is present in the root directory of the project. POM stands for Project Object Model. The two important sections in the pom file are
1. The <dependencies> section which has the list of all dependent libraries with versions specified. When the maven builds the project for the very first time, all the dependent libraries are pulled from the maven repository and downloaded into the build machine.
2. The <build> section which has the configuration for building the project like the compiler version to use, tests to run, reports to be generated, code coverage etc.
When a maven command is executed, it goes through a process called the build lifecycle. There are three built-in build lifecycles in maven. They are:
default: handles project build and deployment
clean: handles project cleaning
site: handles the creation of project site documentation
Each build lifecycle goes through a set of steps called 'phases'. The most common default lifecycle has the following phases:
validate - validate the project is correct and all necessary information is available
compile - compile the source code of the project
test - test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
package - take the compiled code and package it in its distributable format, such as a JAR.
verify - run any checks on results of integration tests to ensure quality criteria are met
install - install the package into the local repository, for use as a dependency in other projects locally
deploy - done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
The build phases given above are executed in the same exact order. It is possible to specify which phase to run. For example, if we just want to compile the project and do not want to run the tests, we can specify the following command:
This will run the validate and compile phase. If we specify
then maven will run the validate, compile, test, package and verify phases. You should have realized that specifying a phase in the command will result in maven running all the other previous phases.
A plugin goal represents a specific task (finer than a build phase) which contributes to the building and managing of a project. It may be bound to zero or more build phases. A goal not bound to any build phase could be executed outside of the build lifecycle by direct invocation. The order of execution depends on the order in which the goal(s) and the build phase(s) are invoked. For example, consider the command below. The clean and package arguments are build phases, while the dependency:copy-dependencies is a goal (of a plugin).
mvn clean dependency:copy-dependencies package
If this were to be executed, the clean phase will be executed first (meaning it will run all preceding phases of the clean lifecycle, plus the clean phase itself), and then the dependency:copy-dependencies goal, before finally executing the package phase (and all its preceding build phases of the default lifecycle).
Moreover, if a goal is bound to one or more build phases, that goal will be called in all those phases.
Furthermore, a build phase can also have zero or more goals bound to it. If a build phase has no goals bound to it, that build phase will not execute. But if it has one or more goals bound to it, it will execute all those goals.
A note on dependency resolution
One of the convenience maven provides is dependency resolution. Lets say, the project needs a library A, but A is dependent on B which in turn depends on C. All we need to add in this case, is just A in pom.xml. Maven will take care of getting B and C. If there are conflicts(two libraries depend on different versions of the same library), maven uses the shortest path. To visualize this scenario, consider the following dependency relation:
A -> B -> Cv2 D -> Cv1
In the above scenario, maven will import Cv1 and ignore Cv2 because it is the shortest path.
Sometimes this could cause compatibility issues. If B is not compatible with Cv1, then we have an issue. In that case, we need to explicitly ask maven to choose Cv2 by asking it to ignore Cv1. This can be done in the pom.xml like the following:
<dependency> <groupId>x.y.z</groupId> <artifactId>D</artifactId> <version>1.0</version> <exclusions> <exclusion> <!-- declare the exclusion here --> <groupId>a.b.c</groupId> <artifactId>C</artifactId> </exclusion> </exclusions> </dependency>
Now maven will simply ignore the artifact C when including D and use Cv2 from B.
This is just a tip of an iceberg of what maven could do. There are lot more interesting things it could do to simplify the project build and automation.