Chapter 11. Build Profiles

What Are They For?

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, whereas in production your system is configured to read from the production database. Maven allows you to define any number of build environments (build profiles) that can override any of the settings in the pom.xml. You can 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 on 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.”

What Is Build Portability?

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 that works without any custom configuration or customization of properties files is more portable than a build that requires a great deal of work to build from scratch. The most portable projects tend to be widely used open source projects such as Apache Commons or Apache Velocity, which ship with Maven builds that 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.

Nonportable builds

The lack of portability is exactly what all build tools are made to prevent; however, any tool can be configured to be nonportable (even Maven). A nonportable 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 to ever deploy your application to another machine, it is best to avoid nonportability entirely. A nonportable build runs only on a single machine; it is a “one-off.” Maven is designed to discourage nonportable builds by offering the ability to customize builds using profiles.

When a new developer gets the source for a nonportable project, he will not be able to build the project without rewriting large portions of a build script.

Environment portability

A build exhibits environment portability if it has a mechanism for customizing behavior and configuration when targeting different environments. For example, a project that contains a reference to a test database in a test environment 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, she will have to run the build within a defined environment, or she will have to create a custom environment to successfully build the project.

Organizational (in-house) portability

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 only portable across environments in a single organization.

Wide (universal) portability

Anyone may download a widely portable project’s source and 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.

Selecting an Appropriate Level of Portability

Clearly, you’ll want to avoid creating the worst-case scenario: the nonportable build. You may have had the misfortune to work or study at an organization that has critical applications with nonportable 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 nonportable build. Nonportable 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.” This is a dangerous situation that 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 on 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 that is available from the public Maven repositories such as MySQL or HSQLDB.

As stated previously, open source projects benefit from having the most widely portable builds 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 the project, he has to make the transition from using the output of a build to running a build. He first has to 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 of arcane build instructions to start becoming a developer; she can download the source, modify the source, build, and submit a contribution without asking someone to help her 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.

Portability Through Maven Profiles

A profile in Maven is an alternative set of configuration values that 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 pom.xml shown in Example 11-1 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>1
    <profile>
      <id>production</id>2
      <build>3
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <debug>false</debug>4
              <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 that is used to invoke this profile from the command line. A profile is invoked by passing the -Pprofile_id command-line argument to Maven.

3

A profile element can contain many of the elements that 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 that 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 
     'org.apache.maven.plugins:maven-compiler-plugin:2.0.2:testCompile' -->
[DEBUG]   (f) basedir = ~examplesprofile
[DEBUG]   (f) buildDirectory = ~examplesprofile	arget
...
[DEBUG]   (f) compilerId = javac
[DEBUG]   (f) debug = false
[DEBUG]   (f) failOnError = true
[DEBUG]   (f) fork = false
[DEBUG]   (f) optimize = true
[DEBUG]   (f) outputDirectory = 
     c:Users	obriensvnwsonatypeexamplesprofile	arget	est-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.

Overriding a Project Object Model

Although 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 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 that 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 XML document in Example 11-2 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 needed to publish an artifact to a staging server in a staging profile, you would create a staging profile that overrides the distributionManagement element in a profile.

Profile Activation

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 such as 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 we have a Java library that has a specific feature available only in the Java 6 release—the Scripting Engine as defined in JSR-223 (see http://jcp.org/en/jsr/detail?id=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 ScriptEngine on the classpath. Let’s take a look at the library project’s pom.xml shown in Example 11-3.

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>1
        <jdk>1.6</jdk>
      </activation>
      <modules>2
        <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. Let’s explore 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 begins with “1.6.” Activation parameters are not limited to Java version; for a full list of activation parameters, see the next section, 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.

Activation Configuration

Activations can contain one or 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 be activated only 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. Example 11-4 lists a profile that 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>1
        <jdk>1.5</jdk>2
        <os>
          <name>Windows XP</name>3
          <family>Windows</family>
          <arch>x86</arch>
          <version>5.1.2600</version>
        </os>
        <property>
          <name>mavenVersion</name>4
          <value>2.0.5</value>
        </property>
        <file>
          <exists>file2.properties</exists>5
          <missing>file1.properties</missing>
        </file>
      </activation>
      ...
    </profile>
  </profiles>
</project>

This 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 be active only for JDK versions that begin with “1.5.” This includes “1.5.0_01” and “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 be activated only if there is no file named file1.properties file in the base directory of the project.

Activation by the Absence of a Property

You can activate a profile based on the value of a property such as 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 configuration shown in Example 11-5 activates a profile if 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.

External Profiles

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 would. This profiles.xml file would look something like Example 11-6.

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 is 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.

Settings Profiles

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 that is consulted on every Maven build? You can do this with a settings profile. Where project profiles are concerned 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 that might set some user-specific configuration properties for all builds. The settings.xml file shown in Example 11-7 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 he 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, such as 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 that 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 section shown in Example 11-8 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 have a project with a profile defined in its pom.xml with an id of dev, it will not be affected by the activeProfile set in your settings.xml. This activeProfile setting affects only profiles defined in your settings.xml file.

Global Settings Profiles

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 if 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 used only 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.

Listing Active Profiles

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, that 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)

Tips and Tricks

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.

Common Environments

One of the 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 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 such as 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. That way, each project defines a dev profile activated by the same environment variable. Let’s see how this is done; the settings.xml shown in Example 11-9 defines a profile in ~/.m2/settings.xml that 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. Let’s take a look in Example 11-10 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 equal to 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</database.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 such as 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 and run SQL, and plugins such as the Maven Hibernate3 plugin 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 can also be used to filter resources. In this example, because we’ve defined a profile in ~/.m2/settings.xml that 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 can always run Maven with:

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

Setting a property on the command line will 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 the codebase can have a profile that 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 nonportable settings.xml files.

Protecting Secrets

This best practice builds on the previous section. In Example 11-10, the production profile does not contain the database.password property. We’ve done this on purpose to illustrate the concept of putting secrets in your user-specific settings.xml. If you were developing an application at a large organization that 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 be carried out only 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 shown in Example 11-11.

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 that sets the environment.type to prod and also set 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.

Platform Classifiers

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 that 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 pom.xml shown in Example 11-12 produces a qualified artifact using profiles that are activated by operating system parameters. For more information about the Maven Assembly plugin, see Chapter 12.

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 needs 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 shown in Example 11-13.

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 dependency syntax shown in Example 11-14.

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>

Summary

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 that is activated by an operating system parameter. If you have a project that 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 that can be shared.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.189.23