Using Profiles
Profiles are Maven’s way of letting you create environmental variations in the build life cycle to accommodate things like building on different platforms, building with different JVMs, testing with different databases, or referencing the local file system. Typically, you try to encapsulate as much as possible in the POM to ensure that builds are portable, but sometimes you simply have to take into consideration variation across systems and this is why profiles were introduced in Maven.
Profiles are specified using a subset of the elements available in the POM itself (plus one extra section), and can be activated in several ways. Profiles modify the POM at build time, and are meant to be used in complementary sets to give equivalent-but-different parameters for a set of target environments (providing, for example, the path of the application server root in the development, testing, and production environments).
As such, profiles can easily lead to differing build results from different members of your team. However, used properly, you can still preserve build portability with profiles. You can define profiles in one of the following three places:
- The Maven settings file (typically
<user_home>/.m2/settings.xml) - A file in the the same directory as the POM, called
profiles.xml - The POM itself
In terms of which profile takes precedence, the local-most profile wins. So, POM-specified profiles override those in profiles.xml, and profiles.xml overrides those in settings.xml. This is a pattern that is repeated throughout Maven, that local always wins, because it is assumed to be a modification of a more general case.
settings.xml profiles have the potential to affect all builds, so they’re sort of a “global” location for profiles. profiles.xml allows you to augment a single project’s build without altering the POM. And the POM-based profiles are preferred, since these profiles are portable (they will be distributed to the repository on deploy, and are available for subsequent builds originating from the repository or as transitive dependencies).
Because of the portability implications, any files which are not distributed to the repository are NOT allowed to change the fundamental build in any way. Therefore, the profiles specified in profiles.xml and settings.xml are only allowed to define:
- repositories
- pluginRepositories
- properties
Everything else must be specified in a POM profile, or in the POM itself, or not at all. For example, if you had a profile in settings.xml that was able to inject a new dependency, and the project you were working on actually did depend on that settings-injected dependency in order to run, then once that project is deployed to the repository it will never fully resolve its dependencies transitively when asked to do so. That’s because it left one of its dependencies sitting in a profile inside your settings.xml file.
repositories, pluginRepositories, and properties can also be specified in profiles within the POM. So, the profiles specified outside the POM are only allowed a small subset of the options available within the POM.You can define the following elements in the POM profile:
- repositories
- pluginRepositories
- dependencies
- plugins
- properties (not actually available in the main POM, but used behind the scenes)
- modules
- reporting
- dependencyManagement
- distributionManagement
A subset of the build element, which consists of:
- defaultGoal
- resources
- testResources
- finalName
There are several ways that you can activate profiles:
- Profiles can be specified explicitly using the -P CLI option. This option takes an argument that contains a comma-delimited list of profile-ids. When this option is specified, no profiles other than those specified in the option argument will be activated. For example:
mvn -Pprofile1,profile2 install
- Profiles can be activated in the Maven settings, via the
activeProfilessection. This section takes a list ofactiveProfileelements, each containing a profile-id. Note that you must have defined the profiles in yoursettings.xmlfile as well. For example:
<settings>
[...]
<profiles>
<profile>
<id>profile1</id>
[...]
</profile>
</profiles>
<activeProfiles>
<activeProfile>profile1</activeProfile>
</activeProfiles>
[...]
</settings>
- Profiles can be triggered automatically based on the detected state of the build environment. These activators are specified via an activation section in the profile itself. Currently, this detection is limited to prefix-matching of the JDK version, the presence of a system property, or the value of a system property. Here are some examples:
<profile>
<id>profile1</id>
[...]
<activation>
<jdk>1.4</jdk>
</activation>
</profile>
This activator will trigger the profile when the JDK’s version starts with “1.4” (e.g., “1.4.0_08”, “1.4.2_07”, “1.4”).
<profile>
<id>profile1</id>
[...]
<activation>
<property>
<name>debug</name>
</property>
</activation>
</profile>
This will activate the profile when the system property “debug” is specified with any value.
<profile>
<id>profile1</id>
[...]
<activation>
<property>
<name>environment</name>
<value>test</value>
</property>
</activation>
</profile>
This last example will activate the profile when the system property “environment” is specified with the value “test”.
Now that you are familiar with profiles, you are going to use them to create tailored assemblies: an assembly of Proficio, which uses the memory-based store, and an assembly of Proficio, which uses the XStream-based store. These assemblies will be created in the proficio-cli module and the profiles used to control the creation of our tailored assemblies are defined there as well.
If you take a look at the POM for the proficio-cli module you will see the following profile definitions:
<project>
[...]
<!-- Profiles for the two assemblies to create for deployment -->
<profiles>
<!-- Profile which creates an assembly using the memory based store -->
<profile>
<id>memory</id>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly-store-memory.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
<activation>
<property>
<name>memory</name>
</property>
</activation>
</profile>
<!-- Profile which creates an assembly using the xstream based store -->
<profile>
<id>xstream</id>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/assembly-store-xstream.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
<activation>
<property>
<name>xstream</name>
</property>
</activation>
</profile>
</profiles>
</project>
You can see there are two profiles: one with an id of memory and another with an id of xstream. In each of these profiles you are configuring the assembly plugin to point at the assembly descriptor that will create a tailored assembly. You will also notice that the profiles are activated using a system property. It should be noted that the examples below depend on other parts of the build having been executed beforehand, so it might be useful to run mvn install at the top level of the project to ensure that needed components are installed into the local repository.
If you wanted to create the assembly using the memory-based store, you would execute the following:
mvn -Dmemory clean assembly:assembly
If you wanted to create the assembly using the XStream-based store, you would execute the following:
mvn -Dxstream clean assembly:assembly
Both of the assemblies are created in the target directory and if you use the jar tvf command on the resulting assemblies, you will see that the memory-based assembly contains the proficio-store-memory-1.0-SNAPSHOT.jar file only, while the XStream-based store contains the proficio-store-xstream-1.0-SNAPSHOT.jar file only. This is a very simple example, but it illustrates how you can customize the execution of the life cycle using profiles to suit any requirement you might have.