
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
|
The |
|
In this profile we are adding the module
|
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:
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.
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.
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.
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.
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)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.
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=prodSetting 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.
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.
Secrets usually conflict with wide portability, but this makes sense. You wouldn't want to share your secrets openly.
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>
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.

