Additional required other products

User level

Required

Sample files

Maven is hardly the new kid on the block when it comes to build automation and project management. It's been circulating in the Java world for many years now and is the cornerstone of many great enterprise projects. The power it offers for dependency management and the conventions it advocates ensure its place in many enterprise developers' toolkits.

Luckily, thanks primarily to the hard work of the open source community, and in particular Marvin Froeder (@velobr), developers in the Flash world can to leverage Maven in their Flex, AS3 and AIR builds. This is in the form of the Flexmojos Maven plug-in.

This series of articles will cover the makeup of Maven and how to use the Flexmojos plug-in to manage every aspect of your Flex project—from building and testing, through to ASDoc support and code coverage reports.

This is the first article in a three-part series. This article will present an overview of Maven and terminology, Flexmojos, and a simple Flex example to get you started. The second article in the series will cover project setup and automation, Flashbuilder integration, unit testing, and multifaceted projects. The final article will go into depth on Nexus, RSLs, runtime modules, deployment and build profiling.

The case for Maven

Why do developers even need build tools?

One of the biggest movements to gain traction within software development over the past decade has been agile software development. For those not accustomed to it, agile promotes development through small, quantifiable increments. In order to deliver while the project is constantly in flux, continuous integration is typically employed to ensure stability and quality of each build. As such, there needs to be both a blueprint to build the product and a mechanism to automatically test the application after every successful build.

On top of enabling regular automated builds, using a build or control script also yields other rewards. First, it ensures that there is responsibility and ownership from every developer. As each check-in to source control management (hereafter SCM) prompts a build, every developer is responsible for ensuring the build's success. Second, it provides a single point of contact for the actual project makeup. Instead of build parameters being slightly different on each developer's machine or IDE, each developer builds from the same script, ensuring the build (and, indeed, project setup) process is part of SCM. Finally, and most importantly, it ensures that the packaging and deployment of the product is continually being tested and refined, so that the crucial act of moving to production is well established.

So why Maven?

Many in the Flex world are comfortable with Apache Ant—a straightforward task-runner tool that allows developers to write out a series of tasks in the form of the various processes to be run and the parameters to pass.

Maven, on the other hand, reduces clutter in the build by assuming conventions in each process. Instead of explicitly stating what must occur during each action—such as the parameters to the compiler—Maven plug-ins determine default settings for you, negating needless configuration. Unlike Ant, it manages dependencies in local repositories and downloads whatever it doesn't have on the fly. Maven manages projects through the use of POM (Project Object Model) files (more information to come in the section "The basics") that allow for the sharing and inheritance of project settings between modular applications.

What about building Flex, AIR and AS3 projects?

In order to build and manage Flex, AIR and AS3 projects with Maven, you need the Flexmojos plug-in. Like all Maven plug-ins, it is downloaded during the execution of a project when it is referenced and is run as required. It converts settings into arguments for mxmlc, compc—the SWF and SWC compilers, respectively. More than just mere building, it also provides automated unit testing, code coverage reports, runtime modules, RSL support, HTML wrappers, WAR deployment and ASDoc output.

For the latest Flexmojos release, site docs and goals listing (v4.0-RC2 at time of writing), see the plug-in info.

Note: Although Flexmojos is hosted on Sonatype.org, it is not affiliated with them in any other way. Flexmojos is an open source initiative that was pioneered by a developer who coincidentally also works for Sonatype (Marvin Froeder).

The basics

Before diving straight into Flexmojos, I'll cover the Maven basics.

Convention over configuration

By nature, Maven promotes convention over configuration. Put simply, if you don't explicitly state how something should be done, Maven will follow convention. Hence it behooves every new developer understand the conventions therein. Indeed, this is why developers new to Maven often find themselves struggling needlessly—Maven isn't very forgiving for those who skim the manual. (Trust me—this comes from painful experience.)

Hierarchical

Maven is formulated through hierarchies. In terms of projects, each project may have a collection of modules, which themselves may have modules to build, and so on. Modules can inherit settings from parents and may override them as required. In terms of dependencies, Maven will look up a collection of remote repositories to find and download a dependency needed to execute. Each of these repositories themselves may proxy out to other remote repositories.

The Project Object Model (POM)

A Maven script is called a POM—the Project Object Model. It is an XML document that outlines the various actions that can be performed on the project. By default, Maven looks for pom.xml in the current folder of execution.

Note: this is just a simple POM to illustrate structure, it is not a full implementation.

In the above example, you can see some basic features:

This script defines the groupId , artifactId , version and packaging for the project (more info on this in the section "Artifacts").

It illustrates providing a source location for Flexmojos to look for .as and .mxml files. In this case I use the convention src/main/flex (more on this in the next article).

It details the Flexmojos plug-in that will compile the SWF file (using mxmlc).

It shows a single project dependency on the Flex 4.5.1 framework itself.

Note: All POMs inherit from the Maven super POM, which contains many of the conventions Maven assumes. Upon execution, Maven will merge configuration from your POM to the super POM. For more details on the super POM, take a look at the Maven guide.

Artifacts

Each build generates an artifact (typically one per POM). An artifact is the result of an action or process. Artifacts are classified by groupId , artifactId , version and packaging (type). The build process generates artifacts and outputs them into the target folder.

The groupId is similar to a package name—typically the reverse domain of the institution and product. The artifactId is the name or identifier of your product. The version is either the SNAPSHOT or release numbering of your product (see section on "Versioning"). The packaging (type) specifies what type of package the artifact represents—for Flex projects you'll typically use any of swc , swf , air , pom or zip .

Note: You may be wondering why the flex-framework is a POM, rather than a SWC or SWZ. That's because the Flex framework is made up of a collection of dependencies. What dependencies, you say? Open up Flashbuilder, create a new Flex project, and expand the Flex SDK listed directly under your project (see Figure 1). For convenience, there's a POM that bundles these dependencies.

It is up to you to decide on the relevant groupId , artifactId and version for your projects. As always, consistency is the key.

Versioning

One key Maven convention is the difference between releases and snapshots. While a project is in flux, each version is suffixed with a -SNAPSHOT to specify that it is not a stable version. When building, Maven will treat that as a snapshot build, and continue to deploy successive iterations to the same location in your repository (using timestamp suffixes on each successive deployment). Releases, on the other hand, are built and deployed once per version. As a consequence, you can depend on release versions of artifacts and rest assured that they will never change.

Generally speaking, your projects will be built as snapshots (eg. 0.0.1-SNAPSHOT) until you are ready to do a release, where you will build and deploy once without the suffix and then increment the version and reapply the suffix (for instance, deploy 0.0.1 and then up the version to 0.0.2-SNAPSHOT).

Apart from that, the actual version numbers are arbitrary and up to you. A general rule of thumb is to keep a major, minor, and incremental number in there (eg. 1.3.9)—it helps if, in the future, you want to specify ranges of dependencies. You can also apply a qualifier if need be (eg. 0.5.2-alpha3).

Repositories

Every install of Maven creates a local repository. This is typically named .m2 in the user's home directory. On Windows 7, it's in /Users/[username]/.m2; on OS X and Linux, it's ~/.m2.

Artifacts are filed away in repositories following another Maven convention as follows:

/groupId/.../artifactId/version/artifactId-version.extension

As shown in Figure 2, the location of the flex-framework dependency from above would be:

As mentioned earlier, this flex-framework POM is just another artifact that simply bundles dependencies for you. As such, it is filed away in your repository along with everything else.

Running Maven

Maven is a command-line process. While there are third-party tools that integrate Maven into modern IDEs, this article will only focus on command-line Maven. (Stay tuned for the next article, which will cover Flashbuilder integration.)

After installing and setting up the Maven binaries on your command-line path (PATH), you can build projects by going into their root folder (where the pom.xml file lives) and simply running:

mvn install

This will kick off the process to create an artifact from the POM, save it in the target folder, and install it in your local repository. If any dependencies are missing along the way, Maven will attempt to download them and add them to your local repository.

Terminology

Goals, phases, and lifecycles

Maven processes are comprised of three parts: goals, phases, and lifecycles.

A goal is a single action. This can be as simple as installing an artifact into the local repository, and as complex as running a series of automated unit tests.

A phase is a collection of goals. Any phase may have one or more goals bound to it. Phases include cleaning ( clean ), compilation ( compile ) and testing ( test ).

A lifecycle is a sequence of phases. When you run a phase from one of the typical lifecycles, you run every phase up to and including that one. The two main lifecycles are the clean and the default lifecycles.

The actual phases within each lifecycle and the goals therein come from both the packaging (for example, jar , swc , or swf ) and the list of plug-ins within your POM.

The odd couple: clean & install

The most common execution of Maven is a clean install. That is:

mvn clean install

This simple command runs the clean lifecycle and then the default lifecycle, up to and including the install phase. In essence, it runs the following:

First, Maven checks the current folder for any pom.xml. From that, it determines which phases and goals to run based on both the packaging ( jar , swc , swf , and so on) and any plug-ins defined.

Then it runs the clean phase, which by default simply removes all files from the target folder in the current directory.

Maven then begins the build by running through the default lifecycle up to and including the install phase. By default, this includes validation, compilation, and testing. However, the particular packaging and/or plug-ins described in the POM will determine the actual goals and phases that come before and during the actual install.

Plug-ins

Plug-ins provide goals and optionally bind them to new or existing phases. In fact, Maven is composed entirely of plug-ins. All the core functionality within the default lifecycle is provided for by core plug-ins.

For example, the compiler plug-in defines two goals: compile and testCompile . You can see their settings via the compiler plug-in page. The two goals are bound to two separate phases, compile and test-compile , respectively.

All goals when run on their own are prefixed with their relevant plug-in. For example, running a single goal involves the following pattern:

plugin:goal

So compiling the unit tests, for example, would be done via:

mvn compile:testCompile

Indeed, Flexmojos itself is a plug-in that binds various phases to the build, both within the SWC/SWF packaging and within the plug-in itself. If you want to run a goal outside the default lifecycle—say, the ASDoc generation for example—you can run it explicitly as follows:

mvn flexmojos:asdoc

If you wanted to do a clean install and then run ASDoc after, you'd use:

mvn clean install flexmojos:asdoc

The Flexmojos plug-in

As previously mentioned, Flexmojos is a plug-in, which by virtue of adding SWF and SWC packaging to your POM, ensures that the Flexmojos lifecycle will be employed during a standard build.

Figure 3 shows the default SWF and SWC lifecycles in terms of the plug-ins and goals executed:

The AIR lifecycle is the same as the SWF with an additional phase for AIR signing after the tests run. For more information on the Flexmojos lifecycle, check out the reference documentation.

Dependencies and repositories

One of Maven's strong suits is in its handling of dependencies.

A dependency is an artifact

Each dependency is listed in terms of the artifact that represents it. For example, a dependency on FlexUnit 4.0-RC-1 looks like:

As such, POMs can reference artifacts from other POMs and within a single build Maven will automatically determine the build order. I'll cover this in depth in the next article.

The local repository

Dependencies are hosted in repositories. Each install of Maven creates a local repository, typically in ~/.m2. As dependencies are discovered in your POMs, Maven checks the local repository first before attempting to download it from a remote repository.

Every time you install your project, you are actually adding the generated artifact into your local repository.

Repository lookup

When building, if Maven cannot find a dependency, it will look in any remote repositories you've defined. To find repositories, Maven looks for repository and pluginRepository definitions in the following areas:

at the executing POM

at the parent POM (if any)

at any settings.xml used in the build (configured via the -s command-line option)

at the user level settings (~/.m2/settings.xml)

at the global level settings (M2_HOME/conf/settings.xml)

It then searches in the Maven Central repository (defined in the super Maven POM, which all POMs inherit from). If it cannot find it, your build will break until you can remedy the situation.

Finding Flex and Flexmojos dependencies

So then, how does your local install of Maven know where to find both the Flex and Flexmojos dependencies listed in your POM?

Flexmojos itself is hosted on Maven Central, which is referenced in the super POM that all POMs inherit from. This means you don't have to add anything to your POM for Maven to find and download it. If you want to search Maven Central manually go to search.maven.org.

Unfortunately, the latest Flex SDK and compiler dependencies are not hosted on Maven Central. You need to add the following repository information into either your POM, your user-level settings.xml in your local repository, or the global-level settings.xml under the conf folder of your Maven install. This way, when Maven runs, it knows where to look for the Flex framework and the Flex compiler:

The above location points to a Nexus repository. Nexus allows an institution to host a collection of artifacts both internally and externally.

Check out the above Nexus by following the url: repository.sonatype.org. Try searching for various artifacts in the search box provided—you can see the various types and versions for each. Indeed, this is often the way you will discover which release versions of various dependencies are available. I'll cover Nexus in more detail in the third article.

Manually adding dependencies to the local repository

What if you cannot find the dependency hosted anywhere? What then? Well, you can manually add artifacts to your repository using install:install-file .

For example, say you want to add the as3-signals dependency to your build (as3-signals is a third-party library I'll use as an example). You might try adding the following to your POM:

However, if you tried to build, you'd likely see something similar to:

[ERROR] Failed to execute goal on project flexmojos-dependencies: Could not resolve dependencies for project org.justinjmoses.flexmojos-introduction:flexmojos-dependencies:swf:0.0.1-SNAPSHOT: Could not find artifact org.osflash:as3-signals:swc:0.8 in flex-mojos-repository (http://repository.sonatype.org/content/groups/flexgroup) -> [Help 1]

Maven cannot find the dependency listed. You could try digging around the Internet, to see if it is hosted anywhere. If found, you could add that repository location to your POM or settings file. Alternatively, you can simply install the dependency locally. Assuming you're in the same folder as the file as3-signals-0.8.swc, you could run the following command to add the artifact to your repository:

Note: Here we're running the install-file goal from the install plug-in. We're passing in parameters using the -D option. For details on these parameters, you can check the plug-in's install-file goal page.

An example POM

Here's an example POM to get started. I'm going to add comments inline to explain each and every aspect of the POM. The following is a Flex 4.5.1 example.

The opening is pretty standard: some XML namespaces and a POM version. The model version states which version of the object model to use, and hence which Maven super POM you are inheriting from. This number changes infrequently, and has been 4.0.0 since Maven 2:

Then, I state what type of artifact this is. In this case it is SWF, which is provided by Flexmojos and which determines the goals and phases within the default build lifecycle:

<packaging>swf</packaging>

Next, I explicitly define a property—much like you would in Ant. I'm simply setting up reusable constants. Here I define the latest release of Flex 4.5.1 (exact numbering is found on the Flex SDK page):

<properties>
<flex.version>4.5.1.21328</flex.version>
</properties>

Now, within the build, I tell Maven where, relative to the current POM, to find the source files. By default, Flexmojos will look for *.mxml and *.as files in, and in all subfolders of, the source directory:

<build>
<sourceDirectory>src/main/flex</sourceDirectory>

Then I specify Flexmojos as a plug-in to the build itself. Here I am using the latest version of Flexmojos, 4.0-RC2:

Extensions are set to true to ensure the Flexmojos default lifecycle comes into play via the SWF packaging defined above:

<extensions>true</extensions>

The configuration lists all the various options for all goals within the build. Alternatively, I could set these on the command-line using the -D option. I'll cover configuration settings in more detail in the next article:

<configuration>

For a SWF build, I define the main Application file, relative to the source folder defined above. By default, Flexmojos looks for the sole .mxml file in the source directory, so this is optional, but I add it nonetheless:

<sourceFile>./Main.mxml</sourceFile>

I prefer to turn off the no constructor compiler warning—I don't need warnings for my lack of constructors:

This particular project uses as3-signals —a third-party dependency. If you were to run this project yourself, you'd have to manually execute mvn install:install-file to add the as3-signals dependency to your local repository (see above):

Note: As you develop your Maven prowess, you build up a collection of POMs that work best for you. Initially, you can start with POMs used on other projects and then prune and add features and configuration settings as required. Always remove what you do not need—it makes detecting problems MUCH easier without the clutter.

Where to go from here

Maven is a powerful tool that is all too often misunderstood and misused by overzealous converts new to the fold. Through its many conventions, Maven solves a lot of common problems simply and efficiently. When everything works, life is brilliant. But when things go wrong, you'll be glad you spent the time learning the conventions, saving countless hours pulling out your hair. If you find yourself getting stuck, go back to first principles. Remove all unnecessary clutter in your POM and try to isolate the problem (sounds a lot like debugging doesn't it?). Many problems arise as developers cut and paste pieces from POMs they find online—their own scripts become a potpourri of configuration settings.

So download Maven, grab the sample code from Github, and have a tinker with the builds.

This is the first article in a three part series. The next article dives into more complex projects and discusses Flashbuilder integration, unit testing, and automated project setup.