How to negate a property in pom.xml? - maven

I'd like to negate a boolean property as defined in pom.xml.
Meaning, given a property ${doSomething} I need to pass <skip> to a plugin where the value of <skip> should be the negation of ${doSomething}.
If ${doSomething} is false, then I want to skip. If it is true, then I don't want to skip. Any ideas?
Clarification: I am not talking about using the negation of a property for profile activation. I just want to pass the negation of a boolean into a plugin.

Apart from profile activation, Maven doesn't have a boolean logic implemented. So if want to negate a property to pass it to a plugin, you'll need to do it yourself. It is a bit clumsy, but you could use the build-helper-maven-plugin:bsh-property goal, which enables to write a BeanShell script and export variables defined in it as Maven properties:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.12</version>
<executions>
<execution>
<id>negate-prop</id>
<phase>initialize</phase>
<goals>
<goal>bsh-property</goal>
</goals>
<configuration>
<source>dontDoSomething = !${doSomething};</source>
<properties>
<property>dontDoSomething</property>
</properties>
</configuration>
</execution>
</executions>
</plugin>
You can't override the property, but you can define a new one containing the result of the negation; in the example above, it is dontDoSomething. This is ran in the initialize phase so that the rest of the plugins can use it as a parameter, with the standard ${dontDoSomething}.
This could be enhanced to have a default value for dontDoSomething if doSomething doesn't exist.
<source>
value = project.getProperties().getProperty("doSomething");
dontDoSomething = value == null ? false : !Boolean.parseBoolean(value);
</source>
BeanShell is a scripting language that looks very much like Java and you can use existing Java methods. In the above, the property "doSomething" is retrieved from the project's properties (project is injected by the plugin at evaluation-time with the current Maven project); it it isn't defined, we return false, otherwise, we negate the value.
If doSomething is specifically a system property, it could also be possible to (ab)use the profile activation feature and have 2 profiles: one activated by a property being true and setting another to false, and a second profile doing the inverse:
<profiles>
<profile>
<id>pro-1</id>
<activation>
<property>
<name>doSomething</name>
<value>!false</value>
</property>
</activation>
<properties>
<dontDoSomething>false</dontDoSomething>
</properties>
</profile>
<profile>
<id>pro-2</id>
<activation>
<property>
<name>doSomething</name>
<value>false</value>
</property>
</activation>
<properties>
<dontDoSomething>true</dontDoSomething>
</properties>
</profile>
</profiles>
This won't work if doSomething is a Maven property set in the <properties> tag for example. It will need to be passed as a system property with mvn -DdoSomething=true|false. The corresponding profile will be activated according to the value of the system property, which will define the dontDoSomething property to its inverse. If the property isn't defined, pro-1 will be active, setting dontDoSomething to the default value of false. All of this is quite ugly though...

I don't know the exact answer but I think you could try the following to invert the properties value (from http://maven.apache.org/guides/introduction/introduction-to-profiles.html). Declare another property where you invert the value of the first property:
<property>
<name>dontDoSomething</name>
<value>!${doSomething}</value>
</property>

Related

Maven profile filtering with lists

I'm using Maven Filtering to create a property file that would have specialized properties for a given stack.
I have a application.properties file with a property like
elasticsearch.members=${elasticsearch.members}
I have two config files
config.ppd.properties which has
elasticsearch.members=ppd-es-01:9700;ppd-es-02:9700;ppd-es-03:9700
config.prod.properties which has
elasticsearch.members=prod-es-01:9700;prod-es-02:9700;prod-es-03:9700
I have 2 sets of servers with the aliases ppd-es-nn and prod-es-nn
This all works great. When I generate the artifacts using the ppd profile, I get the correct property in the application.properties file.
But what I'd really like to do is have this concatenation somehow done in the application.properties file. So, I could specify the number of elasticsearch servers and a pattern in the application.properties file.
elasticsearch.members=${environment}-es-[index]:9700;...
Is there some way I can achieve that? Perhaps subclassing the class that does the filtering? This example is slightly contrived since I wanted to keep it simple. But one of the things I want to do is append the deployment mode (blue or green) to each server alias. And while I can do that in the filter file, it would be better to do it in the application.properties file so that no typo in the filter class could mess up the alias.
I like the groovy-maven-plugin for tasks like this. The below is untested, but should give you the idea.
<properties>
<!-- these may be overridden on the cmdline -->
<target.env>ppd</target.env>
<es.server.count>2</es.server.count>
<properties>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<version><latestPluginVersionHere></version>
<executions>
<execution>
<id>build-es-host-list</id>
<!-- phase must be earlier than process-resources -->
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
<![CDATA[
def sb = new StringBuilder()
def targetEnv = project.properties.getProperty('targetEnv')
def numServers = project.properties.getProperty('es.server.count')
(1..numServers).eachWithIndex { it, idx ->
sb.append("${targetEnv}-es-${idx}:9700")
if (idx < numServers) {
sb.append(';')
}
}
project.properties.setProperty('elasticsearch.members', sb.toString())
]]>
</source>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.12</version>
</dependency>
</dependencies>
</plugin>
Then, the application.properties file line needs to use the property:
elasticsearch.members=${elasticsearch.members}
The POM needs to enable resource filtering for the calculated value to be used in the file.

Maven property referencing a list

I am working with a (custom, internally developed, details do not matter here) Maven plugin which takes a list of names, like this:
<configuration>
<names>
<name>Alice</name>
<name>Bob</name>
<name>Joe</name>
<name>Sally</name>
</names>
...
The list is hard-coded in the POM file.
I'd like to have something a little more dynamic, so that I can use a user-defined variable to specify a single name. I can do it this way:
mvn clean package -Dname=Bob
<configuration>
<names>
<name>${name}</name>
</names>
The problem is that the only way I've found so far to do this is to have two profiles with duplicate plugin configurations: one with the pre-defined list, and one with the single user-defined value:
<profiles>
<profile>
<id>default</id>
...
<configuration>
<names>
<name>Alice</name>
<name>Bob</name>
<name>Joe</name>
<name>Sally</name>
</names>
...
</profile>
<profile>
<id>single-value</id>
<activation>
<property>
<name>name</name>
</property>
</activation>
...
<configuration>
<names>
<name>${name}</name>
</names>
...
</profile>
</profiles>
This works, but my problem with it is that both profiles have almost completely identical configurations (and they are rather lengthy configurations as well) for this plugin, except for the list of names.
What I'd like to do is have a Maven property contain a list, rather than a single value. Something like this:
<project>
<properties>
<names>
<name>bob</name>
</names>
</properties>
...
So that my single-value profile will simple redefine the names property. But of course, this doesn't work so easily: the code sample above produces a non-parseable POM error.
Is there any way to do what I want with some other Maven plugin, or trick?
Use a parameter which takes a comma-separated value. In your code, translate this String to a List of Strings. You should really wonder if you want this to be set by commandline or if it is sufficient enough to do it only in the plugin configuration. The latter is preferred of course.
Just as a small example: see http://maven.apache.org/plugins/maven-site-plugin/site-mojo.html#locales

CATALINA_HOME and Maven + LOG4J

I have this line in log4j:
log4j.appender.FILE.File=${catalina.home}/logs/debug.log
Works perfectly when I run the project from IntelliJ.
But when I try to run a TestNG test (from maven) it fails:
log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /logs/debug.log (No such file or directory)
I could hardcode the path and all will be good. But I don't want that solution since I can deploy on various systems where ${catalina.home} is in different place.
I develop on a mac and deploy on freebsd and centos. Tomcat is in different places all the time. I could use /var/log/myapp.log but ...
Is any way to define a common variable (available in IntelliJ and when I run the maven test) with the log file path?
Please try to use the Maven Profile which will be activated when the ${env.catalina.home} is not existed, together with the Maven Surefire Plugin:Using System Properties.
<profile>
<id>mock-catalina-home</id>
<activation>
<property>
<name>!env.catalina.home</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.14</version>
<configuration>
<encoding>UTF-8</encoding>
<systemProperties>
<property>
<name>catalina.home</name>
<value>PATH_TO_CATALINA_HOME</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</profile>
Please note that the PATH_TO_CATALINA_HOME can be referred by the Maven Properties as well. e.g.
<systemProperties>
<property>
<name>PATH_TO_CATALINA_HOME</name>
<value>${my.dev.catalina.home}</value>
</property>
</systemProperties>
This will help us to define the ${my.dev.catalina.home} to be various values.
I hope this may help.
In order for the log4j properties file get the correct value for ${catalina.home} when running the tests from maven, it needs to be in a file that is filtered by maven (src/main/resources is a directory for files like that). Also, the variable 'catalina.home' needs to be setup in maven. You can create a variable AKA maven property that uses an environment variable so you can define the different location for the tomcat install on each machine:
<properties>
<catalina.home>${env.catalina.home}</catalina.home>
<properties>
You can also specify the location of the log files in relation to the home directory for your user account.
In log4j this would use the following format: ${user.home}/weblogs/log4j1.log
In log4j 2, this would use the following format: ${sys:user.home}/weblogs/log4j2Rolling.log
log4j 2 can use the Java system properties, environmental variables, or Maven properties

Maven Multiproject Profile activation via property

I have a problem to activate a profile. There is a master pom with the profile:
<profile>
<id>EntityUpdater</id>
<activation>
<property>
<name>entityupdater.start</name>
<value>true</value>
</property>
</activation>
....
In my childpom (jar package) I specify the property:
<properties>
<!-- ENTITY UPDATER CONFIG -->
<entityupdater.start>true</entityupdater.start>
....
But in the build process the profile does not start.
I would be very happy if anyone could help me.
Best regards, Daniel
Unfortunately, Maven can only trigger property based profiles, if you are passing them in from the command-line as JVM args:
mvn clean package -Dentityupdater.start

Control Maven Reactor Modules with External Property File

My question is similar to this: How to exclude a module from a Maven reactor build?; however what I'd like to do is define a way of turning off-on modules based on a property. This property will be defined in an external property file we're using as our build profile.
The problem is that the property file isn't read in until the lifecycle after the Reactor is run (it appears Reactor is always run first).
Something like:
propertyfile.properties
module1.enabled = true
module2.enabled = false
module3.enabled = true
pom.xml
<profiles>
<profile>
<id>module1</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>module</name>
<value>true</value>
</property>
</activation>
</profile>
...
</profiles>

Resources