Filed under: Maven, — Tags: Almost simplest possible, Simplest possible — Thomas Sundberg — 2013-02-24
Maven is a build tool for Java. You define a project with Maven, build and test your product.
Usually you hate Maven or you love it. Before you have understood how things work, you usually hate it. If you persist, then there is a chance that you will like it.
I have met many developers that claim that Maven is either "To complicated" or "Impossible to understand".
I have even been asked "Don't you feel dirty using Maven?" during a session at an Agile conference. My answer was "No, why should I feel dirty for using a good tool?" This question and an opinion stated at another conference triggered me to write this. I aim to show you how Maven can solve some problems, using baby steps and starting from the simplest possible solution that could work. My goal is to write an easy to understand getting started guide. If you think I take a too large step somewhere on the way, please give feedback and tell me where I take a large step. If you think my steps are to small, then you probably already know enough about Maven so you don't need this introduction.
Learning from Test Driven Development, TDD, I will start with the absolute smallest, and therefore the simplest, possible example that could work. The only thing I define is the project file and the directories needed.
Our starting point is a file called
pom.xml. It is located in the root of a directory that will contain
our project. The name of the directory would be good to call something similar to the project. This is not
mandatory; it will just assist you when you browse your files.
Why the name pom? Pom is an abbreviation for Project Object Model and it describes all properties for a project that can be built using Maven. Other tool vendors can use the pom to create their own project files. IntelliJ IDEA is one example.
The content of the simplest possible solution that could work is this:
<project> <modelVersion>4.0.0</modelVersion> <groupId>se.somath.maven</groupId> <artifactId>simplest-possible</artifactId> <version>1.0.0-SNAPSHOT</version> </project>
This is six lines of xml and it is the smallest possible pom that can work. The first and last lines define the start and ending of the project. Xml may be a reason why some people don't like Maven, they hate xml.
I know a developer that claims that he can't read xml. There are to many details for him, he gets lost and don't see the wood for the trees. This of course unfortunate because Maven uses xml to define its projects.
The second line defines that this is version 4.0.0 of the model version. This line is mandatory, Maven will fail the build if it is missing. There is actually only one version of the model and it is called 4.0.0. Having this version is a preparation for changing the definition of the pom in the future.
The third line defines the group id that this project will be a part of. Group id could be defined any way you like.
The Maven convention is to name the group id as you would name packages in Java. I have named it
since I have a domain called
The fourth line defines the artifact id that the specific artifact that is the deliverable from this project. It may
also be named anything you like. It is of course preferable to name it so you later understand what it actually
represents. I called it
simplest-possible. This will remind me that this is the simplest possible
example I could create.
The fifth line defines the version for this project. This may also be called anything. I have chosen the version
1.0.0 since this is the first version. I am also following the convention that any artifact not released will be
appended with the suffix
-SNAPSHOT. The word SNAPSHOT has a special meaning in Maven and it is there to
clearly mark that the version you currently are looking at is work in progress. Snapshots are treated differently
than released artifacts.
The triple group id, artifact id and version create what sometimes is refereed to as Maven coordinates or Maven GAV. They clearly define an artifact.
With the project definition done, we need to create the structure where the source code should be stored. Storing the source code in the predefined directories that Maven expects is mandatory unless you want to fight Maven. And you don't want to fight Maven. You will get hurt and you will loose.
The Maven convention is to store everything used to build the project in a directory called
src as in
source. For a conventional Java project, you divide the Java code and anything else in two separate directories. The
java directory should contain the Java source code. If you use packages, create the packages in the
root of the java directory. Anything else that you want to be included in the project and available on the classpath
should be stored in the directory
resources. The resources directory may also contain sub-directories
as well as Java packages. Whatever you store here will be made available to the project on the class path during the
execution of your project. An example of the smallest possible directory structure that could work is:
simplest-possible |-- pom.xml `-- src `-- main |-- java `-- resources
We see that the root directory is called
simplest-possible, we notice that a file called
pom.xml is stored in the root. We also notes the directory
src and it's sub-directories
If you store some Java source code in
simplest-possible/src/main/java and executes
compile then will the project be compiled. If you instead execute
mvn package then the project
will both compiled and packaged into the default packaging type
jar, i.e. a jar file containing the
compiled classes will be created.
All files created when executing Maven are stored in a directory called
target. This is where anything
generated ends up, this is where the packaged artifacts end up, this is the output directory for the compiled
classes. A lot of things end up here but, by definition,
target never contains anything you can't
This is an example of the simplest possible solution that could work and that relies on all Maven defaults. It is also an example where I used two different life cycles to achieve two different results.
Maven is very much about conventions. Convention over configuration is a key feature and a mantra that is repeated all the time. There is a default packaging type, jar. If you follow the convection of file locations, then you need little configuration. In the case above, six lines of xml.
Using Maven will force you to follow what sometimes is called the Maven way. That is using the default that the Maven team has defined and almost never deviate from them. This also implies following the defaults for any plugin that you may choose to use. The Maven ecosystem contains many plugins.
Another key feature of Maven is its ability to handle dependencies. If you need a dependency in a project, all you have to do is to define is using the Maven coordinates as described above. If the dependencies are available in any repository Maven has knowledge about, it will be downloaded.
There is a default repository that every Maven installation is able to use. It is called The Central Repository. It contains many open source projects and all you have to do to use a dependency is to define it in the pom and be connected to the Internet. Any dependencies that you don't have cached locally will be downloaded.
The dependencies Maven downloads will be stored in a local cache on your computer. The default location is in you
home directory in a directory called
.m2/repository. It is
/Users/tsu/.m2/repository on my
Our previous example didn't have any possibility to use any unit tests. I hadn't defined any testing framework dependency. In the following example, I will add it and show how easy it is to define a dependency. I will also show where any tests should be located in the project.
The first thing I want to show is a new dependency. I define a pom like this:
<project> <modelVersion>4.0.0</modelVersion> <groupId>se.somath.maven</groupId> <artifactId>simple-with-junit</artifactId> <version>1.0.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> </dependencies> </project>
The addition here is the
dependencies section. It is here that we add any dependencies we want in our
I just added a dependency to
JUnit, a well-known testing framework. **
The next thing should be to define a home for the test classes and the test resources. The Maven convention is that
they shall live in
src/test. A file tree that satisfies this looks like:
simple-with-junit |-- pom.xml `-- src |-- main | |-- java | `-- resources `-- test |-- java `-- resources
If you add test classes in
src/test/java, they will be executed during the test phase in a build. Any
resources you add in
src/test/resources will be available on the class path during the test. Anything
test will not be included in the final artifacts that you deploy from your project.
A gothcha that might surprise you is that you have to name the test files according to a specific pattern so they
can be picked up properly. Add the suffix
Test to the test class name and you will be fine.
** Note that it would be accurate to add a dependency scope to this dependency, but I choose not to do so as it would make my example unnecessary complicated and my goal is simplicity. The scope should be
The final thing I want to mention are the build phases defined in Maven. There are a total of 23 different phases in the default life cycle. This is a lot, but in order to get started you only need a few of them.
All build phases are executed in a specific order. There is no way to do some of them and skip some of the previous phases. As with age, you can't have your 100th birthday without having your 99th.
This means that you will probably end up using one or two phases in your daily work. I usually use
install like this:
mvn clean install
You can stack the phases after each other. The execution above will first clean all created files, i.e. delete a
target directory, and then execute all build phases to install. This means compile, test, package and
finally install the generated artifacts in your local repository. The complete list of build phases can be found in
online Maven documentation.
Maven is a tool that is very capable and therefore it would be possible to write longs book about it. There are long books written, but they try to describe everything. The goal here is to tell you enough to get started and hopefully see that it doesn't have to be very complicated. This means that I have to leave out many details and skip large sections. Things that are interesting but out of scope includes these things:
The list above is not complete. Many other things can be described but they are, as stated earlier, out of scope.
I wish to thank Johan Helmfrid and Johan Karlsson for the feedback. It is, as always, a pleasure.