Gradle mapping for `compile project(:dependency)` maven dependencies? - maven

I'm having multi-project Gradle config:
-- root (folder 'gradle_test')
L--wrapper (depends on some 3rd-party maven libs)
L--module1 (depends on wrapper)
L--app
I need module1 jar (and wrapper jar as transitive dependency) to be published in local maven repo.
root build.gradle:
// for maven
ext {
groupId = 'mygroup'
version = '3.0'
}
wrapper build.gradle:
apply plugin: 'maven'
...
// maven pom
install {
repositories.mavenInstaller {
pom.groupId = rootProject.ext.groupId
pom.artifactId = 'wrapper'
pom.version = rootProject.ext.version
}
}
module1 build.gradle:
dependencies {
compile project(':wrapper')
...
}
// maven pom
install {
repositories.mavenInstaller {
pom.groupId = rootProject.ext.groupId
pom.artifactId = 'module1'
pom.version = rootProject.ext.version
}
}
Howeven when installing module1 into local maven cache i can see dependency to 'wrapper' module is generated incorrectly (version is not specified). module1 pom.xml in repo:
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>mygroup</groupId>
<artifactId>module1</artifactId>
<version>3.0</version>
<dependencies>
<dependency>
<groupId>gradle_test</groupId> // error 1: gradle project name (instead of overriden mvn groupId)
<artifactId>wrapper</artifactId>
<version>unspecified</version> // error 2: not set at all
<scope>compile</scope>
</dependency>
</dependencies>
</project>
In other words Gradle does not use maven module groupId/artifactId/version for maven dependencies mapped from compile project(:wrapper) declaration.
How can i do/fix it?

I had to write for wrapper and module1:
// mvn
group = rootProject.ext.groupId
version = rootProject.ext.version
and you can remove:
install {
repositories.mavenInstaller {
...
}
}
since Gradle will use project.group as mvn module groupId and project.version as mvn module version:
https://docs.gradle.org/current/userguide/maven_plugin.html#sec:maven_pom_generation

Related

Dependency management in gradle with mavenBom

I have a java library that is imported in gradle like this
sourceControl {
gitRepository("git#github.com/mytest/commonslib.git") {
producesModule("com.lib:commonslib")
}
}
the lib contains a pom.xml with all dependencies.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lib</groupId>
<artifactId>commonslib</artifactId>
<version>0.1.10</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
now i would like to import this pom to handle the dependend libraries.
But it does not work when i used it like this.
dependencyManagement {
imports {
mavenBom 'com.lib:commonslib:0.1.10'
}
}
I got the error: Could not resolve commonslib.pom (project :commonslib). Any hint whats wrong? Is it even possible to use producesModule and mavenBom in compbination?

How to allow spring boot applications to use custom jar having spring cloud dependency

I have many spring boot microservices and I have developed a new project that has Spring-Vault as a dependency. This new project (say vault-client-spring) is developed in order to have common configuration for setting up of Vault and use it in all of the microservices and I have published the jar in private maven hosted repository in my organization.
My problem is when I add this jar as dependency in any microservices, the application is not starting throwing the following error. The Spring-Cloud-Vault dependencies are not imported to my consuming projects. I've also added the necessary properties requrired starting with prefix spring.cloud.vault in bootstrap.yml file.
Here's my build.gradle file for vault-client-spring.
group = 'com.mygroup'
version = '0.0.1'
buildscript {
ext {
springBootVersion = '2.4.4'
springDepsMgmtVersion = '1.0.11.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "io.spring.gradle:dependency-management-plugin:${springDepsMgmtVersion}"
}
}
apply plugin: 'java-library'
apply plugin: 'idea'
apply plugin: 'maven-publish'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
bootJar {
enabled = false
}
jar {
archivesBaseName = 'vault-client-spring'
enabled = true
}
ext {
springCloudVersion = '2020.0.2'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-vault-config'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier('sources')
from sourceSets.main.allSource
}
publishing {
publications {
mavenJava(MavenPublication) {
artifact jar
artifact sourcesJar
}
}
repositories {
maven {
credentials {
username 'PRIVATE-USER'
password 'PRIVATE-PASSWORD'
}
url 'PRIVATE MAVEN URL'
}
}
}
Here's the build.gradle file for a microservice using this jar.
plugins {
id 'org.springframework.boot' version '2.4.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.mygroup'
version = ''
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
maven { url 'PRIVATE MAVEN URL' }
}
bootJar {
setArchivesBaseName("microservice-one")
version("")
}
dependencies {
implementation 'com.mygroup:vault-client-spring:0.0.1' // here's the dependency
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
compileJava {
compileJava.inputs.files(processResources)
}
test {
useJUnitPlatform()
}
The generated POM file.
<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.mygroup</groupId>
<artifactId>vault-client-spring</artifactId>
<version>0.0.1</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.2</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.4</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
</project>
The error I'm facing is as follows..
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/vault/config/AbstractVaultConfiguration.class] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:180) ~[spring-core-5.3.4.jar:5.3.4]
at org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:55) ~[spring-core-5.3.4.jar:5.3.4]
at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:49) ~[spring-core-5.3.4.jar:5.3.4]
at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103) ~[spring-core-5.3.4.jar:5.3.4]
at org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory.createMetadataReader(ConcurrentReferenceCachingMetadataReaderFactory.java:86) ~[spring-boot-2.4.3.jar:2.4.3]
at org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory.getMetadataReader(ConcurrentReferenceCachingMetadataReaderFactory.java:73) ~[spring-boot-2.4.3.jar:2.4.3]
at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81) ~[spring-core-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.asSourceClass(ConfigurationClassParser.java:696) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser$SourceClass.getSuperClass(ConfigurationClassParser.java:1010) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:341) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:199) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:304) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:207) ~[spring-context-5.3.4.jar:5.3.4]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:175) ~[spring-context-5.3.4.jar:5.3.4]
... 14 common frames omitted
How to solve this error? I'm working for the first time in Spring Cloud native projects
I found it myself. Since I'm developing as "library", I have to allow Spring-Vault dependencies to get included in consuming applications. As per Gradle's java-library plugin, I used api dependency. The consuming applications can able to access and bootstrap them.

How to convert Maven profiles to gradle

I am in the process of converting test project from maven to gradle. The last part is to convert many maven profiles like this to gradle.
<profile>
<id>fasttrack_ui_saucelabs</id>
<properties>
<selenium.remote>true</selenium.remote>
<selenium.saucelabs>true</selenium.saucelabs>
<selenium.browser>chrome</selenium.browser>
<selenium.platform>Windows 10</selenium.platform>
<parallel.threads>4</parallel.threads>
<junit.tag>fasttrack</junit.tag>
<test.retry>2</test.retry>
</properties>
</profile>
<profile>
<id>fasttrack_login_hub</id>
<properties>
<selenium.remote>true</selenium.remote>
<selenium.saucelabs>false</selenium.saucelabs>
<selenium.browser>firefox</selenium.browser>
<parallel.threads>10</parallel.threads>
<selenium.host>selenium-test.rec.com</selenium.host>
<selenium.port>4444</selenium.port>
<junit.tag>fasttrack-login</junit.tag>
<test.retry>2</test.retry>
</properties>
</profile>
How can I convert these profiles/properties to gradle ?
You could create a plugin for each profile
Plugin<Project> fasttrack_ui_saucelabs = (Project project) -> { ... }
Plugin<Project> fasttrack_login_hub = (Project project) -> { ... }
if (condition1) apply plugin: fasttrack_ui_saucelabs
if (condition2) apply plugin: fasttrack_login_hub

How to maven-publish a Gradle project JAR with provided scope

Given a Gradle web project that is to be published as a JAR (so that it can be a dependency of another Gradle web project, which has a different release cycle).
The maven-publish plugin is used:
apply plugin: 'war'
apply plugin: 'maven'
apply plugin: 'maven-publish'
The web project has a providedCompile dependency:
providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
A jar is published using mavenJava:
publishing {
publications {
// mavenJava publishes a jar file
mavenJava(MavenPublication) {
from components.java
}
}
repositories {
mavenLocal()
}
}
The problem is that javax.servlet-api has a runtime scope in the resulting Maven POM:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>runtime</scope>
</dependency>
Runtime scope makes no sense for the servlet-api, it is even harmful. How can the scope be set to provided in the pom.xml?
With the help of pom.withXml (see this Gradle sample) it's possible to transform Gradle's providedCompile into provided scope for Maven's POM:
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
// providedCompile -> provided scope
pom.withXml {
asNode().dependencies.'*'.findAll() {
it.scope.text() == 'runtime' && project.configurations.providedCompile.allDependencies.find { dep ->
dep.name == it.artifactId.text()
}
}.each() {
it.scope*.value = 'provided'
}
}
}
}
repositories {
mavenLocal()
}
}
What the pom.withXml section does is going through all dependencies of type providedCompile within the Gradle project configuration and changing the scope to be written into the Maven pom.xml from runtime to provided.
The generated pom.xml now has the provided scope set for the javax.servlet-api:
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
[...]
<dependencies>
[...]
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

gradle pull test jar from other project

I have a multi-project setup in maven and trying to switch to gradle. I am trying to figure out how to have one project's test dependencies include another project's test jar. Right now i have the following in ProjectA:
packageTests = task packageTests(type: Jar) {
classifier = 'tests'
from sourceSets.test.output
}
tasks.getByPath(":ProjectA:jar").dependsOn(packageTests)
And in ProjectB i have:
testCompile project(path: ':ProjectA', classifier: 'tests')
I see that my tests are failing to compile. Looks like they are missing classes defined in the test jar. When I check the build dir, i see that the ProjectA-0.1.56-SNAPSHOT-tests.jar is present.
In maven I had the following for ProjectA:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
And this for ProjectB:
<!-- Testing -->
<dependency>
<groupId>com.example</groupId>
<artifactId>ProjectA</artifactId>
<version>0.1.56-SNAPSHOT</version>
<type>test-jar</type>
</dependency>
How can I get this to work just like maven?
What you will end up with is something like
tasks.create( [
name: 'testJar',
type: Jar,
group: 'build',
description: 'Assembles a jar archive containing the test classes.',
dependsOn: tasks.testClasses
] ) {
manifest = tasks.jar.manifest
classifier = 'tests'
from sourceSets.test.output
}
// for test dependencies between modules
// usage: testCompile project(path: ':module', configuration: 'testFixtures')
configurations { testFixtures { extendsFrom testRuntime } }
artifacts {
archives testJar
testFixtures testJar
}
tasks.uploadArchives.dependsOn testJar

Resources