How to migrate a Maven based web application POM to use Gradle? - maven

Yes, yes, I know! There are hundreds of Maven-to-Gradle related questions here on SO, but none of them, AFAICT addresses the inherent difficulties (of a constantly moving target [Gradle!]) of porting a very simple Maven POM to use Gradle for compilation.
In this particular case, we have an app the original developers are relying on using the GWT plugin for Eclipse, but that many other developers would like to use something more modern and less bloated, i.e. Gradle. All sources simply brush this off to, "you can simply convert pom.xml in Gradle", as is stated in the Gradle documentation, unfortunately this is simply not working for most practical purposes.
The project under consideration is circuitJS1 which was ported to use Maven here. The resulting pom.xml reads:
<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.lushprojects.circuitjs1</groupId>
<artifactId>circuitjs</artifactId>
<version>2.1.15</version>
<packaging>gwt-app</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt</artifactId>
<version>2.8.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- generating dependency report is slow; disable it -->
<dependency.locations.enabled>false</dependency.locations.enabled>
</properties>
<dependencies>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <!-- artifact with sources is easier to handle during development -->
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- declaring only in order to skip during site deployment -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.7</version>
<executions>
<execution>
<id>deploy</id>
<phase>deploy</phase>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
<configuration>
<skip>true</skip>
<siteDirectory>site</siteDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.9</version>
<configuration><!-- we don't need those reports; disabling speeds up
build -->
<dependencyDetailsEnabled>false</dependencyDetailsEnabled>
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
</configuration>
</plugin>
<!-- gwt compiler -->
<plugin>
<groupId>net.ltgt.gwt.maven</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>1.0-rc-9</version>
<extensions>true</extensions>
<configuration>
<moduleName>com.lushprojects.circuitjs1.circuitjs1</moduleName>
<!-- this is the best setting for a laptop with 2 cores and HT -->
<localWorkers>0.5C</localWorkers>
<warName>circuitjs</warName>
<optimize>9</optimize>
<compilerArgs>
<compilerArg>-style</compilerArg>
<compilerArg>PRETTY</compilerArg>
</compilerArgs>
<codeServerPort>8888</codeServerPort>
</configuration>
</plugin>
<!-- copy a few things around before packaging the website -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>install</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/site</outputDirectory>
<resources>
<resource>
<directory>war</directory>
</resource>
<resource>
<directory>${project.build.directory}/${project.name}-${project.version}/circuitjs1</directory>
<targetPath>circuitjs1</targetPath>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Cloning that repository and simply doing: mvn install will give you a working (circuit simulation) website, that you can open with your browser, under the file location:
file:///C:/path/to/circuitjs1/target/site/circuitjs.html
However, using gradle init --type pom to convert to Gradle, yield a build.gradle like this:
/* This file was generated by the Gradle 'init' task. */
plugins {
id 'java'
id 'maven-publish'
}
repositories {
mavenLocal()
maven {
url = 'http://repo.maven.apache.org/maven2'
}
}
dependencies {
compile 'com.google.gwt:gwt-user:2.8.2'
compile 'com.google.gwt:gwt-dev:2.8.2'
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from(sourceSets.main.allJava)
}
group = 'com.lushprojects.circuitjs1'
version = '2.1.15'
sourceCompatibility = '1.8'
publishing {
publications {
maven(MavenPublication) {
from(components.java)
artifact(sourcesJar)
}
}
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
Unfortunately, using that with gradle build, doesn't seem to yield anything that can be ran in the browser. It does generate ./build/ directory but which contents are not of any use at all.
build
├── classes
│   └── java
│   └── main
│   └── com
├── generated
│   └── sources
│   └── annotationProcessor
│   └── java
├── libs
│   └── circuitjs-2.1.15.jar
└── tmp
├── compileJava
└── jar
└── MANIFEST.MF
I have also tried to beat this file into shape by adding things like:
//id 'com.gradle.build-scan' version '2.2.1'
id 'java'
id 'maven'
//id 'gwt-maven'
id 'maven-publish'
id 'war'
//id 'org.metahelicase.site-builder' version '1.1'
//id 'org.jbake.site' version '5.0.0'
//id 'gradle.site' version '0.6'
//id 'com.stehno.gradle.webpreview' version '0.3.0'
But to no avail...
How can I make this build.gradle script work correctly to produce the web files necessary to run the application locally in my browser?
Are there any up-to-date tools available that can do this for you?

The problem was that because the Maven build file (pom.xml) had so many plugins in it, that I was thinking they were needed also in Gradle. That is a big mistake because Gradle's build.gradle file only need minimal plugin info. The rest is downloaded and used automagically.
In my case it only needed the Gradle version of GWT, the gwt-gradle-plugin plugin.
The corrected and hugely simplified build.gradle becomes:
// This must be before plugins!
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.wisepersist:gwt-gradle-plugin:1.0.8"
}
}
plugins {
id 'java'
}
repositories {
mavenLocal()
maven {
url = 'https://plugins.gradle.org/m2/'
url = 'http://repo.maven.apache.org/maven2'
}
}
dependencies {
implementation 'com.google.gwt:gwt-user:2.8.2'
implementation 'com.google.gwt:gwt-dev:2.8.2'
}
apply plugin: 'gwt-compiler'
...
Now you can just run:
gradle compileGwt --console verbose --info
and all files will be built in the ./build directory.

Related

maven to gradle conversion

I am trying to convert a maven project to gradle and is looking for a corresponding tags in gradle.
Here is the POM Snippet:
<configuration>
<configurationLocations>
<configurationDirectory>
<path>src/main/environment</path>
</configurationDirectory>
<configurationArtifact>
<groupId>com.test.aaa</groupId>
<artifactId>test1.jar</artifactId>
<version>1.0.0</version>
</configurationArtifact>
<configurationArtifact>
<groupId>com.test.bbb</groupId>
<artifactId>lib1.jar</artifactId>
<version>1.0.0</version>
</configurationArtifact>
</configurationLocations>
<eclipseBuildEnvironment>local</eclipseBuildEnvironment>
<testEnvironment>local</testEnvironment>
</configuration>
<plugin>
<groupId>com.test.myapp</groupId>
<artifactId>my-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>generate-environments</id>
<goals>
<goal>generate-environments</goal>
</goals>
</execution>
<execution>
<id>package-environments</id>
<goals>
<goal>package-environments</goal>
</goals>
</execution>
</executions>
<configuration>
<configurationLocations>
<configurationDirectory>
<path>src/main/environment</path>
</configurationDirectory>
<configurationArtifact>
<groupId>com.test.aaa</groupId>
<artifactId>test1.jar</artifactId>
<version>1.0.0</version>
</configurationArtifact>
<configurationArtifact>
<groupId>com.test.bbb</groupId>
<artifactId>lib1.jar</artifactId>
<version>1.0.0</version>
</configurationArtifact>
</configurationLocations>
<eclipseBuildEnvironment>local</eclipseBuildEnvironment>
<testEnvironment>local</testEnvironment>
</configuration>
</plugin>
The corresponding gradle configuration i added is as below
apply plugin: myapp-generator
buildscript {
repositories {}
dependencies {
classpath 'com.test.myapp:my-plugin:1.0.0'
}
So what I am looking up ithe corresponding script in gradle for
the node <configuration> in the POM
Maven plugins cannot be used by Gradle. Given that the POM extract is mostly about a custom plugin and its configuration, you will have to develop a matching Gradle plugin to replicate the features of the Maven plugin.

Maven Assembly Plugin jar-with-dependencies -> No Dependencies in Jar

The following reference mentions the descriptor Reference jar-with-dependencies. Afaik it is a predefined assembly, which includes all jar dependencies into a single big self-contained jar file. This is great if you have multiple dependencies and need to copy your project to another machine because you don't need to update/delete obsolete libraries separately.
https://newfivefour.com/category_maven-assembly.html
I added the maven-assembly-plugin to my pom, and the MyTool.jar-with-dependency.jar is created. I expected that the jar contains all external dependencies, but it is the same as the normal MyTool.jar and does not contain any dependencies like apache.commons or apache.logging.
The important detail is that the dependencies scope is set to provided. Without this it works as expected. But I use the scope later on with the maven-dependency-plugin to copy all dependencies in the provided scope to a specific directory.
[...]
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
<scope>privided</scope>
</dependency>
</dependencies>
<build>
<!--pluginManagement-->
<plugins>
<plugin> <!-- This is the plugin I added. -->
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
[...]
I use Apache Maven 2.2.1 (rdebian-14).
How can I include the dependencies from the provided scope? Or is there an other solution?

maven-war-plugin ignores <archiveClasses>

The <archiveClasses> option has no effect.
Running mvn clean compile war:exploded produces a war directory with .class files in the classes directory, and they are not archived into a jar in the lib directory neither. war:war produces same result.
Plugin configuration:
...
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<archiveClasses>true</archiveClasses>
</configuration>
</plugin>
...
Workarounds?
Maven version 3.3.3, maven-war-plugin version 2.6.
JIRA ticket – https://issues.apache.org/jira/browse/MWAR-355
This is the project in question: https://bitbucket.org/dmos62/raudondvaris
The first thing is you should move the plain configuration into a pluginManagement block like this:
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<archiveClasses>true</archiveClasses>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
If you do the above the classes will be created within the war archive by using: mvn clean compile war:war
~/ws-git/so-questions/so-5 (master)$ unzip -t target/web-1.0.0-SNAPSHOT.war
Archive: target/web-1.0.0-SNAPSHOT.war
testing: META-INF/ OK
testing: META-INF/MANIFEST.MF OK
testing: WEB-INF/ OK
testing: WEB-INF/classes/ OK
testing: WEB-INF/lib/ OK
testing: WEB-INF/lib/commons-fileupload-1.1.1.jar OK
testing: WEB-INF/lib/commons-io-1.1.jar OK
testing: WEB-INF/lib/web-1.0.0-SNAPSHOT.jar OK
testing: WEB-INF/web.xml OK
testing: META-INF/maven/com.soebes.examples.so/web/pom.xml OK
testing: META-INF/maven/com.soebes.examples.so/web/pom.properties OK
testing: META-INF/INDEX.LIST OK
No errors detected in compressed data of target/web-1.0.0-SNAPSHOT.war.
This will also working for your call mvn clean compile war:exploded.
└── web-1.0.0-SNAPSHOT
├── META-INF
└── WEB-INF
├── classes
├── lib
│   ├── commons-fileupload-1.1.1.jar
│   ├── commons-io-1.1.jar
│   └── web-1.0.0-SNAPSHOT.jar
└── web.xml
The reason for this behaviour is simply cause by using a goal like war:war, or war:exploded there will be no life cycle started which means the configuration in the pom is not taken into account. If you like having a configuration for your command line calls you can do this by using a special configuration for command line calls like this (The id default-cli is the important part):
<project>
<build>
<plugins>
<plugin>
<groupId...>
<artifactId...>
<executions>
<execution>
<id>default-cli</id>
<configuration>
.....
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
which means having a special configuration for command line calls. Starting with Maven 3.3.1 it is possible having more than one configuration for command line calls by using it like:
<project...>
<build>
<plugins>
<plugin>
<groupId>...</groupId>
<artifactId>...</artifactId>
<executions>
<execution>
<id>first-cli</id>
<configuration>
....
</configuration>
</execution>
<execution>
<id>second-cli</id>
<configuration>
....
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
This can be used by maven via the following:
mvn plugin:goal#second-cli
mvn plugin:goal#first-cli
See also the release notes for Maven 3.3.1.

How to embed a library JAR in an OSGi bundle using Tycho

I am using Maven with the Tycho plugin to build my OSGi bundles.
In one of my bundles, I use the facebook API through the restfb-1.7.0.jar library.
For now, it is directly placed on the classpath (in Eclipse) and embedded in the effective OSGi bundle jar file with following build.properties configuration:
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
lib/restfb-1.7.0.jar
Now I would like to have this restfb lib downloaded from Maven (e.g. as dependency) and embedded into my OSGi bundle jar. Is it possible with Maven/Tycho? How?
You need the following configuration to embed a JAR into an OSGi plugin with Tycho:
In the pom.xml, configure the copy goal of the maven-dependency-plugin
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-libraries</id>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<item>
<groupId>com.restfb</groupId>
<artifactId>restfb</artifactId>
<version>1.7.0</version>
</item>
</artifactItems>
<outputDirectory>lib</outputDirectory>
<stripVersion>true</stripVersion>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Edit the MANIFEST.MF to have the library added to the OSGi bundle classpath
Bundle-ClassPath: ., lib/restfb.jar
Edit the build.properties to have the library included in the JAR packaged by Tycho
bin.includes = META-INF/,\
.,\
lib/restfb.jar
I think what you would want is to have a dependency in the POM with the compile time scope like the example below: use the correct artifact and version info to get the item you want. you should investigate the dependencies section of the maven ref for poms
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>compile</scope>
</dependency>

Ivy project can't get the latest Maven snapshot (with assembly plugin)

When a Ivy project depends a maven project (using assembly plugin), there will be problems. For example:
Maven project:
Suppose the maven project will deploy 2 snapshot packages: for exmaple, one is my-app-1.0.0-20130504.000602-1.jar and the other is my-app-1.0.0-20130504.001348-1-myzip.zip. The pom.xml is shown below.
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<... ...>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<packaging>zip</packaging>
<file>my-app-1.01-myzip.zip</file>
</configuration>
</plugin>
</plugins>
</build>
</project>
The Ivy project
The Ivy project depends on the 2 artifacts published by above maven project. The ivy.xml is shown below:
<configurations>
<conf name="get-maven" />
</configurations>
<dependencies>
<dependency org="com.mycompany.app" name="my-app" rev="1.0.0-SNAPSHOT" changing="true" conf="get-maven->default">
<artifact name="my-app" ext="jar" type="jar"></artifact>
<artifact name="my-app" ext="zip" type="zip" m:classifier="myzip"></artifact>
</dependency>
</dependencies>
Issues:
Each time when the maven project deploy new snapshot to the artifactory server, the ivy project can retrieve the latest my-app-xxx.jar from the artifacory server but it can't retrieve the latest my-app-xxx-myzip.zip (ivy can't get to know that the zip is updated and just retrieve the zip from local cache).
What I have to do is to delete the local cache, and run the ivy project again.
I did some investigation, and found the ivy task "convertpom" didn't convert the app-xxx-myzip.zip from pom to ivy, and only 1 artifact (the my-app-xxx.jar) can be found in the converted ivy.xml. Not sure if this is the root cause.
Can anyone help on this? How can I get the lastet snapshots for both artifacts in ivy project?
Regards,
Alben
So to clarify, you're attempting to configure ivy to pull down a snapshot dependency published by a Maven project?
In that case, the following answer will help to explain:
What's wrong with this Ivy changingPattern / SNAPSHOT configuration?

Resources