Dynamic maven artifactId - scala

Is it possible for a POM to declare (or at least publish) an artifactId containing system properties? I mean the artifactId of the actual project, not dependencies.
I am using maven to build a scala project and thus, to allow publishing the project for different scala versions, in the pom.xml I'd like to declare:
<artifactId>myproject_${scalaBinaryVersion}</artifactId>
however maven 3.3. complains
[WARNING] 'artifactId' contains an expression but should be a constant
Since I'd like this project to be interoperable with sbt, what would be the best way to publish an artifact suffixed with the scala binary version?

The Maven way of doing so would be to use classifiers. From official documentation an example matches exactly your case (for different Java versions, but you can replace Java with Scala):
The classifier allows to distinguish artifacts that were built from the same POM but differ in their content. It is some optional and arbitrary string that - if present - is appended to the artifact name just after the version number.
As a motivation for this element, consider for example a project that offers an artifact targeting JRE 1.5 but at the same time also an artifact that still supports JRE 1.4. The first artifact could be equipped with the classifier jdk15 and the second one with jdk14 such that clients can choose which one to use.
You can configure your POM as following:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>${scalaBinaryVersion}</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Note: we are adding an additional execution of the Maven Jar Plugin, so the project would create two jars, the normal one + an additional one with the specified (dynamic) classifier.
Then Maven will automatically publish the classified jar together with the normal jar (since it will be automatically attached to the build). You can then import it as a further Maven dependency in another project specifying its classifier as part of the Maven GAV (GAVC in this case):
<dependency>
<groupId>your.group.id</groupId>
<artifactId>your.constant.artifact.id</artifactId>
<version>your.version</version>
<classifier>your.dynamic.classifier</classifier>
</dependency>
If you want to only build the classified one and no standard (unused) jar, you can skip the creation of the normal jar as following:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>default-jar</id>
<phase>none</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>scala-version-jar</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>${scalaBinaryVersion}</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Note: we are simply overriding the default execution of the Jar Plugin and binding it to a non existing phase. Hence Maven will only generate the classified Jar. The Install Plugin will then only install the classified one.
Update: how to have dynamic artifactId installed with dynamic dependencies
If different transitive dependencies are required for different dynamic versions, then indeed classifiers are not suitable.
Dynamic artifactIds with dynamic dependencies (and hence dynamic transitive dependencies) can however be achieved. Here below is the approach I used (and successfully tested):
As preference, I isolated the dynamic behavior in a profile, but you can obviously move it back to the default build (or have the profile active by default).
First of all, let's define in our pom the dependencies requiring a dynamic version, hence via properties as following:
<properties>
<scalaBinaryVersion>scalaversion</scalaBinaryVersion>
<dependency.version>4.11</dependency.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${dependency.version}</version>
</dependency>
</dependencies>
Note: for the sake of an example, I'm using Junit as dependency in this case and not in test scope, because I want it as compile dependency (again, just for this example).
Then let's define a profile for our dynamic behavior:
<profiles>
<profile>
<id>build-scala-version</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<finalName>${project.artifactId}_${scalaBinaryVersion}-${project.version}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-pom</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/${scalaBinaryVersion}</outputDirectory>
<resources>
<resource>
<directory>${basedir}</directory>
<includes>
<include>pom.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.1</version>
<executions>
<execution>
<id>replace-artifactid</id>
<phase>prepare-package</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<file>target/${scalaBinaryVersion}/pom.xml</file>
<token><artifactId>${project.artifactId}</artifactId></token>
<!-- Replace to -->
<value><artifactId>${project.artifactId}_${scalaBinaryVersion}</artifactId></value>
<outputDir>target\${scalaBinaryVersion}\replacer</outputDir>
<outputFile>pom.xml</outputFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<id>default-install</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
<execution>
<id>install-scala-version</id>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}_${scalaBinaryVersion}</artifactId>
<version>${project.version}</version>
<packaging>${project.packaging}</packaging>
<file>${project.build.directory}/${project.artifactId}_${scalaBinaryVersion}-${project.version}.jar</file>
<pomFile>${project.build.directory}/${scalaBinaryVersion}/replacer/pom.xml</pomFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Note, the profile is customizing and providing the following:
Changing the final Jar name with a dynamic name, depending on the runtime (aka dynamic) value, to ${project.artifactId}_{scalaBinaryVersion}-${project.version}
Filtering the existing pom file via the Maven Resources Plugin and copying it to the directory target\${scalaBinaryVersion}. The copied pom will have the dependencies with the dynamic version because the Resources Plugin will replace them. However, it will not have the dynamic artifactId (yet).
Finalizing the dynamic pom file. The Replacer plugin will replace the artifactId XML element with the dynamic value (working on the target folder, hence everything on temporarely files)
Skipping the generation of the default installation
Performing a custom install-file installation with the dynamic pom file (the filtered, copied and replaced one, providing dynamic dependencies (and as such dynamic transitive dependencies) and a dynamic artifactId
Hence, performing the following maven invocation:
mvn clean install -Pbuild-scala-version -DscalaBinaryVersion=hello -Ddependency.version=4.4
Maven will effectively install a new artifact in the local cache for the dynamic artifactId, the dynamic dependency version and the dynamic pom.
Note: if the concerned dependency version(s) and the dynamic scala version is the same, then you can save up a parameter and make the invocation shorter and more consistent.

if you are using maven for that I will saggest using multi pom with maven helper plugin so the artifactId is constant in each module.

We follow the properties (via profiles) based approach as suggested here: scala-maven-plugin FAQ
<artifactId>something_${scala.compat.version}</artifactId>
<properties>
<scala.compat.version>2.12</scala.compat.version>
</properties>
maven will still issue a warning (with good reason), but with the help of the flatten-maven-plugin we build/install poms that has the variables replaced.

Related

How to build a Maven project with Eclipse with a pom.xml that imports an external properties file?

I have looked around from various questions on stackoverflow, but I have not found the answer that solve my purpose.
I want to import a properties file in a pom.xml; my purpose is to replace the <properties> section with the properties loaded from the external file.
Each property refers to the version of a particular maven dependency.
I have tried the properties-maven-plugin, but the properties are not solved and the project is not built.
I'm looking for a way that preserve the standard build of Eclipse, and also the mvn install goal of Maven.
As an example, I want that this section:
<properties>
<dependency1.version>1.0.0</dependency1.version>
</properties>
will be replaced with the property declared in a dependency.properties file, like this one:
dependency1.version=1.0.0
How can I implement my pom.xml in order to obtain this behaviour?
Thanks in advance.
EDIT
This is what I have tried:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>${basedir}/dependencies.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>my version: ${dependency1.version}</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Well, if I do a simple mvn install, in maven console it prints:
[INFO] Executing tasks
[echo] my version: 1.0.0
[INFO] Executed tasks
So, the plugin solve the property, but when I try to import the dependency with this:
<dependencies>
<dependency>
<groupId>dependency1</groupId>
<artifactId>dependency1</artifactId>
<version>${dependency1.version}</version>
</dependency>
</dependencies>
I obtain this error:
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] 'dependencies.dependency.version' for dependency1:dependency1:jar must be a valid version but is '${dependency1.version}'. # line 21, column 13
#
The property is solved in the maven-antrun-plugin, but if it is used in the dependencies section, it doesn't work.

Include scala classes in the sources jar generated by maven package

The following plugin added to the pom.xml allows source-jar to be created when performing mvn package:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
The usage of the scala-maven-plugin is :
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmArgs>
<jvmArg>-Xmx12g</jvmArg>
</jvmArgs>
<args>
<arg>-feature</arg>
<arg>-deprecation</arg>
<arg>-target:jvm-1.7</arg>
</args>
</configuration>
</plugin>
However only the java sources are being included: the scala sources are left out. Note that we are using the standard maven directory layout. In particular we have scala sources here:
src/main/scala
So - are there additional options to the maven-source-plugin to encourage it to invite the scala classes to participate? Or a different scala-specific plugin and/or option to get them onboard?
The jar goal of the Maven Source Plugin will bundle all of the sources of the Maven project into a JAR. You can select what to include or exclude in those source folders (with the includes and excludes parameters), but you cannot add whole new source folders to it; they must be added as source folders of the Maven project itself.
When you have a pure Scala project, src/main/scala and src/test/scala are declared as source folders, because you would have:
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
in your POM, overriding the default src/main/java and src/test/java. So the Maven Source Plugin would correctly add the sources present in those two folders without additional configuration.
But when you have a mixed Java / Scala project, the <sourceDirectory> and <testSourceDirectory> element are typically left in to their default values. This does not create any problems with regard to compiling or running Scala code with the plugin, as it looks up the files by default in ${project.build.sourceDirectory}/../scala. However, other unrelated Maven plugins can't know about those new folders.
To fix this, the plugin provides the add-source goal, which adds src/main/scala and src/test/scala as source and test source directory to the Maven project, and, thus, makes them available for the other plugins relying on the source directories, like the Maven Source Plugin. You should therefore change your POM to:
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<!-- rest of configuration -->
</plugin>

Is it possible to use reflections-maven to scan for classes inside jars in web-Inf/lib?

I need to create a list of subclasses for a particular interface during maven build process and then use that at runtime to load those classes. I have added reflections-maven (from google code reflections) in my webapp pom but during maven build, its only including the classes from the web application and not the classes inside the packaged jars that are there in web-inf/lib folder of that application. Below is the straight forward configuration I have used. I looked at the plugin source code and it seems it scans the following: getProject().getBuild().getOutputDirectory().
Is there anyways I can configure the plugin to scan the dependent jar files of the project?
<plugin>
<groupId>org.reflections</groupId>
<artifactId>reflections-maven</artifactId>
<version>0.9.9-RC1</version>
<executions>
<execution>
<goals>
<goal>reflections</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
</plugin>
You can easily run Reflections with any configuration you like, using, for example, gmaven-plugin:
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.3</version>
<dependencies>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.9-RC1</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
new org.reflections.Reflections("f.q.n")
.save("${project.build.outputDirectory}/META-INF/reflections/${project.artifactId}-reflections.xml")
</source>
</configuration>
</execution>
</executions>
</plugin>
So all you need to do is to use the right configuration, maybe in your specific case:
def urls = org.reflections.util.ClasspathHelper.forWebInfLib()
new org.reflections.Reflections(urls)
.save("${project.build.outputDirectory}/META-INF/reflections/${project.artifactId}-reflections.xml")

GWT compilation is skipped in maven when module packaging is pom

I am trying to reuse a assembled gwt compilation in another war. For this i am try to change the current maven module's packaging from war to pom. I then plan to use maven-assembly-plugin to zip up gwt's module js output and use that later on in another war module.
I tried changing the packaging tag from <packaging>war</packaging> to <packaging>pom</packaging> in pom.xml from Validation sample . gwt-maven-plugin never enters into compilation. Instead, it skips compilation!!!!!
What is happening?
Is this expected?
Is there any workaround?
To join multiple gwt compiled modules into a single .war file, it is very easy with the maven-dependency-plugin
Package all your gwt examples as habitual (.war), and install them mvn install or mvn deploy if you have a private maven repo.
Create an empty maven module of type war, with no code but with the maven folder structure, you can put any additional stuff you need here like a global src/main/webapp/index.html.
Configure the new module to use the maven-dependency-plugin like shown below, and run mvn package:
<dependency>
<groupId>my.group</groupId>
<artifactId>example1</artifactId>
<version>...</version>
<type>war</type>
</dependency>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-gwt-examples</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>my.group</includeGroupIds>
<includes>**/example1/**</includes>
<outputDirectory>${project.build.directory}/${project.artifactId}-${project.version}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Finally and related with the gwt-maven-plugin, like with any other maven pluging, it would be enough to select an appropriate phase of the pom-packaging life cycle (package, install or deploy):
...
<packaging>pom</packaging>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
...
<configuration>
...
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
Unfortunately, gwt-maven-plugin specifically disallows compilation when packaging is pom, take a look to line #269 of CompileMojo.java
You can create the reusable modules (that you mention as samples in the comments) as separate GWT projects with no EntryPoint. Package them as jar and add the following as resources:
the client side source code
other resource items that will be necessary for the final compilation (images, xml files, etc.)
Something like this:
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
...
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/services/**</include>
<include>**/client/**</include>
<include>**/public/**</include>
<include>**/*.gwt.xml</include>
</includes>
</resource>
</resources>
</build>
That's it, you can reuse it in any other GWT project. When you will do so, you just have to add the dependency (to the reusable module) in the pom.xml and import in the *.gwt.xml.
As for Maven's behaviour, it seems correct. pom packaging is going through package, install and deploy phases and
By default, the compile goal is configured to be executed during the ''prepare-package'' phase to run as late as possible.
You could change the phase in the plugin's execution, but I think it's risky because you can't know when exactly during the package phase will your code get compiled.

Maven GWT Plugin copies multiple versions of the same snapshot jars

I have this issue where I build my project (mvn clean install), some of the transitive dependencies are snapshot versions and are downloaded and copied into the target webapp directory e.g XXXUtil-1.0-20110922.172721-52.jar. Then when I run mvn gwt:run, it finds uses XXXUtil-1.0-SNAPSHOT.jar and copies it to the target webapp directory. I can't figure out why this is happening. In doesn't matter whether I run as exploded or inplace.
<plugins>
<!-- GWT Maven Plugin -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>2.3.0-1</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>i18n</goal>
<goal>generateAsync</goal>
</goals>
</execution>
</executions>
<configuration>
<runTarget>Shell.html</runTarget>
<hostedWebapp>${webappDirectory}</hostedWebapp>
<i18nMessagesBundle>com.myapp.client.Messages</i18nMessagesBundle>
</configuration>
<dependencies>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>${gwt.version}</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
<version>${gwt.version}</version>
</dependency>
</dependencies>
</plugin>
<!-- Copy static web files before executing gwt:run -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>exploded</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- <outputFileNameMapping>#{artifactId}#-#{version}#.#{extension}#</outputFileNameMapping> -->
<webappDirectory>${webappDirectory}</webappDirectory>
</configuration>
</plugin>
</plugins>
None of the suggestions described here help:
http://www.tikalk.com/alm/forums/maven-war-plugin-picking-multiple-version-same-snapshot-jars.
If i build local snapshots of XXXUtil-1.0-SNAPSHOT.jar it works buts not when downloading snapshots from a nexus repository. Another way to look at it is like this Project A generates a WAR, and depends on B.jar, which depends on C.jar. When i build my war using mvn install, it generates the correct jars in WEB-INF/lib so we have C-1.0-20110922.172721-52.jar. Which is correct and it works if i deploy my war. If i run in hosted mode using eclipse, its fine. But when i run mvn:gwt-run, C-1.0-SNAPSHOT.jar is copied into WEB-INF/lib so i have 2 jars C-1.0-SNAPSHOT.jar and C-1.0-20110922.172721-52.jar.
The only thing I can suggest you is to try to debug maven-gwt-plugin.
Checkout it from git repository
https://github.com/gwt-maven-plugin/gwt-maven-plugin.git
I had exactly the same problem. After debugging, I removed the use of maven-war-plugin and added maven-resources-plugin (compile phase, copy-resources goal). I tried gwt:run and install after that, worked without any problems. This way, we avoid the dependencies getting copied twice.