Remember the complaints we had at the end of exercise 1d:
- If we have more than one dependency, how annoying will it be to find download links for all of them?
- How do we know what versions are available for dependencies?
- What if our dependency has dependencies? How do we download those?
Build tools such as Maven, Gradle, and Bazel attempt to make it easier to build Java code while helping to handle these problems.
They work slightly differently and have different capabilities and syntax, but the core functionality of getting dependencies and building are mostly the same. For this exercise, we're going to focus on Maven.
Maven is complicated, so this isn't going to be very short. However, I've attempted to pick only the most useful things to note in this document.
Maven is a software application that builds Java code, but it is also a framework that defines the de-facto standard for how to organize and distribute Java jars for other developers to use.
Maven breaks up a build into units of work called "Projects". This can be slightly misleading because it is a smaller unit than what most people think of when talking about a "Software Project". A single project can have many sub-projects, in what is known as a multi-project or multi-module project.
When most developers to refer to them, as well as in much of resources available online, plus in the official Maven documentation, the sub-projects are referred to as modules. However, most of the actual output of the Maven commands as well as the Maven command-line arguments refer to them as projects. For the purposes of this training, we may refer to sub-projects as modules, to be consistent with other documentation and common usage. However, if we refer to Maven projects in general, then we mean both projects and sub-projects/modules.
For example, the widely used Log4j logging framework is considered one software project by most people. It exports several modules, such as:
log4j-api
- The module that declares what the generic API and interfaces looks like.log4j-core
- The default logging code that logs to filelog4j-jul
- An optional module that provides an adapter for thejava.util.logging
API.log4j-mongodb
- An optional module that logs to MongoDBlog4j-jmx-gui
- An optional dependency that provides a GUI for remotely editing configuration- And much much more.
Maven introduces the concept of an Artifact. An artifact is a file generated by a Maven project, along with some
metadata. A jar
that can be depended on by another project, for example, is probably the most common type of artifact.
A Maven project can generate one or more artifacts.
The metadata associated with a Maven artifact include three elements used to identify the artifact. These elements are known as Maven Coordinates.
The maven coordinates are the groupId
, artifactId
, version
.
The groupId
is the identifier of the group that owns the project. This key makes it easier to find all of the
artifacts that are published by a group. The groupId
follows the same naming rules as Java packages - they're the
reverse of a URL that points to a group.
For example, Guava is released under groupId
com.google.guava
.
Most of IMC's artifacts are com.imc.xxxxx
.
The artifactId
is the identifer of the module within the group. The best practice is to use the actual project name
as a prefix, which makes it easier to find the artifacts. This is why the slf4j
modules all begin with slf4j-
.
The version
defines the specific version of the project that built this artifact.
Note that Maven defines a set of version specifications, as well as the concept of releases and snapshots, that we'll go into later.
The version can be an arbitrary string, but Maven has rules for determining whether a version is a "newer" version when compared to another version.
An artifact is often described in the format: groupId:artifactId:version
. For example, a recent Guava artifact
is com.google.guava:guava:31.1-jre
Maven also introduced (or at least popularized) the concept of an artifact repository in the Java ecosystem. Rather than every project hosting its own JARs somewhere on its website, Maven hosts a centralized repository called "Maven Central" that everyone uploads their artifacts to. Then all you need is to know the group and artifact IDs in order to find and download an Artifact.
At IMC, we host an internal repository for our JARs, as well a mirror of Maven Central.
Maven then handles downloading dependencies from the repositories, as well as deploying your artifacts to the repository.
As you noticed from Lab 1, if you're just building Java files with javac
and jar
, you can tell them to generate
your class files and jars basically wherever, and you just need to correctly specify where everything is when building.
However, as we mentioned, this can be very annoying to work with, if generated class files and jars are just floating about, mixed in with your source code. Additionally, without a standard directory layout, it can be very difficult to navigate code in a new project and understand how everything works.
Maven specifies a Standard Directory Layout that it is highly recommended to follow, for the above reasons.
By a miraculous coincidence, this is almost exactly the same directory structure we used in exercises 1b-1d. Though
Maven uses /src/{main,test}/java
for source code, in case you have files that are not Java code.
The relevant parts of the directory structure are as follows:
Path | Description |
---|---|
src/main/java | Application/Library sources |
src/main/resources | Application/Library resources |
src/test/java | Test sources |
src/test/resources | Test resources |
target | Generated artifacts |
target/generated-sources | Generated source code |
target/generated-test-sources | Generated source code used in tests |
target/classes | Compiled .class files |
target/test-classes | Compiled test .class files |
The Project Object Model, or POM is an XML file (pom.xml
) that contains information about the project, the coordinates
for the project, configuration and properties, details about how to build the project, the project's dependencies, the
plugins and goals that can be executed, build profiles, and so on.