Maven: The Complete Reference
5.5. 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.
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 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 simply
pass in the appropriate property value on the command-line to ensure
that your builds target the correct environment. 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.
Project Profile Activated by setting environment.type to dev.
<project>
...
<profiles>
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
<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 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. If we needed to target the development
environment, we would just run the following command:
~/examples/profiles $ mvn install
Because the development profile is active by default, and because
there are no other profiles activated, running mvn
help:active-profiles will show that the development profile is
active. Now, the activeByDefault option will only work if no other
profiles are active. If you wanted to be sure that the development
profile would be active for a given build, you could explicitly pass
in the environment.type variable as follows:
~/examples/profiles $ mvn install -Denvironment.type=dev
Alternatively, if we need to activate the production profile, we could always run Maven with:
~/examples/profiles $ mvn install -Denvironment.type=prod
To test which profiles are active for a given build, use mvn
help:active-profiles.
This best practice builds upon the previous section. In
Project Profile Activated by setting environment.type to 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:
Storing Secrets in a User-specific Settings Profile.
<settings>
<profiles>
<profile>
<activeByDefault>true</activeByDefault>
<properties>
<environment.type>prod</environment.type>
<database.password>m1ss10nimp0ss1bl3</database.password>
</properties>
</profile>
</profiles>
</settings>
This user has defined a default profile which sets the
environment.type to prod and which also sets the production
password. When the project is executed, the production profile is
activated by the environment.type property and the
database.password property is populated. This way, you can put all
of the production-specific configuration into a project’s pom.xml
and leave out only the single secret necessary to access the
production database.
Note
Secrets usually conflict with wide portability, but this makes sense. You wouldn’t want to share your secrets openly.
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 Operating System parameters. For more information about the Maven Assembly plugin, see Chapter 8, Maven Assemblies.
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:
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:
Depending on a Qualified Artifact.
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
<classifier>linux</classifier>
</dependency>
