Category Archives: Maven

Maven Profile Inheritance (okay, DISinheritance)

One best practice with the Maven build system is to take configuration that’s common to many projects and put it into a parent pom.xml.

Unfortunately, parent pom inheritance has some limits.  One of them is that profiles aren’t inherited.

You may be forgiven for thinking that profiles ARE inherited.  Not only does it seem like a logical (some would say necessary) feature of Maven, you might have seen it working… or so you thought.   See, profiles in parent poms can be triggered by the building of a child. When activated, they are applied directly to the parent pom, prior to that parent being used for inheritance into the child. So the effects of an active profile in a parent pom may be felt by the child.

Sometimes this indirect inheritance works well.  Sometimes, it’s just crack-addled.

When It Works…

Here’s an example where it works well. Say you have a parent pom with a ‘prod’ profile whose job is to insert some properties into the build.   On a production system, Maven will activate that profile and the parent pom will contain the properties.  Since properties are inherited from parents, your child pom will get them.  The end effect is just like the profile was inherited, but in fact the profile was NOT inherited.

… and When It Doesn’t

Here’s an example where it doesn’t.  Say your parent pom has a ‘prod’ profile that adds a module for publishing your app to a webserver.  On your production system, Maven will activate that profile and the parent pom will contain the module.  However, that module will try to publish the parent to the webserver, not your child project.

If you simply insist on trying to “inherit” plugins via a profile, you’d have to:

  1. Set up the profile in the parent pom, but instead of defining the module in a <plugin> section, define it in a <pluginManagement> section.  The pluginManagement is explicitly for declaring plugins that aren’t used in the parent but need to be propagated to the child.
  2. Set up the profile again in the child pom (yes, copy and paste).  Within the profile, REdeclare the plugin  (in a <plugin> section not <pluginManagement>), but without all the configuration.  This will activate the plugin defined in the parent.

Yes, it sucks that you have to double-declare things.   I try to find other solutions if at all possible.

Maven: activation conditions really DON’T work as advertised

Many people using the Maven build system want to conditionally trigger parts of the build:   say, add a .dll only when on Windows, or add a library only when some file is missing.

This is why Maven profiles exist. Profile activation conditions are the mechanism for turning on a profile based on conditions like operating system and missing files. However, they work nearly opposite of how they are documented to work.

Despite all the examples in the official docs, profile activation conditions aren’t ANDed together, but ORed. For example, you might want to activate a profile when on Windows AND a file is missing.  You can’t:  the profile will activate if either condition is true.

This is true in version 3.0.2 and has been thus ever since Maven 2.1: you can see the bug ticket where someone “fixed” it the wrong way. This is an egregious flaw that makes activation conditions unusable for many common use cases.

Working Around It Using Groovy

One workaround is to dip into the Groovy scripting language to do multiple conditions.

In the Maven pom.xml sample below, you can see that I’m using a combination of profile activation (to test for the existence of a file) and Groovy conditionals (to test for the operating system). The profile activation isn’t necessary, really; I could have done the whole thing with Groovy conditions.

<profiles>
	<!-- a Maven profile that does something when a particular file is missing from the build -->
	<profile>
		<id>install-shared-resources</id>
		<activation>
			<file>
				<!-- Activate this profile when there's no src/main/some-file yet -->
				<missing>src/main/some-file</missing>
			</file>
		</activation>
		<build>
			<plugins>
				<!-- Use the Groovy scripting language detect the OS and do something OS-specific -->
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>groovy-maven-plugin</artifactId>
					<version>1.3</version>
					<executions>
						<execution>
							<phase>package</phase>
							<goals>
								<goal>execute</goal>
							</goals>
							<configuration>
								<source>
									// True for any POSIX compliant operating system such as AIX,
									// HP-UX, Irix, Linux, MacOSX, Solaris or SUN OS
									if (org.apache.commons.lang.SystemUtils.IS_OS_UNIX)
									{
										// do something like execute a shell script
									}
									// Else if Windows 7 or Vista
									else if (org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS_VISTA ||
											 System.properties['os.name'].toLowerCase().contains('windows 7'))
									{
										// do something like execute a shell script
									}
									// Else it's an unsupported OS (in this case, like Windows XP)
									else
									{
										fail("\nYou're on an unsupported operating system: " + System.properties['os.name']);
									}
				        		</source>
							</configuration>
						</execution>
					</executions>
				</plugin>
			</plugins>
		</build>
	</profile>
</profiles>