A A Brief Tour of Maven↓
In this chapter we’ll discuss the Maven build tool and some of the basics of configuration and usage. Maven is what Lift uses for build management, so becoming acquainted with Maven is important to getting the most out of Lift. If you’re already familiar with Maven you can safely skip this chapter.
A.1 What is Maven?
Maven is a project management tool, as opposed to simply a build tool. The Maven site describes the goals of Maven as:
Make the build process easy
Provide a uniform build system
Provide quality project information
Provide guidelines for best practices
Allow transparent migration to new features
As a project management tool, Maven goes beyond just controlling compilation of your code. By default, Maven comes equipped not only to perform development-centric tasks, but it can generate documentation from your code and for your project website. Everything in Maven is controlled via the pom.xml (Project Object Model) file, which contains both information and configuration details on the project. We’ll be covering some of the basic aspects of the POM through the rest of this chapter.
A.2 Lifecycles, Phases and Goals
Maven is designed around the concept of project lifecycles. While you can define your own, there are three built-in lifecycles: default, clean and site. The default lifecycle builds and deploys your project. The clean lifecycle cleans (deletes) compiled objects or anything else that needs to be removed or reset to get the project to a pristine pre-build state. Finally, the site lifecycle generates the project documentation.
Within each lifecycle there are a number of phases that define various points in the development process. The most interesting lifecycle (from the perspective of writing code) is default. The most commonly used phases in the default lifecycle are:
compile - compiles the main source code of the project
test - tests the main code using a suitable unit testing framework. These tests should not require that the code is packaged or deployed. This phase implicitly calls the testCompile goal to compile the test case source code
package - packages the compiled code into its distributable format, such as a JAR. The POM controls how a project is packaged through the <packaging/> element
install - installs the package into the local repository (see section A.3↓), for use as a dependency in other projects locally
deploy - used in an integration or release environment. Copies the final package to the remote repository for sharing with other developers and projects.
Maven is typically run from the command line by executing command “mvn <phase>”, where <phase> is one of the phases listed above. Since phases are defined in order, all phases up to the one you specify will be run. For example, if you want to package your code, simply run “mvn package” and the compile and test phases will automatically be run. You can also execute specific goals for the various plugins that Maven uses. Execution of a specific goal is done with the command “mvn <plugin>:<goal>”. For instance, the compile phase actually calls the compiler:compile goal by default. A common usage of executing a goal for Lift is the jetty:run goal, which compiles all of your code and then runs an instance of the Jetty web server so that you can exercise your app. The jetty plugin is not directly bound to any lifecycle or phase, so we have to execute the goal directly.
One final note is that you can specify multiple phases/goals in one command line, and Maven will execute them in order. This is useful, for instance, if you want to do a clean build of your project. Simply run “mvn clean jetty:run” and the clean lifecycle will run, followed by the jetty:run goal (and all of the prerequisites for jetty:run, such as compile).
Repositories are one of the key features of Maven. A repository is a location that contains plugins and packages for your project to use. There are two types of repository: local and remote. Your local repository is, as the name suggests, local to your machine, and represents a cache of artifacts downloaded from remote repositories as well as packages that you’ve installed from your own projects. The default locations of your local repo will be:
Windows: C:\Documents and Settings\<user>\.m2\repository
You can override the local repository location by setting the M2_REPO environment variable, or by editing the <home>/.m2/settings.xml file.
Remote repositories are repositories that are reachable via protocols like http and ftp and are generally where you will find the dependencies needed for your projects. Repositories are defined in the POM; listing A.3↑
shows the definition of the scala-tools.org release repository where Lift is found. Maven has an internal default set of repositories so usually you don’t need to define too many extra repos.
<name>Scala Tools Maven2 Repository</name>
As a final note, sometimes you may not have net access or the remote repos will be offline for some reason. In this case, make sure to specify the “-o” (offline) flag so that Maven skips checking the remote repos.
Plugins add functionality to the Maven build system. Lift is written in Scala, so the first plugin that we need to add is the Maven Scala Plugin; this adds the ability to compile Scala code in your project. Listing A.4↓
shows how we configure the plugin in the pom.xml file for a Lift application. You can see the Scala plugin adds a compile
goal for the build phase, which makes Maven execute this plugin when those goals are called (explicitly or implicitly). In addition, the configuration element allows you to set properties of the plugin executions; in this case, we’re explicitly specifying the version of Scala that should be used for compilation.
Configuring the Maven Scala Plugin
Dependency management is one of the more useful features of Maven. Listing A.5↓
shows a declaration of the Jetty dependency for the default Lift application. The details of the specification are straightforward:
The groupId and artifactId specify the artifact. A given group may have many artifacts under it; for instance, Lift uses net.liftweb for its groupId and the core artifacts are lift-core and life-util
The version is specified either directly or with a range, as we’ve used in this example. A range is defined as <left>min,max<right> where left and right indicate an inclusive or exclusive range: [ and ] are inclusive, ( and ) are exclusive. Omitting a version in a range leaves that portion of the range unbounded. Here we configure the pom so that Jetty 6.1.6 or higher is used
The scope of the dependency is optional, and controls exactly where the dependency is used. In this case we specify a test scope which means that the package will only be available to test phases
A.5.1 Adding a Dependency
As an example, let’s say that you’d like to add a new library and you want Maven to make sure you’ve got the most up-to-date version. We’re going to add Configgy as a dependency. Configgy is “a library for handling config files and logging for a scala daemon. The idea is that it should be simple and straightforward, allowing you to plug it in and get started quickly, writing small useful daemons without entering the shadowy world of java frameworks.”
First we need to tell Maven where we can get Configgy, so in the <repositories> section add the following:
Then in the <dependencies> section add:
Adding the Configgy dependency
That’s it, you’re done. The next time you run Maven for your project, it will pull down the Configgy jars into your local repository. Maven will periodically check for new versions of dependencies when you build, but you can always force a check with the “-U” (update) flag.
A.6 Further Resources
Obviously we’ve only scratched the surface on what you can with Maven and how to configure it. We’ve found the following set of references useful in learning and using Maven:
A.7 Project Layout
One of the things that allows Maven to work so well is that there is a standardized layout for projects. We’re not going to cover all of the standard locations for parts of your Maven project, but we do want to highlight a few locations that are important to Lift applications specifically:
<application_root>/src/main/scala This directory is where you place your Scala source, such as snippets, model objects, and any libraries you write. The subfolder structure follows the traditional Java packaging style.
<application_root>/src/main/resources This directory is where you would place any resources that you want to go into the WAR file. Typically this is used if you want to add entries to the META-INF directory in the WAR, since normal web resources should be placed under the webapp/WEB-INF directory.
This is a special location for templates. As we discuss more in sections 5.1↑
, templates placed in this directory cannot be viewed directly by clients, but are available to other templates.
<application_root>/src/test/scala This directory is where you can put all of your test code. As with src/main/scala, the subfolder structure follows the traditional Java packaging style.
(C) 2012 Lift 2.0 EditionWritten by Derek Chen-Becker, Marius Danciu and Tyler Weir