How to enforce the use of exactly one out of two Maven profiles?
Asked Answered
A

5

16

I have a Maven project that defines two separate profiles, developer and release (surely you get the drift, here). I want one of these two profiles to be activated at any time, but never both. If both are somehow activated, this build makes no sense and should fail. If neither is activated, this build also makes no sense and should fail.

I'm sure I can write some custom plugin code to achieve this, and I might very well end up going that way, but I'd be interested in achieving this using POM configuration (could be using existing plugins from Maven Central).

It should be possible to activate plugins using -P (--activate-profiles) so <activation> through properties would not be a valid solution. Solutions using activeByDefault would not be valid either, since activeByDefault is generally known as a pitfall, unreliable (and we may in fact activate other profiles, thus rendering activateByDefault unusable).

Your suggestions much appreciated.

Atalie answered 20/7, 2014 at 22:54 Comment(0)
H
5

The simplest solution for this kind of problem would be to use the maven-enforcer-plugin which exactly has such a rule to force to activate at least one of two or more profiles.

Unfortunately the requireActiveProfile has currently a bug. But currently a preparation for a new release is on going which solves this.

Update The bug mentioned above has been fixed in release 1.4 (which was released in 2015).

Housum answered 21/7, 2014 at 9:59 Comment(14)
Thank you. I looked at the enforcer plugin before, and would never have guessed this rule to handle that, nor was its link clickable. Upon further googling I found this description, which doesn't suggest it handles the mutual exclusion part, does it?Atalie
The description is exactly the location. If you don't expect all of the given profiles being activated this means only one of them must be activated which is logically an or relationship. The link which is not clickable is an other fix for the new release.Housum
Thank you. But did you catch that I didn't want an OR, but an XOR?Atalie
Obviously i didn't catch it. So i need to think to implement an XOR handling as well.Housum
Are you contributing on the enforcer plugin? The all handles the AND case, vs. the OR case, only missing XOR :)Atalie
Curious what the status of these changes to the Enforcer plugin is?Atalie
Just take a look into the jira here: jira.codehaus.org/browse/…Housum
Thanks. I won't try to make this into a discussion about your good work for this plugin, but let me at least acknowledge the appreciation for good open source work being done on Maven.Atalie
Sorry for communicating through here (there surely must be a better place to ask this). But I am getting to the point where I need this functionality, so I am about to pull the latest code and build my own (internal) release. But I could wait a little longer if it is perhaps about to be released? :)Atalie
I ended up writing my own rule for the Maven Enforcer Plugin.Atalie
can you share that please ? :DCommunity
Maven enforcer plugin 1.4 has been released on 24 januari 2015, with the "'All' property is not handled by RequireActiveProfile rule" bug fixed.Farleigh
My KISS solution is available on Bintray or GitHub.Atalie
@KeesvanDieren If you like to enhance that please add a new jira for maven-enforcer-plugin see issues.apache.org/jira/browse/MENFORCERHousum
A
7

I had a similar need (i.e. for mutual exclusivity of two profiles) and solved it by considering the two target profiles to be internal profiles that shouldn't be specified on the command line: Instead, a controlling system property can either be specified or not. E.g. let's assume that by default you want the "dev" profile to be active. We can then activate/deactivate the relevant internal profiles based on whether the -Drelease option is specified as follows:

<!-- Internal profile: FOR INTERNAL USE ONLY - active if -Drelease is *not* specified. -->
<profile>
  <id>internal-dev</id>
  <activation>
    <!-- Activation via *absence* of a system property to ensure mutual exclusivity
         of this profile with internal-release -->
    <property>
      <name>!release</name>
    </property>
  </activation>
  ...
</profile>

<!-- Internal profile: FOR INTERNAL USE ONLY - active if -Drelease *is* specified. -->
<profile>
  <id>internal-release</id>
  <activation>
    <!-- Activation via *presence* of a system property to ensure mutual exclusivity
         of this profile with internal-dev -->
    <property>
      <name>release</name>
    </property>
  </activation>
  ...
</profile>
Agminate answered 12/1, 2016 at 12:41 Comment(2)
This is a perfectly reasonable way to go, it just does not guarantee that users will get the right profile. They could misunderstand your instructions and do a mvn -P release instead of mvn -D release in which case both profiles get enabled. I just wanted a way that ab-so-lu-tely guaranteed me that the profiles weren't enabled at the same time (as I didn't want to be debugging those problems for my users).Atalie
Sander, I'm using this same technique. It works well. Notice that neither profile has an id of release. Hence -P release will not activate either release directly, but the absense of -D release will cause the internal-dev profile to be activated. So the result is exactly what is desired, only one of these two will be activated.Adrenaline
H
5

The simplest solution for this kind of problem would be to use the maven-enforcer-plugin which exactly has such a rule to force to activate at least one of two or more profiles.

Unfortunately the requireActiveProfile has currently a bug. But currently a preparation for a new release is on going which solves this.

Update The bug mentioned above has been fixed in release 1.4 (which was released in 2015).

Housum answered 21/7, 2014 at 9:59 Comment(14)
Thank you. I looked at the enforcer plugin before, and would never have guessed this rule to handle that, nor was its link clickable. Upon further googling I found this description, which doesn't suggest it handles the mutual exclusion part, does it?Atalie
The description is exactly the location. If you don't expect all of the given profiles being activated this means only one of them must be activated which is logically an or relationship. The link which is not clickable is an other fix for the new release.Housum
Thank you. But did you catch that I didn't want an OR, but an XOR?Atalie
Obviously i didn't catch it. So i need to think to implement an XOR handling as well.Housum
Are you contributing on the enforcer plugin? The all handles the AND case, vs. the OR case, only missing XOR :)Atalie
Curious what the status of these changes to the Enforcer plugin is?Atalie
Just take a look into the jira here: jira.codehaus.org/browse/…Housum
Thanks. I won't try to make this into a discussion about your good work for this plugin, but let me at least acknowledge the appreciation for good open source work being done on Maven.Atalie
Sorry for communicating through here (there surely must be a better place to ask this). But I am getting to the point where I need this functionality, so I am about to pull the latest code and build my own (internal) release. But I could wait a little longer if it is perhaps about to be released? :)Atalie
I ended up writing my own rule for the Maven Enforcer Plugin.Atalie
can you share that please ? :DCommunity
Maven enforcer plugin 1.4 has been released on 24 januari 2015, with the "'All' property is not handled by RequireActiveProfile rule" bug fixed.Farleigh
My KISS solution is available on Bintray or GitHub.Atalie
@KeesvanDieren If you like to enhance that please add a new jira for maven-enforcer-plugin see issues.apache.org/jira/browse/MENFORCERHousum
S
5

This can still be done with Maven Enforcer plugin

Although mutual exclusion, <requireActiveProfile>...<all>false</all>... is buggy as reported by @khmarbaise, there's still the <evaluateBeanshell/> built-in rule that lets one do whatever he wants.

I wrote one especially for this case: XOR of two profiles. I hope it helps.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>1.4.1</version>
                <executions>
                    <execution>
                        <id>enforce-PROFILE_ONE-XOR-PROFILE_TWO-is-active</id>
                        <goals>
                            <goal>enforce</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <requireActiveProfile>
                                    <profiles>PROFILE_ONE,PROFILE_TWO</profiles>
                                    <all>false</all>
                                </requireActiveProfile>
                                <evaluateBeanshell>
                                    <condition><![CDATA[
                                        // ensure PROFILE_ONE XOR PROFILE_TWO
                                        print("Checking if only one of PROFILE_ONE and PROFILE_TWO profiles is active ...");
                                        boolean profile1 = false, profile2 = false;
                                        for(s: "${project.activeProfiles}".replaceAll("\\[?\\s?Profile \\{id: (?<profile>\\w+), source: \\w+\\}\\]?", "${profile}").split(",")) {
                                            if("PROFILE_ONE".equalsIgnoreCase(s)){ profile1 = true;}
                                            if("PROFILE_TWO".equalsIgnoreCase(s)){ profile2 = true;}
                                        }
                                        print("PROFILE_ONE XOR PROFILE_TWO: "+(profile1 != profile2));
                                        return profile1 != profile2;
                                    ]]></condition>
                                </evaluateBeanshell>
                            </rules>
                            <failFast>true</failFast>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

The tricky part is looping over active profiles, which I've already done here. You can extend it to more than two profiles if you need. But you'll have to write the long xor expression, since beanshell doesn't implement Java xor ^ operator.

Sola answered 16/8, 2017 at 17:0 Comment(1)
This works great and is quite simple. I didn't feel like going through the trouble of writing my own rule.Abaft
W
3

I always issue a build command like so:

mvn package -P-dev,prod

It explicitly disables the dev profile and enables the production one. To my knowledge, you can not conditionally enable one build profile if another is active (which is a bit unfortunate), and because of that you can't ensure that the profiles are mutually exclusive.

Wheat answered 20/7, 2014 at 23:8 Comment(2)
Thank you for your answer. I'm aware of what you're doing here, but I want to make this as fool-proof as possible for a whole community of developers that would be newly-exposed to Maven. If they can do this, they can also provide the right profile to begin with, but I just want to help them doing it right.Atalie
Good point. I agree that this is not as simple as it could be. I'm curious to see if anybody has a better solution.Wheat
A
1

I needed a slightly more advanced version of this rule. I ended up writing it myself. I've submitted a patch to them that includes the following 2 rules:

  • The ability to specify a set of mutually-exclusive profiles (p1,p2:p1,p3 would mean p1 can't be active with either p2 or p3).

  • The ability to ban profiles (the contrary of requireActiveProfile). p1, p2 would mean neither p1 nor p2 can be active for this build.

Both of these rules support wildcards and consider inherited profiles as well. These are built on v1.4 of the rules.

http://jira.codehaus.org/browse/MENFORCER-225

Abomb answered 21/2, 2015 at 18:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.