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.
Example 5.6. 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:
Example 5.7. 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.
Example 5.8. 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 5.9. 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 5.10. Depending on a Qualified Artifact
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
<classifier>linux</classifier>
</dependency>
