Previous: Chapter
8 |
Up: Chapter 8 |
Next: Chapter 9 |
What Are They For?
Profiles are the next item on the agenda. The main purpose behind profiles in Maven is portability between different build environments. Environment examples are: development, testing, performance, staging, production, clients, or any other type you can dream up. Profiles allow you to create a single project that can be ported to all environments, making minor tweaks to the final build along the way.
Types of Portability
There are different levels of portability, from narrow to wide, listed below.
Non-Portability
The lack of portability is exactly what all build tools are made to prevent - however, any tool can be configured to be non-portable. What makes Maven unique is the relative difficulty in making this happen (Ant is relatively easier to create non-portable builds - Make even moreso). A non-portable project is buildable only under a specific set of circumstances and criteria (e.g., your local machine). Unless you have no plans on porting your project to any other machinesever, it is best to avoid non-portability entirely. Non-portable is similar to environmental portability with a single definition; the project may be built in only one environment setting.
Environmental Portability
Maven created profiles for the environmental portability level, which largely concerns code and resource generation. As an example, consider a test environment that points to a separate database from that of production. When built on a test box, therefore, at least one resource file (or codehopefully not) must be manipulated. If a file must be altered to successfully build and run on a specific environment, then the project is at best environmentally portable. 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. 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.
In-House Portability
For most non-trivial software efforts, in-house portability is the best you can hope for. The majority of software projects fall under this listing, either for an open source development team or a closed-source production company. The center of this level of portability is your project's requirement that only a select few may access an internal (in-house) remote repository. In-house does not necessarily mean a specific company. A widely dispersed open-source project may require specific tools or connection access; this would be classified as in-house.
Another in-house portability example is a common database to which all in-house members may connect. 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. Hence, your project is portable only in-house.
Nothing is wrong with being in-house portable, per se, so do not twist your project out of shape attempting to avoid it. If it makes sense to split a project into multiple interdependent parts, then do it. If it makes sense to use a dependency version that is not available in the Maven Central repository, then use it. You can always make your own public repository and reference dependencies in your projects POM via the "repository" element, giving your project the coveted "widely portable" status.
Wide Portability
In the Maven world, anyone may download a wide portability project's source, and then compile and install it (sans modification) to the POM or requirements beyond standard Maven. 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 thrive on the ability for would-be contributors to easily download and install.
As you may imagine, being the highest level of portability makes it generally the most difficult to attain. It restricts your dependencies to those projects and tools that may be widely distributed according to their licenses (unlike many commercial software packages - which may not be made available before accepting a certain license). It also restricts dependencies to those pieces of software that may be distributed as Maven artifacts. For example, if you depend upon MySQL, your users will have to download and install it; this is not widely portable (only MySQL users can use your project without changing their systems). Using HSQLDB (HyperSonic Database), on the other hand, is available from Maven Central repository, and thus is widely portable.
Where Do Profiles Come In?
The goal is to make your project as widely portable as possible. The wider the portability the lighter the work on your builders. "Builders" constitute anyone (or anything) attempting to build the project. A profile in Maven is an alternative set of configurations which set or override the default values (and sometimes each other) under certain circumstances. This simple gesture lends Maven the power to completely alter its build by a simple activation of a profile - and in this way adds a portability for a certain environment. In other words - profiles turn a non-portable project into an environmentally portable one.
POM Profiles
The Maven POM contains an element under project called profiles containing a project's alternate configurations. The elements below have the same definitions as the project-level elements - see the Appendix on the POM for details.
<project>
...
<profiles>
<profile>
...
<reporting>...</reporting>
<modules>...</modules>
<dependencies>...</dependencies>
<dependencyManagement>...</dependencyManagement>
<distributionManagement>...</distributionManagement>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<properties>...</properties>
</profile>
</profiles>
</project>Though a subset of the higher-level POM elements, it can often compose the bulk of a POM.
Activation
The activation element is the only strictly profile element. Activations contain different types of selectors - if the chosen selector is true of the environment being run in then the profile becomes active. If the profile is active then all elements override the corrosponding project-level elements (profile modules override project modules).
<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>Build
The Maven POM proper contains a specification for tweaking build parameters - the artifact's output name, configurations for plugins and modifications to the build lifecycle. The profile's build element contains a subset of the POM's main build element.
<project>
...
<profiles>
<profile>
<build>
<defaultGoal>install</defaultGoal>
<directory>${basedir}/target</directory>
<finalName>${artifactId}-${version}</finalName>
<filters>
<filter>filters/filter1.properties</filter>
</filters>
<resources>
<resource>
<targetPath>META-INF/plexus</targetPath>
<filtering>false</filtering>
<directory>${basedir}/src/main/plexus</directory>
<includes>
<include>configuration.xml</include>
</includes>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource>...</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.1</version>
<extensions>false</extensions>
<inherited>true</inherited>
<configuration>
<sourceRoot>${project.build.directory}/generated-sources</sourceRoot>
</configuration>
<dependencies>
<dependency>
<groupId>ant</groupId>
<artifactId>ant-optional</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>echodir</id>
<goals>
<goal>run</goal>
</goals>
<phase>verify</phase>
<inherited>false</inherited>
<configuration>
<tasks>
<echo>Build Dir: ${project.build.directory}</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>...</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project>In other words, profile's build contains the POM's standard build elements, sans extensions and the directory definition elements {sourceDirectory, scriptSourceDirectory, testSourceDirectory, outputDirectory, testOutputDirectory}.
External Profiles
Before wrapping up POM profiles, one last note. There may be occasions when you wish to externalize the profiles from the POM. For these scenarios, profiles.xml was created. Just place all profiles elements into the file in the ${basedir} and run as normal. If you wish to see the source of the active profiles, run the maven-help-plugin.
mvn help:active-profiles
The output will be something like this:
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)
Note two things:
- Having an external profiles.xml profile does not preclude you from declaring profiles within the POM.
- There is a third source for profiles - the system's or user's settings.xml file. We will cover that next.
Settings Profiles
Although somewhat similar in ideology (they both offer alternative values) project and settings profiles are different in form and function. Whereas project profiles concern themselves with overriding the values of a specific set of system criteria, settings profiles are concerned with the system as a whole - because of this, they are a subset of the project's profiles.
The activation element is the same as the project's activation above. One extra level of activation in settings is the activeProfiles list element. Any profile ID set will automatically be activated for all builds using that settings.xml.
<settings>
...
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>
</settings>Note that these will activate settings profiles only, not project profiles of matching names. However, there are tricks you can do to activate environment-specific profiles easily.
The id and activation are just an identifier and profile selector. The only real system-wide use of profiles is to set the repositories to use and runtime properties - exemplified by repositories and pluginRepositories, and properties, respectively.
Tips and Tricks
Once you understand the elements and concept, there is not much to profiles. All of the interesting things are around the tricks you can do with them.
Environment
The core motivation for project profiles were to enact environment-specific behaviors for project - and the simplest way to mark a specific environment is by creating a common property for activation. The example below shows how to set an environment.type property for the dev environment:
<settings>
<profiles>
<profile>
<activeByDefault>true</activeByDefault>
<properties>
<environment.type>dev</environment.type>
</properties>
</profile>
</profiles>
</settings>Then all with profiles with a matching POM activations will be triggered when built on that specific machine.
<project>
...
<profiles>
<profile>
<id>dev</id>
<activation>
<property>
<name>environment.type</name>
<value>dev</value>
</property>
</activation>
<!-- Development Environment Values -->
</profile>
</profiles>
</project>Moreover, you can activate a profile by its absence as well. For example:
<project>
...
<profiles>
<profile>
<id>dev</id>
<activation>
<property>
<name>!environment.type</name>
</property>
</activation>
</profile>
</profiles>
</project>Note the bang (!) - or not - character prefixing the property name. This profile is activated when no ${environment.type} property is set.
Classifiers
Although profiles are excellent for altering builds for seperate environments, how can you differentiate between those builds? Classifiers are a powerful answer to this question. There are several ways to create an artifact with a classifier - the most common are via the maven-assembly-plugin or the maven-jar-plugin. There are two methods for setting a classifier in this way: configuring the plugin within the profile, or under the project plugin configuration specified by a property. which you choose depends heavily upon how much duplication is required for each profile. For example, the following will build a jar classified for Windows or Linux, depending on the building environment:
<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>This works, but is a lot of redundancy for each additional environment. The easier way is to set a common property:
<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 either case - the JAR artifact will be named ${finalName}-${envClassifier}.jar and usable like:
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
<classifier>linux</classifier>
</dependency>Summary
Profiles are a useful way for increasing the portability of a Maven project across environments with different setups - activated by a defined set of criteria.
Previous: Chapter
8 |
Up: Chapter 8 |
Next: Chapter 9 |