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.”
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.
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.
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.
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.
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.
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.
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.
<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:
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.
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.
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.
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
.
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.
<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.
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.
<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. Let’s explore this
activation configuration in more detail:
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.”
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.
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.
<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 example defines a very narrow set of activation parameters. Let’s examine each activation criterion in detail:
The activeByDefault
element controls
whether this profile is considered active by
default.
This profile will be active only for JDK versions that begin with “1.5.” This includes “1.5.0_01” and “1.5.1.”
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.
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.
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.
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.
<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 would.
This profiles.xml file would look
something like Example 11-6.
<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.
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
.
<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.
<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.
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.
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)
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 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
.
<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
.
<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.
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.
<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.
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 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.
<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.
<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
${
and included as a dependency using the dependency
syntax shown in Example 11-14.finalName
}-${envClassifier}.jar
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.
18.191.189.23