Chapter 11. Build Profiles

11.1. What Are They For?

Favicon

Profiles allow for the ability to customize a particular build for a particular environment; profiles enable portability between different build environments.

What do we mean by different build environments? Two example build environments are production and development. When you are working in a development environment, your system might be configured to read from a development database instance running on your local machine while in production, your system is configured to read from the production database. Maven allows you to define any number of build environments (build profiles) which can override any of the settings in the pom.xml. You could configure your application to read from your local, development instance of a database in your "development" profile, and you can configure it to read from the production database in the "production" profile. Profiles can also be activated by the environment and platform, you can customize a build to run differently depending the Operating System or the installed JDK version. Before we talk about using and configuring Maven profiles, we need to define the concept of Build Portability.

11.1.1. What is Build Portability

Favicon

A build's "portability" is a measure of how easy it is to take a particular project and build it in different environments. A build which works without any custom configuration or customization of properties files is more portable than a build which requires a great deal of work to build from scratch. The most portable projects tend to be widely used open source projects like Apache Commons of Apache Velocity which ship with Maven builds which require little or no customization. Put simply, the most portable project builds tend to just work, out of the box, and the least portable builds require you to jump through hoops and configure platform specific paths to locate build tools. Before we show you how to achieve build portability, let's survey the different kinds of portability we are talking about.

11.1.1.1. Non-Portable Builds

Favicon

The lack of portability is exactly what all build tools are made to prevent - however, any tool can be configured to be non-portable (even Maven). A non-portable project is buildable only under a specific set of circumstances and criteria (e.g., your local machine). Unless you are working by yourself and you have no plans on ever deploying your application to another machine, it is best to avoid non-portability entirely. A non-portable build only runs on a single machine, it is a "one-off". Maven is designed to discourage non-portable builds by offering the ability to customize builds using profiles.

When a new developer gets the source for a non-portable project, they will not be able to build the project without rewriting large portions of a build script.

11.1.1.2. Environment Portability

Favicon

A build exhibits environment portability if it has a mechanism for customizing behavior and configuration when targeting different environments. A project that contains a reference to a test database in a test environment, for example, and a production database in a production environment, is environmentally portable. It is likely that this build has a different set of properties for each environment. When you move to a different environment, one that is not defined and has no profile created for it, the project will not work. Hence, it is only portable between defined environments.

When a new developer gets the source for an environmentally portable project, they will have to run the build within a defined environment or they will have to create a custom environment to successfully build the project.

11.1.1.3. Organizational (In-House) Portability

Favicon

The center of this level of portability is a project's requirement that only a select few may access internal resources such as source control or an internally-maintained Maven repository. A project at a large corporation may depend on a database available only to in-house developers, or an open source project might require a specific level of credentials to publish a web site and deploy the products of a build to a public repository.

If you attempt to build an in-house project from scratch outside of the in-house network (for example, outside of a corporate firewall), the build will fail. It may fail because certain required custom plugins are unavailable, or project dependencies cannot be found because you don't have the appropriate credentials to retrieve dependencies from a custom remote repository. Such a project is portable only across environments in a single organization.

11.1.1.4. Wide (Universal) Portability

Favicon

Anyone may download a widely portable project's source, compile, and install it without customizing a build for a specific environment. This is the highest level of portability; anything less requires extra work for those who wish to build your project. This level of portability is especially important for open source projects, which depend on the ability for would-be contributors to easily download and build from source.

Any developer could download the source for a widely portable project.

11.1.2. Selecting an Appropriate Level of Portability

Favicon

Clearly, you'll want to avoid creating the worst-case scenario: the non-portable build. You may have had the misfortune to work or study at an organization that had critical applications with non-portable builds. In such organizations, you cannot deploy an application without the help of a specific individual on a specific machine. In such an organization, it is also very difficult to introduce new project dependencies or changes without coordinating the change with the single person who maintains such a non-portable build. Non-portable builds tend to grow in highly political environments when one individual or group needs to exert control over how and when a project is built and deployed. "How do we build the system? Oh, we've got to call Jack and ask him to build it for us, no one else deploys to production." That is a dangerous situation which is more common that you would think. If you work for this organization, Maven and Maven profiles provide a way out of this mess.

On the opposite end of the portability spectrum are widely portable builds. Widely portable builds are generally the most difficult build systems to attain. These builds restrict your dependencies to those projects and tools that may be freely distributed and are publicly available. Many commercial software packages might be excluded from the most-portable builds because they cannot be downloaded before you have accepted a certain license. Wide portability also restricts dependencies to those pieces of software that may be distributed as Maven artifacts. For example, if you depend upon Oracle JDBC drivers, your users will have to download and install them manually; this is not widely portable as you will have to distribute a set of environment setup instructions for people interested in building your application. On the other hand, you could use a JDBC driver which is available from the public Maven repositories like MySQL or HSQLDB.

As stated previously, open source projects benefit from having the most widely portable build possible. Widely portable builds reduce the inefficiencies associated with contributing to a project. In an open source project (such as Maven) there are two distinct groups: end-users and developers. When an end-user uses a project like Maven and decides to contribute a patch to Maven, they have to make the transition from using the output of a build to running a build. They have to first become a developer, and if it is difficult to learn how to build a project, this end-user has a disincentive to take the time to contribute to a project. In a widely portable project, an end-user doesn't have to follow a set or arcane build instructions to start becoming a developer, they can download the source, modify the source, build, and submit a contribution without asking someone to help them set up a build environment. When the cost of contributing source back to an open-source project is lower, you'll see an increase in source code contributions, especially casual contributions which can make the difference between a project's success and a project's failure. One side-effect of Maven's adoption across a wide group of open source projects is that it has made it easier for developers to contribute code to various open source projects.

11.2. Portability through Maven Profiles

Favicon

A profile in Maven is an alternative set of configuration values which set or override default values. Using a profile, you can customize a build for different environments. Profiles are configured in the pom.xml and are given an identifier. Then you can run Maven with a command-line flag that tells Maven to execute goals in a specific profile. The following pom.xml uses a production profile to override the default settings of the Compiler plugin.

Example 11.1. Using a Maven Profile to Override Production Compiler Settings

<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook</groupId>
  <artifactId>simple</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>simple</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <profiles>
    <profile>
      <id>production</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <debug>false</debug>
              <optimize>true</optimize>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>

In this example, we've added a profile named production that overrides the default configuration of the Maven Compiler plugin, let's examine the syntax of this profile in detail.

1

The profiles element is in the pom.xml, it contains one or more profile elements. Since profiles override the default settings in a pom.xml, the profiles element is usually listed as the last element in a pom.xml.

2

Each profile has to have an id element. This id element contains the name which is used to invoke this profile from the command-line. A profile is invoked by passing the -P<profile_id> command-line argument to Maven.

3

A profile element can contain many of the elements which can appear under the project element of a POM XML Document. In this example, we're overriding the behavior of the Compiler plugin and we have to override the plugin configuration which is normally enclosed in a build and a plugins element.

4

We're overriding the configuration of the Maven Compiler plugin. We're making sure that the bytecode produced by the production profile doesn't contain debug information and that the bytecode has gone through the compiler's optimization routines.

To execute mvn install under the production profile, you need to pass the -Pproduction argument on the command-line. To verify that the production profile overrides the default Compiler plugin configuration, execute Maven with debug output enabled (-X) as follows:

~/examples/profile $ mvn clean install -Pproduction -X
... (omitting debugging output) ...
[DEBUG] Configuring mojo 'o.a.m.plugins:maven-compiler-plugin:2.0.2:testCompile'
[DEBUG]   (f) basedir = ~\examples\profile
[DEBUG]   (f) buildDirectory = ~\examples\profile\target
...
[DEBUG]   (f) compilerId = javac
[DEBUG]   (f) debug = false
[DEBUG]   (f) failOnError = true
[DEBUG]   (f) fork = false
[DEBUG]   (f) optimize = true
[DEBUG]   (f) outputDirectory = \
          ~\svnw\sonatype\examples\profile\target\test-classes
[DEBUG]   (f) outputFileName = simple-1.0-SNAPSHOT
[DEBUG]   (f) showDeprecation = false
[DEBUG]   (f) showWarnings = false
[DEBUG]   (f) staleMillis = 0
[DEBUG]   (f) verbose = false
[DEBUG] -- end configuration --
... (omitting debugging output) ...

This excerpt from the debug output of Maven shows the configuration of the Compiler plugin under the production profile. As shown in the output, debug is set to false and optimize is set to true.

11.2.1. Overriding a Project Object Model

Favicon

While the previous example showed you how to override the default configuration properties of a single Maven plugin, you still don't know exactly what a Maven profile is allowed to override. The short-answer to that question is that a Maven profile can override almost everything that you would have in a pom.xml. The Maven POM contains an element under project called profiles containing a project's alternate configurations, and under this element are profile elements which define each profile. Each profile must have an id, and other than that, it can contain almost any of the elements one would expect to see under project. The following XML document shows all of the elements, a profile is allowed to override.

Example 11.2. Elements Allowed in a Profile

<project>
  <profiles>
    <profile>
      <build>
        <defaultGoal>...</defaultGoal>
        <finalName>...</finalName>
        <resources>...</resources>
        <testResources>...</testResources>
        <plugins>...</plugins>
      </build>
      <reporting>...</reporting>
      <modules>...</modules>
      <dependencies>...</dependencies>
      <dependencyManagement>...</dependencyManagement>
      <distributionManagement>...</distributionManagement>
      <repositories>...</repositories>
      <pluginRepositories>...</pluginRepositories>
      <properties>...</properties>
    </profile>
  </profiles>
</project>


A profile can override an element shown with ellipses. A profile can override the final name of a project's artifact in a profile, the dependencies, and the behavior of a project's build via plugin configuration. A profile can also override the configuration of distribution settings depending on the profile; for example, if you need to publish an artifact to a staging server in a staging profile, you would create a staging profile which overrides the distributionManagement element in a profile.

11.3. Profile Activation

Favicon

In the previous section we showed you how to create a profile that overrides default behavior for a specific target environment. In the previous build the default build was designed for development and the production profile exists to provide configuration for a production environment. What happens when you need to provide customizations based on variables like operating systems or JDK version? Maven provides a way to "activate" a profile for different environmental parameters, this is called profile activation.

Take the following example, assume that we have a Java library that has a specific feature only available in the Java 6 release: the Scripting Engine as defined in JSR-223. You've separated the portion of the library that deals with the scripting library into a separate Maven project, and you want people running Java 5 to be able to build the project without attempting to build the Java 6 specific library extension. You can do this by using a Maven profile that adds the script extension module to the build only when the build is running within a Java 6 JDK. First, let's take a look at our project's directory layout and how we want developers to build the system.

When someone runs mvn install with a Java 6 JDK, you want the build to include the simple-script project's build, when they are running in Java 5, you would like to skip the simple-script project build. If you failed to skip the simple-script project build in Java 5, your build would fail because Java 5 does not have have ScriptEngine on the classpath. Let's take a look at the library project's pom.xml:

Example 11.3. Dynamic Inclusion of Submodules Using Profile Activation

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook</groupId>
  <artifactId>simple</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>simple</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <profiles>
    <profile>
      <id>jdk16</id>
      <activation>
        <jdk>1.6</jdk>
      </activation>
      <modules>
        <module>simple-script</module>
      </modules>
    </profile>
  </profiles>
</project>

If you run mvn install under Java 1.6, you will see Maven descending into the simple-script subdirectory to build the simple-script project. If you are running mvn install in Java 1.5, the build will not try to build the simple-script submodule. Exploring this activation configuration in more detail:

1

The activation element lists the conditions for profile activation. In this example, we've specified that this profile will be activated by Java versions that begin with "1.6". This would include "1.6.0_03", "1.6.0_02", or any other string that began with "1.6". Activation parameters are not limited to Java version, for a full list of activation parameters, see Activation Configuration.

2

In this profile we are adding the module simple-script. Adding this module will cause Maven to look in the simple-script/ subdirectory for a pom.xml.

11.3.1. Activation Configuration

Favicon

Activations can contain one of more selectors including JDK versions, Operating System parameters, files, and properties. A profile is activated when all activation criteria has been satisfied. For example, a profile could list an Operating System family of Windows, and a JDK version of 1.4, this profile will only be activated when the build is executed on a Windows machine running Java 1.4. If the profile is active then all elements override the corresponding project-level elements as if the profile were included with the -P command-line argument. The following example, lists a profile which is activated by a very specific combination of operating system parameters, properties, and a JDK version.

Example 11.4. Profile Activation Parameters: JDK Version, OS Parameters, and Properties

<project>
  ...
  <profiles>
    <profile>
      <id>dev</id>
      <activation>
        <activeByDefault>false</activeByDefault>
        <jdk>1.5</jdk>
        <os>
          <name>Windows XP</name>
          <family>Windows</family>
          <arch>x86</arch>
          <version>5.1.2600</version>
        </os>
        <property>
          <name>mavenVersion</name>
          <value>2.0.5</value>
        </property>
        <file>
          <exists>file2.properties</exists>
          <missing>file1.properties</missing>
        </file>
      </activation>
      ...
    </profile>
  </profiles>
</project>


This previous example defines a very narrow set of activation parameters. Let's examine each activation criterion in detail:

1

The activeByDefault element controls whether this profile is considered active by default.

2

This profile will only be active for JDK versions that begin with "1.5". This includes "1.5.0_01", "1.5.1".

3

This profile targets a very specific version of Windows XP, version 5.1.2600 on a 32-bit platform. If your project uses the native plugin to build a C program, you might find yourself writing projects for specific platforms.

4

The property element tells Maven to activate this profile if the property mavenVersion is set to the value 2.0.5. mavenVersion is an implicit property that is available to all Maven builds.

5

The file element allows us to activate a profile based on the presence (or absence) of files. The dev profile will be activated if a file named file2.properties exists in the base directory of the project. The dev profile will only be activated if there is no file named file1.properties file in the base directory of the project.

11.3.2. Activation by the Absence of a Property

Favicon

You can activate a profile based on the value of a property like environment.type. You can activate a development profile if environment.type equals dev, or a production profile if environment.type equals prod. You can also activate a profile in the absence of a property. The following configuration activates a profile is the property environment.type is not present during Maven execution.

Example 11.5. Activating Profiles in the Absence of a Property

<project>
  ...
  <profiles>
    <profile>
      <id>development</id>
      <activation>
        <property>
          <name>!environment.type</name>
        </property>
      </activation>
    </profile>
  </profiles>
</project>


Note the exclamation point prefixing the property name. The exclamation point is often referred to as the "bang" character and signifies "not". This profile is activated when no ${environment.type} property is set.

11.4. External Profiles

Favicon

If you start making extensive use of Maven profiles, you may want to separate your profiles from your POM in a separate file named profiles.xml. You can mix and match profiles defined in the pom.xml with profiles defined in the external profiles.xml file. Just place the profiles element into profiles.xml in ${basedir} and run Maven as you normally woudl. This profiles.xml file would look something like the following example:

Example 11.6. Placing Profiles in a profiles.xml File

 <profiles>
    <profile>
      <id>development</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <debug>true</debug>
              <optimize>false</optimize>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>production</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <debug>false</debug>
              <optimize>true</optimize>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>

You might find that your profiles have grown so large that you are having trouble managing the pom.xml, or you might just find separating the pom.xml from the profiles.xml file a cleaner approach to putting everything into a single file. You can invoke profiles stored in profiles.xml the same way you would invoke them if they were defined in the pom.xml.

11.5. Settings Profiles

Favicon

Project profiles are useful when a specific project needs to customize a build setting for a specific environment, but why would you want to override a build setting for every project in Maven? How do you do something like add an internal repository which is consulted on every Maven build? You can do this with a settings profile. Where project profiles are concern with overriding the configuration of a specific project, settings profiles can be applied to any and all projects you build with Maven. You can place settings profiles in two locations: a user specific settings profile defined in ~/.m2/settings.xml or a global settings profile defined in ${M2_HOME}/conf/settings.xml. Here is an example of a settings profile defined in ~/.m2/settings.xml which might set some user-specific confguration properties for all builds. The following settings.xml file is defined for user tobrien:

Example 11.7. Defining User-specific Settings Profiles (~/.m2/settings.xml)

<settings>
  <profiles>
    <profile>
      <id>dev</id>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <executions>
           <execution>
              <goals>
                 <goal>sign</goal>
              </goals>
           </execution>
        </executions>
        <configuration>
           <keystore>/home/tobrien/java/keystore</keystore>
           <alias>tobrien</alias>
           <storepass>s3cr3tp@ssw0rd</storepass>
           <signedjar>
         ${project.build.directory}/signed/${project.build.finalName}.jar
           </signedjar>
           <verify>true</verify>
        </configuration>
      </plugin>
    </profile>
  </profiles>
</settings>

The previous example is a plausible use of a user-specific settings profile. This example sets user-specific settings like the password and alias to use when signing a JAR file during a release. These are configuration parameters you wouldn't want to store in a project's shared pom.xml or a profiles.xml file because they involve some secrets that should not be public.

The downside of settings profiles is that they tend to interfere with project portability. If the previous example were an open source project, a new developer would not be able to sign a JAR until they had manually configured a settings profile and talked to one of the existing developers. In this case, the security requirements of signing a JAR are in conflict with the larger goal of achieving a universally portable project build. On most open-source projects, there are tasks that require security credentials: publishing an artifact to a remote repository, publishing a project's web site, or signing a JAR file. For these tasks, the highest level of portability we can hope for is organizational portability. These higher security tasks usually require some manual setup and configuration of a profile.

Instead of explicitly specifying the name of the profile with the -P command-line argument. You can define a list of active profiles which are activated for every project you run. For example, if you wanted to activate the dev profile defined in settings.xml for every project you run, you would add the following section to your ~/.m2/settings.xml file:

Example 11.8. Defining Active Settings Profiles

<settings>
  ...
  <activeProfiles>
    <activeProfile>dev</activeProfile>
  </activeProfiles>
</settings>


This will activate settings profiles only, not project profiles with matching id elements. For example, if you had a project with a profile defined in its pom.xml with an id of dev, it would not be affected by the activeProfile set in your settings.xml. This activeProfile setting only affects profiles defined in your settings.xml file.

11.5.1. Global Settings Profiles

Favicon

Just like settings profiles, you can also define a set of global profiles in ${M2_HOME}/conf/settings.xml. Profiles defined in this configuration file are available across all users using a specific installation of Maven. The ability to define a global settings profile is useful f you are creating a customized distribution of Maven for a specific organization and you want to ensure that every user of Maven has access to a set of build profiles that ensure in-house portability. If you need to add custom plugin repositories or define a custom set of plugins that are only used by your organization, you could distribute a copy of Maven to your users that has these settings "baked in". The configuration of global settings profiles is the same as the configuration of User-specific settings profiles.

11.6. Listing Active Profiles

Favicon

Maven profiles can be defined in either pom.xml, profiles.xml, ~/.m2/settings.xml, or ${M2_HOME}/conf/settings.xml. With these four levels, there's no good way of keeping track of profiles available to a particular project without remembering which profiles are defined in these four files. To make it easier to keep track of which profiles are available, and where they have been defined, the Maven Help plugin defines a goal, active-profiles, which lists all the active profiles and where they have been defined. You can run the active-profiles goal, as follows:

$ mvn help:active-profiles
Active Profiles for Project 'My Project':

The following profiles are active:

 - my-settings-profile (source: settings.xml)
 - my-external-profile (source: profiles.xml)
 - my-internal-profile (source: pom.xml)

11.7. Tips and Tricks

Favicon

Profiles can encourage build portability. If your build needs subtle customizations to work on different platforms or if you need your build to produce different results for different target platforms, project profiles increase build portability. Settings profiles generally decrease build portability by adding extra-project information that must be communicated from developer to developer. The following sections provide some guidelines and some ideas for applying Maven profiles to your project.

11.7.1. Common Environments

Favicon

One of core motivations for Maven project profiles was to provide for environment-specific configuration settings. In a development environment, you might want to produce bytecode with debug information and you might want to configure your system to use a development database instance. In a production environment you might want to produce a signed JAR and configure the system to use a production database. In this chapter, we defined a number of environments with identifiers like dev and prod. A simpler way to do this would be to define profiles that are activated by environment properties and to use these common environment properties across all of your projects. For example, if every project had a development profile activated by a property named environment.type having a value of dev, and if those same projects had a production profile activated by a property named environment.type having a value of prod, you could create a default profile in your settings.xml that always set environment.type to dev on your development machine. This way, each project defines a dev profile activated by the same environment variable. Let's see how this is done, the following settings.xml defines a profile in ~/.m2/settings.xml which sets the environment.type property to dev.

Example 11.9. ~/.m2/settings.xml defines a default profile setting environment.type

<settings>
  <profiles>
    <profile>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <environment.type>dev</environment.type>
      </properties>
    </profile>
  </profiles>
</settings>


This means that every time you run Maven on your machine, this profile will be activated and the property environment.type will have the value dev. You can then use this property to activate profiles defined in a project's pom.xml as follows. Let's take a look at how a project's pom.xml would define a profile activated by environment.type having the value dev.

Example 11.10. Project Profile Activated by environment.type equalling 'dev'

<project>
  ...
  <profiles>
    <profile>
      <id>development</id>
      <activation>
        <property>
          <name>environment.type</name>
          <value>dev</value>
        </property>
      </activation>
      <properties>
        <database.driverClassName>com.mysql.jdbc.Driver</database.driverClassName>
        <database.url>
          jdbc:mysql://localhost:3306/app_dev
        </database.url>
        <database.user>development_user</database.user>
        <database.password>development_password</database.password>
      </properties>
    </profile>
    <profile>
      <id>production</id>
      <activation>
        <property>
          <name>environment.type</name>
          <value>prod</value>
        </property>
      </activation>
      <properties>
        <database.driverClassName>com.mysql.jdbc.Driver</driverClassName>
        <database.url>jdbc:mysql://master01:3306,slave01:3306/app_prod</database.url>
        <database.user>prod_user</database.user>
      </properties>
    </profile>
  </profiles>
</project>


This project defines some properties like database.url and database.user which might be used to configure another Maven plugin configured in the pom.xml. There are plugins available that can manipulate the database, run SQL, and plugins like the Maven Hibernate3 plugin which can generate annotated model objects for use in persistence frameworks. A few of these plugins, can be configured in a pom.xml using these properties. These properties could also be used to filter resources. In this example, because we've defined a profile in ~/.m2/settings.xml which sets environment.type to dev, the development profile will always be activated when we run Maven on our development machine. Alternatively, if we wanted to override this default, we could set a property on the command-line. If we need to activate the production profile, we could always run Maven with:

~/examples/profiles $ mvn install -Denvironment.type=prod

Setting a property on the command-line would override the default property set in ~/.m2/settings.xml. We could have just defined a profile with an id of "dev" and invoked it directly with the -P command-line argument, but using this environment.type property allows us to code other project pom.xml files to this standard. Every project in your codebase could have a profile which is activated by the same environment.type property set in every user's ~/.m2/settings.xml. In this way, developers can share common configuration for development without defining this configuration in non-portable settings.xml files.

11.7.2. Protecting Secrets

Favicon

This best practice builds upon the previous section. In Project Profile Activated by environment.type equalling 'dev', the production profile does not contain the database.password property. I've done this on purpose to illustrate the concept of putting secrets in you user-specific settings.xml. If you were developing an application at a large organization which values security, it is likely that the majority of the development group will not know the password to the production database. In an organization that draws a bold line between the development group and the operations group, this will be the norm. Developers may have access to a development and a staging environment, but they might not have (or want to have) access to the production database. There are a number of reasons why this makes sense, particularly if an organization is dealing with extremely sensitive financial, intelligence, or medical information. In this scenario, the production environment build may only be carried out by a lead developer or by a member of the production operations group. When they run this build using the prod environment.type, they will need to define this variable in their settings.xml as follows:

Example 11.11. Storing Secrets in a User-specific Settings Profile

<settings>
  <profiles>
    <profile>
      <activeByDefault>true</activeByDefault>
      <properties>
        <environment.type>prod</environment.type>
        <database.password>m1ss10nimp0ss1bl3</database.password>
      </properties>
    </profile>
  </profiles>
</settings>

This user has defined a default profile which sets the environment.type to prod and which also sets the production password. When the project is executed, the production profile is activated by the environment.type property and the database.password property is populated. This way, you can put all of the production-specific configuration into a project's pom.xml and leave out only the single secret necessary to access the production database.

Note

Secrets usually conflict with wide portability, but this makes sense. You wouldn't want to share your secrets openly.

11.7.3. Platform Classifiers

Favicon

Let's assume that you have a library or a project that produces platform-specific customizations. Even though Java is platform-neutral, there are times when you might need to write some code that invokes platform-specific native code. Another possibility is that you've written some C code which is compiled by the Maven Native plugin and you want to produce a qualified artifact depending on the build platform. You can set a classifier with the Maven Assembly plugin or with the Maven Jar plugin. The following pom.xml produces a qualified artifact using profiles which are activated by Operation System parameters. For more information about the Maven Assembly plugin, see Chapter 12, Maven Assemblies.

Example 11.12. Qualifying Artifacts with Platform Activated Project Profiles

<project>
  ...
  <profiles>
    <profile>
      <id>windows</id>
      <activation>
        <os>
          <family>windows</family>
        </os>
      </activation>
      <build>
        <plugins>
          <plugin
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
              <classifier>win</classifier>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>linux</id>
      <activation>
        <os>
          <family>unix</family>
        </os>
      </activation>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
              <classifier>linux</classifier>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>


If the Operating System is in the Windows family, this pom.xml qualifies the JAR artifact with "-win". If the Operating System is in the Unix family, the artifact is qualified with "-linux". This pom.xml successfully adds the qualifiers to the artifacts, but it is more verbose than it need to be due to the redundant configuration of the Maven Jar plugin in both profiles. This example could be rewritten to use variable substitution to minimize redundancy as follows:

Example 11.13. Qualifying Artifacts with Platform Activated Project Profiles and Variable Substitution

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <classifier>${envClassifier}</classifier>
        </configuration>
      </plugin>
    </plugins>
  </build>
  ...
  <profiles>
    <profile>
      <id>windows</id>
      <activation>
        <os>
          <family>windows</family>
        </os>
      </activation>
      <properties>
        <envClassifier>win</envClassifier>
      </properties>
    </profile>
    <profile>
      <id>linux</id>
      <activation>
        <os>
          <family>unix</family>
        </os>
      </activation>
      <properties>
        <envClassifier>linux</envClassifier>
      </properties>
    </profile>
  </profiles>
</project>


In this pom.xml, each profile doesn't need to include a build element to configure the Jar plugin. Instead, each profile is activated by the Operating System family and sets the envClassifier property to either win or linux. This envClassifier is then referenced in the default pom.xml build element to add a classifier to the project's JAR artifact. The JAR artifact will be named ${finalName}-${envClassifier}.jar and included as a dependency using the following dependency syntax:

Example 11.14. Depending on a Qualified Artifact

<dependency>
      <groupId>com.mycompany</groupId>
      <artifactId>my-project</artifactId>
      <version>1.0</version>
      <classifier>linux</classifier>
    </dependency>


11.8. Summary

Favicon

When used judiciously, profiles can make it very easy to customize a build for different platforms. If something in your build needs to define a platform-specific path for something like an application server, you can put these configuration points in a profile which is activated by an operating system parameter. If you have a project which needs to produce different artifacts for different environments, you can customize the build behavior for different environments and platforms via profile-specific plugin behavior. Using profiles, builds can become portable, there is no need to rewrite your build logic to support a new environment, just override the configuration that needs to change and share the configuration points which can be shared.


Creative Commons License
Maven: The Definitive Guide by Sonatype, Inc. is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Based on a work at www.sonatype.com.

Favicon Report Typos, Errors, Ask Questions, Discuss, Share Your Ideas with us at Get Satisfaction