30

I am creating a maven 2 build for a project and I came up with profiles since the build has to be created for both different locations (say Berlin, Paris, North Pole) and different environment (Development, Production). Those are specified via properties. So for "North Pole" "DEV" I do:

-Dlocation=NorthPole -Denvironment=DEV

Now I would like to acivate my porfile based on both these properties, not just one. So I tried following:

<profiles>
  <profile>
    <id>NOrth Pole DEV</id>
    <activation>
      <property>
        <name>location</name>
        <value>NorthPole</value>
      </property>
      <property>
        <name>environment</name>
        <value>DEV</value>
      </property>
    </activation>
    ... <!-- Set some North Pole DEV specific stuff -->
  </profile>
</profiles>

This doesn't work, maven expect to see at most one <property> element there.

Please note I have another use for the properties as well so making it single property locationEnvof value NorthPole-DEV isn't what I want to have.

So is there any way or workaround or whatever else how to activate an profile based on combination of properties?

Jan Zyka
  • 17,460
  • 16
  • 70
  • 118
  • You are activating **Build** [**Profiles**](https://maven.apache.org/guides/introduction/introduction-to-profiles.html) form CommandLine with **multiple profile-ids** using **system property** **`-D`** `mvn install -Dactivationprop1 -Dactivationprop2`, the answer shows using **`-P`** `mvn install -P profileid1 -Pprofileid2`, uing [`comma-delimited list`](https://stackoverflow.com/q/16792499/5081877) form PowerShell `mvn install -P "profile-1,profile-2"`. – Yash May 31 '19 at 12:28

6 Answers6

14

I am afraid there is no good solution to your problem (unless there are new Maven features I am not aware of).

In theory, you could introduce a derived property whose value is concatenated from the two properties you listed. However, the problem is that profiles are evaluated before properties defined in the pom, so such a derived property can't be used to activate a profile :-(

The best workaround I could think of for a similar problem was to activate the profile explicitly, and put the different combinations of command line parameters into separate batch/script files to make execution simpler and avoid mistyping issues.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • You are right, it turned out that going directly with profiles and set the properties in them (just as if they were set via commandline) will be the best option here. Thanks for answer anyway! – Jan Zyka Mar 25 '11 at 09:32
  • example might be nice... :) – rogerdpack May 21 '18 at 19:50
12

why not using profile directly like:

<profiles>
   <profile>
    <id>north-pole</id>
    <activation>
      <activeByDefault>false</activeByDefault>
    </activation>
    ....
  </profile>
   <profile>
    <id>dev</id>
    <activation>
      <activeByDefault>false</activeByDefault>
    </activation>
    ....
  </profile>
</profiles>

Now you can activate the profiles by command line.

mvn -Pdev,north-pole ...
khmarbaise
  • 92,914
  • 28
  • 189
  • 235
  • 2
    Sure but if I have config file NorthPole-DEV.xml and NorthPole-PROD how would I know which one to use in your scenario? I need to decide based on both location and environment and act upon them – Jan Zyka Mar 24 '11 at 11:27
  • 1
    Since I was converting the ANT build into maven I probably was too fixed on using commandline properties. It turned out that it is really better to do it via profiles, set required properties in them and forget the properties at all. Sorry for missleading question. Marking this as correct answer. – Jan Zyka Mar 25 '11 at 09:31
  • Could you clarify what should be expected if the 2 profiles define different values for the same property? Which one is taken? – Ivaylo Slavov May 11 '12 at 07:00
  • 2
    That does not solve the problem because second property will trigger the profile even thought first is not set (it matters in my case). -1 for typical useless Maven advice (eg. "do it Maven way and make everything dumb, bloated & unmaintainable"). – woky Aug 09 '12 at 15:00
  • 1
    @worky If you are the opinion is is useless than you should improve maven in a way you think it would be better. – khmarbaise Oct 12 '16 at 10:45
5

Possible Solution

Try this extension: https://github.com/kpiwko/el-profile-activator-extension

This allows to have such syntax:

<profile>
    <id>NOrth Pole DEV</id>

    <activation>
        <property>
            <!-- mvel property name is obligatory -->
            <name>mvel</name>
            <value>isdef location &amp;&amp; location=="NorthPole" &amp;&amp; 
                   isdef environment &amp;&amp; environment=="DEV"</value>
        </property>
    </activation>
</profile>

I did not try it myself, but seems to be a nice project.

How to avoid manual configuration of Maven

You need to put the needed two jars of the project into $MAVEN_HOME/lib/ext. You can however automize configuring them. Like this:

  • You can add a profile which is activated on absense of $MAVEN_HOME/lib/ext/el-profile-activator-extension.jar file
  • This profile can download the jars from maven using dependency plugin into the $MAVEN_HOME/lib/ext folder in init phase
  • Then you can write out a message, that the build configured the maven folder, and the next build will be successful.

Tested profile:

<profile>
    <id>prepare-maven-extended-libs</id>
    <activation>
      <file>
        <missing>${maven.home}/lib/ext/el-profile-activator-extension.jar</missing>
      </file>
    </activation>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.8</version>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>com.redhat.jboss.maven</groupId>
                                    <artifactId>el-profile-activator-extension</artifactId>
                                    <version>1.0.0-SNAPSHOT</version>
                                    <type>jar</type>
                                    <overWrite>true</overWrite>
                                    <outputDirectory>${maven.home}/lib/ext</outputDirectory>
                                    <destFileName>el-profile-activator-extension.jar</destFileName>
                                </artifactItem>
                                <artifactItem>
                                    <groupId>org.mvel</groupId>
                                    <artifactId>mvel2</artifactId>
                                    <version>2.1.3.Final</version>
                                    <type>jar</type>
                                    <overWrite>true</overWrite>
                                    <outputDirectory>${maven.home}/lib/ext</outputDirectory>
                                    <destFileName>mvel2.jar</destFileName>
                                </artifactItem>
                            </artifactItems>
                            <outputDirectory>${project.build.directory}/wars</outputDirectory>
                            <overWriteReleases>true</overWriteReleases>
                            <overWriteSnapshots>true</overWriteSnapshots>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals><goal>execute</goal></goals>
                    </execution>
                </executions>
                <configuration>
                    <source>
                        fail("For profile activation we use an extension jar. It is now in your ${maven.home}/lib/ext folder. Please restart the build, and then it will be successful.")
                    </source>
                </configuration>
            </plugin>               
        </plugins>
    </build>
</profile>
Gábor Lipták
  • 9,646
  • 2
  • 59
  • 113
1

khmarbaise's answer seems more elegant to me. To Jan's comment, you can refer to the file by appending the properites e.g. with profile dev, North Pole activated you can refer to NorthPole-dev.xml with ${location}-${env}.xml.

I had to post another reply as I'm not able to add comments to other's replies. :(

Prabhjot
  • 695
  • 3
  • 8
  • 21
0

I believe you can do something like this

<properties>
        <env>dev</env>
        <location>North Pole</location>
    </properties>

<profiles>
        <!-- dev North Profile -->
        <profile>
            <id>dev North Pole</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <!-- qa North Profile -->
        <profile>
            <id>qa North Pole</id>
            <properties>
                         <env>qa</env>
                             <location>North Pole</location>
            </properties>
        </profile>

    </profiles>
<build>
do profile specific stuff here
</build>

Of couse, to activate a profile you can add in the command '-P=dev North Pole'

Prabhjot
  • 695
  • 3
  • 8
  • 21
  • Hm, but this is for the case there is North Pole only. In such case I wouldn't need a property for it at all. right? :) There are other locations as well sa described in the question. – Jan Zyka Mar 24 '11 at 11:03
  • Yes, you can add other profiles as per your requirement.Essentially, location and env properties are being set as per the profile selected, thus you won't need a separate properties file. – Prabhjot Mar 24 '11 at 11:18
-3

After an exhaustive investigation I've posted a video where I explain the usage of Maven profiles per environment with Spring Boot. That is a spring boot rest project that handle the application properties per environment using Maven profiles.

Here are the links:

Youtube: https://youtu.be/UbDpvh3YvDw

Github: https://github.com/carlosCharz/mavenprofilespringboot

Code snippet:

Application parameters

custom.server_url = @custom.server_url@

custom.server_port = @custom.server_port@

custom.debuggable = @custom.debuggable@

custom.image_quality = HIGH

Overrides parameters

custom.server_url = api-dev.yourserver.com

custom.server_port = 80

custom.debuggable = true

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.wedevol</groupId>
<artifactId>mvnspringboot</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<name>Spring Boot Project with Maven</name>
<description>This is a spring boot rest project that handle the application properties per environment using Maven profiles.</description>

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes -->
</parent>

<dependencies>
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

<!-- Maven profile per environment -->
<profiles>
    <profile>
        <id>local</id>
        <activation>
           <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <overrides.props.file>local.overrides.properties</overrides.props.file>
            <current.profile>local</current.profile>
        </properties>
    </profile>
    <profile>
        <id>dev</id>
        <properties>
            <overrides.props.file>dev.overrides.properties</overrides.props.file>
            <current.profile>dev</current.profile>
        </properties>
    </profile>
    <profile>
        <id>qa</id>
        <properties>
            <overrides.props.file>qa.overrides.properties</overrides.props.file>
            <current.profile>qa</current.profile>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <overrides.props.file>prod.overrides.properties</overrides.props.file>
            <current.profile>prod</current.profile>
        </properties>
    </profile>
</profiles>

<build>
    <finalName>mvnspringboot</finalName>
    <!-- Maven Resources. It handles the copying of project resources to the output directory. -->
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <excludes>
                <exclude>profiles/*</exclude>
            </excludes>
        </resource>
    </resources>
    <!-- Maven filtering. The variables are included in the resources ( ${..} or @...@ delimiters) -->
    <filters>
        <filter>src/main/resources/profiles/${overrides.props.file}</filter>
    </filters>
    <plugins>
        <!-- Spring boot maven plugin -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!-- Ant plugin to print the current maven profile -->
        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
                <execution>
                    <phase>generate-resources</phase>
                    <goals>
                        <goal>run</goal>
                    </goals>
                    <configuration>
                        <tasks>
                            <echo>Current maven active profile: ${current.profile}</echo>
                        </tasks>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Let me know if it worked! Gretings!