Using a spring-beans .xml file (in 'resources' folder) from a shared module with gradle multi module (subproject) - gradle

(To assist with this question, I have github links at the bottom of this question)
I have (and am "starting with" the below gradle multiple module/project)
settings.gradle
rootProject.name = 'com.mycompany.myteam.myapp-rootProjectName'
include ':source:java:myapproot:myapp-toplayer-console-di-xml-one'
include ':source:java:myapproot:myapp-business-logic'
(maybe even a few that are purely .java/.class "code logic" libraries)
In the subproject:
':source:java:myapproot:myapp-toplayer-console-di-xml-one'
I have the typical Spring-Boot startup and a spring-bean setup using applicationContext.xml.
./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/java/demo/SpringBootApplicationContextXmlConsoleApplication.java
--
./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/resources/applicationContext.xml
./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/resources/orch.one.di.xml
./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/resources/clientporxy.one.di.xml
The contents of "applicationContext.xml" are below.
Note, it has a "pointer" 'import' entries to some other .xml files.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="orch.one.di.xml"/>
<import resource="clientproxy.one.di.xml"/>
</beans>
Everything above works just fine. I've been doing the above approach for years and for many different projects.
============================================================
What I am trying to do is put the 2 "extra" *.di.xml files in another gradle module.
settings.gradle
rootProject.name = 'com.mycompany.myteam.myapp-rootProjectName'
include ':source:java:myapproot:myapp-toplayer-console-di-xml-one'
include ':source:java:myapproot:myapp-business-logic'
(new one below)
include ':source:java:myapproot:myapp-shared-resxex'
And I want to move the *.di.xml files to this different/new gradle-subproject/module. (':source:java:myapproot:myapp-shared-resxex' to be clear)
So now I have moved the two *.di.xml files.
File location is here: (under "resources")
./source/java/myapproot/myapp-shared-resxex/src/main/resources/orch.one.di.xml
./source/java/myapproot/myapp-shared-resxex/src/main/resources/clientproxy.one.di.xml
So now when I start the "main" application, I get errors like below (and I kinda expected these errors)
Caused by: java.io.FileNotFoundException:
./source/java/myapproot/myapp-toplayer-console-di-xml-one/build/resources/main/clientproxy.one.di.xml
(No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:86)
at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:184)
at org.springframework.core.io.UrlResource.getInputStream(UrlResource.java:187)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:333)
I have "tried" (and worked through) things like these:
https://docs.gradle.org/current/userguide/cross_project_publications.html
Gradle: common resource dependency for multiple java projects
How module can use resources of another module using gradle multi-module
https://discuss.gradle.org/t/how-to-bundle-static-resources-from-other-subproject-into-java-application/42076
Like many days and many hours been trying to figure out the magic-sauce.
So, my "in general" question is
How can I get a spring bean .xml file to work from a different gradle-subproject/module?
I will show my latest attempt:
./source/java/myapproot/myapp-shared-resxex/build.gradle
configurations {
myCoolConfigurationName
}
task tryToPackageStuffForOtherSubprojectsTask(type: Jar) {
archiveClassifier
from sourceSets.main.resources
}
artifacts {
myCoolConfigurationName tryToPackageStuffForOtherSubprojectsTask
}
dependencies {
implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-domain')
implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-interfaces')
implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-concrete')
}
and then:
./source/java/myapproot/myapp-toplayer-console-di-xml-one/build.gradle
plugins {
id "application"
}
apply plugin : "java"
ext {
javaMainClass = "demo.SpringBootApplicationContextXmlConsoleApplication"
}
application {
mainClassName = javaMainClass
}
task myCustomPrintClasspathTask {
doLast {
configurations.runtimeClasspath.each { println 'helloThere->' + it }
}
}
dependencies {
//the below line, if left not-commented-out, will produce a warning : "Execution optimizations have been disabled for task"
implementation project(path: ':source:java:myapproot:myapp-shared-resxex', configuration: 'myCoolConfigurationName')
implementation(group: 'org.springframework.boot', name: 'spring-boot-starter', version: springBootVersion) {
exclude module: "logback-classic"
}
implementation(group: 'org.springframework.boot', name: 'spring-boot-autoconfigure', version: springBootVersion) {
exclude module: "logback-classic"
}
implementation project(":source:java:myapproot:myapp-shared-resxex")
implementation project(':source:java:myapproot:myapp-business-logic')
implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-domain')
implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-interfaces')
implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-concrete')
implementation group: 'org.apache.commons', name: 'commons-lang3', version: commonsLangVersion
implementation group: 'javax.inject', name: 'javax.inject', version: "${javaxInjectVersion}"
implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion
implementation group: 'org.slf4j', name: 'slf4j-simple', version: slf4jSimpleVersion
testImplementation group: 'junit', name: 'junit', version: junitVersion
testImplementation group: 'org.mockito', name: 'mockito-core', version: mockitoVersion
}
When I run (a clean build and then) the custom task:
$ ./gradlew clean build
$ ./gradlew myCustomPrintClasspathTask
I get the below output:
> Task :source:java:myapproot:myapp-toplayer-console-di-xml-one:myCustomPrintClasspathTask
helloThere->./source/java/myapproot/myapp-shared-resxex/build/libs/myapp-shared-resxex.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter/2.7.5/c28e1546461803490588085345ba5d2897d232bc/spring-boot-starter-2.7.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/2.7.5/96646e63a2296d0a3209383e81cdb8c87ab2f913/spring-boot-autoconfigure-2.7.5.jar
helloThere->./source/java/myapproot/myapp-business-logic/build/libs/myapp-business-logic.jar
helloThere->./source/java/myapproot/clientproxies/myclientproxy-root/myclientproxy-concrete/build/libs/myclientproxy-concrete.jar
helloThere->./source/java/myapproot/clientproxies/myclientproxy-root/myclientproxy-interfaces/build/libs/myclientproxy-interfaces.jar
helloThere->./source/java/myapproot/clientproxies/myclientproxy-root/myclientproxy-domain/build/libs/myclientproxy-domain.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.12.0/c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e/commons-lang3-3.12.0.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-simple/1.7.36/a41f9cfe6faafb2eb83a1c7dd2d0dfd844e2a936/slf4j-simple-1.7.36.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-logging/2.7.5/61f4c53e35baa31a269bbeb7bb9d5e781448feef/spring-boot-starter-logging-2.7.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-to-slf4j/2.17.2/17dd0fae2747d9a28c67bc9534108823d2376b46/log4j-to-slf4j-2.17.2.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j/1.7.36/ed46d81cef9c412a88caef405b58f93a678ff2ca/jul-to-slf4j-1.7.36.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.36/6c62681a2f655b49963a5983b8b0950a6120ae14/slf4j-api-1.7.36.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/2.7.5/fd04e228e6e21b7ad13c10ae29afd31868d842e5/spring-boot-2.7.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/jakarta.annotation/jakarta.annotation-api/1.3.5/59eb84ee0d616332ff44aba065f3888cf002cd2d/jakarta.annotation-api-1.3.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/5.3.23/530b36b2ce2c9e471c6a260c3f181bcd20325a58/spring-context-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/5.3.23/30d0034ba29178e98781d85f51a7eb709a628e9b/spring-aop-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/5.3.23/3bdefbf6042ed742cbe16f27d2d14cca9096a606/spring-beans-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/5.3.23/3a676bf4b9bc42bd37ab5ad264acb6ceb63397a2/spring-expression-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/5.3.23/91407dc1106ea423c44150f3da1a0b4f8e25e5ca/spring-core-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.30/8fde7fe2586328ac3c68db92045e1c8759125000/snakeyaml-1.30.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jcl/5.3.23/3c7eb5fcca67b611065f73ff4325e398f8b051a3/spring-jcl-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.17.2/f42d6afa111b4dec5d2aea0fe2197240749a4ea6/log4j-api-2.17.2.jar
If I "peek" into the jar:
./source/java/myapproot/myapp-shared-resxex/build/libs/myapp-shared-resxex.jar
I do see the two *.di.xml files.
and then if I run:
$ ./gradlew run
I continue to get:
Caused by: java.io.FileNotFoundException: ./source/java/myapproot/myapp-toplayer-console-di-xml-one/build/resources/main/clientproxy.one.di.xml (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:86)
at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:184)
at org.springframework.core.io.UrlResource.getInputStream(UrlResource.java:187)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:333)
... 32 more
My ./gradle/wrapper/gradle-wrapper.properties file (for completeness and to know my gradle-version)
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
.....
Github repo with the code.
https://github.com/granadacoder/gradle-multi-with-spring-bean-proof
"main" branch...has a working spring-bean di.xml setup.
"main" branch relates to my comment above "everything above works just fine".
I have a second branch (and a PR so you can see the "diff").
branch:
https://github.com/granadacoder/gradle-multi-with-spring-bean-proof/tree/feature/move-spring-bean-xml-files-try-1
and PR (from the feature branch to the main branch to show the "diff")
https://github.com/granadacoder/gradle-multi-with-spring-bean-proof/pull/2/files

Ok.
I found an "old" documentation page.
https://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch04s07.html
I'll post the quote from the above URL, in case it "disappears".. especially since it is "old" documentation.
//START ARTICLE QUOTE
4.7.2.2 The classpath*: prefix When constructing an XML-based application context, a location string may use the special classpath*:
prefix:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); This
special prefix specifies that all classpath resources that match the
given name must be obtained (internally, this essentially happens via
a ClassLoader.getResources(...) call), and then merged to form the
final application context definition.
[Note] Classpath*: portability The wildcard classpath relies on the
getResources() method of the underlying classloader. As most
application servers nowadays supply their own classloader
implementation, the behavior might differ especially when dealing with
jar files. A simple test to check if classpath* works is to use the
classloader to load a file from within a jar on the classpath:
getClass().getClassLoader().getResources("").
Try this test with files that have the same name but are placed inside
two different locations. In case an inappropriate result is returned,
check the application server documentation for settings that might
affect the classloader behavior.
The "classpath*:" prefix can also be combined with a PathMatcher
pattern in the rest of the location path, for example
"classpath*:META-INF/*-beans.xml". In this case, the resolution
strategy is fairly simple: a ClassLoader.getResources() call is used
on the last non-wildcard path segment to get all the matching
resources in the class loader hierarchy, and then off each resource
the same PathMatcher resoltion strategy described above is used for
the wildcard subpath.
//END ARTICLE QUOTE
So I had forgotten about the "string-magic" of using classpath (before file-names, etc).
I was able to update my applicationContext.xml file to the below.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- below are "imports" to OTHER (spring-bean) di.xml files -->
<import resource="classpath:clientproxy.one.di.xml"/>
<import resource="classpath*:orchestrators.one.di.xml"/>
</beans>
You can read about the difference between using the "*" (after classpath) in the below SOF question/answers.
Spring classpath prefix difference
For clarity....all I did was "move" the two di.xml files to the "other" gradle/module-subproject. I did not use any of the "myCoolConfigurationName" stuff I showed in my original question ... as one of my "tries".
"so simple"........I was happy and in disbelief with my days of struggle with this.....

Related

gradle default configuration, what is it and how can I define it

I am having a weird error, for which I found a lot of hits in google but most about android studio or library imports and I don't use android studio nor am I trying to build any app/library so I'll try and ask here and see if I understand the background of it.
I have a project in which I have this plugin:
https://gitlab.com/zkovari/gradle-mermaid-plugin/-/blob/master/examples/single-project/build.gradle
This is a plugin that displays my dependencies as a mermaid graph.
Everything was fine until today I updated some dependencies and created a sub-project to do something else.
I'm trying to generate the graph again and somehow it's failing with this error message.
> Task :generateMermaidDependenciesDiagram FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':generateMermaidDependenciesDiagram'.
> Configuration with name 'default' not found.
I can see in the source code of the plugin that it automatically sets some configurations
#Override
public void apply(Project project) {
GenerateMermaidDependenciesDiagram generateDependencyDiagramsTask = project.getTasks()
.create(GENERATE_MERMAID_DEPENDENCIES_DIAGRAM_TASK_NAME, GenerateMermaidDependenciesDiagram.class);
generateDependencyDiagramsTask.setConfiguration("default");
generateDependencyDiagramsTask
.setOutput(project.getBuildDir().toPath().resolve("mermaid/dependencies.mmd").toFile());
generateDependencyDiagramsTask.setDiagramGenerator(new MermaidDiagramGenerator());
}
So my question is a bit high level but what is the "default" configuration?
And in my project I don't really have any configuration block I simply have my dependencies:
dependencies {
implementation group: 'com.amazonaws', name: 'aws-java-sdk-s3', version: awsVersion, transitive: false
...
testCompile group: 'org.slf4j', name: 'slf4j-simple', version: slf4jVersion
}
My structure looks like this:
project:
-- build.gradle
-- settings.gradle
-- sub-project:
--- build.gradle
I think this stopped working when I added my sub-project but the plugin is applied at the root project level.
I do have a dependency between my sub-project and my root project (one of my sub-project tasks depends on the root project)
I'm trying to understand that this might be very similar but I'm not fully getting it.
Gradle: What Is The Default Configuration and How Do I Change It
I do apply the java plugin on my main project so I expect the configuration.default should exist.
Even if I think I shouldn't do it I tried to re-define a default configuration extending implementation but that did not work
https://docs.gradle.org/current/userguide/declaring_dependencies.html
configurations {
default.extendsFrom implementation
}
build file 'C:\dev\repo\connector-custom\build.gradle': 66: unexpected token: default # line 66, column 5.
default.extendsFrom implementation
I did a quick test and removing my subproject did work so I know the problem is there but no idea why.
It means somehow I must pass to my subproject the default configuration of the main project?

Gradle bundle import to module

I am trying to import base to a project, but I get an error.
settings.gradle
enableFeaturePreview('VERSION_CATALOGS')
dependencyResolutionManagement {
versionCatalogs {
libs {
// https://mvnrepository.com/artifact/org.springframework/spring-core
version('spring', '5.3.14')
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter
version('spring-boot', '2.6.2')
alias('spring-core').to('org.springframework', 'spring-core').versionRef('spring')
alias('spring-context').to('org.springframework', 'spring-context').versionRef('spring')
alias('spring-boot').to('org.springframework.boot', 'spring-boot-starter').versionRef('spring-boot')
bundle('base', ['spring-core', 'spring-context'])
}
}
}
module's build.gradle
dependencies {
implementation(libs.base)
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
refreshing gradle will produce the following error
Could not get unknown property 'base' for extension 'libs' of type org.gradle.accessors.dm.LibrariesForLibs.
How to import libs.base correctly?
The correct way of referencing a bundle from a version catalog is
libs.bundles.base
Similarly for plugins use:
libs.plugins.yourplugin
It is also possible to reference versions, but this works slightly different. For example:
implementation group: "org.springframework.boot", name: "spring-boot-starter-web-services", version: libs.versions.spring.boot.get()
This assumes a version definition in your catalog which looks like:
version('spring-boot', '2.6.7')
Note that bundles, versions and plugins are all spelled as plural.
Also note that as of Gradle 7 version catalogs are no longer a feature preview. They are available by default. Also, the notation for libraries has changed slightly. You have to use library instead of alias.
See Gradle's support site for more information.

Gradle project test depends on another project's test

This might be a duplicate somewhere but some of the answers were for old Gradle which doesn't work anymore. Please let me know if this is doable. I have a new project named NewProject . NewProject depends on common project which lives in another directory.
- NewProject
- \build.gradle
- \settings.gradle
- \src
- \main\java
- \test\java
- common
- \build.gradle
- \settings.gradle
- \src
- \main\java
- \test\java
In NewProject, my file settings.gradle looks like this
rootProject.anme = 'NewProject'
include ':common'
project(':common').projectDir = file('../common')
and my file build.gradle has below lines in dependencies section
dependencies {
implementation project(':common')
...
testImplementation project(':common')
...
}
The problems I have are two:
Because I don't own the common project code, so I can not change the build file in common.
My test in NewProject couldn't locate the common's test classes. For example BaseTest etc.
I have tried approaches of:
Using testImplementation project(':common:test') doesn't work because ':common:test' is not recognized.
Using testImplementation project(':common').sourceSets.output.classes, but this approach is invalid for Gradle 5.0 and above.
Please let me know if there are other ways of doing this!
What is needed ?
The test-classes and test-resources dir of :common need to be in the classpath of the :newProject test
A way for your setup via Local dir access - in the :newProject build.gradle
testImplementation files('../common/build/classes/java/test')
testImplementation files('../common/build/resources/test')
note: this does not transitively add :common test dependencies to :newProject test, you have to do that manually.

Gradle Task messes with runtime Dependencies

Another strange behaviour of gradle...
So I've found this post:
Gradle exclude module for Copy task
Totally fine and works like a charm to exclude things from copying.
But here is where it gets interesting. This is how my Copy Task looks:
task copyDependencies(type: Copy) {
into "$buildDir/libs/dependencies"
from configurations.runtime {
exclude module: 'groovy'
exclude module: 'aws-java-sdk-s3'
exclude module: 'commons-io'
}
}
If I try to run the Application through Gradles 'application run' task. It fails with "Main Class xxx couldn't be found or loaded". Digging deeper into the problem I noticed that Groovy couldn't be resolved.
I don't even run this Task, or depend on it.
But if I comment out line 4 like this:
task copyDependencies(type: Copy) {
into "$buildDir/libs/dependencies"
from configurations.runtime {
//exclude module: 'groovy'
exclude module: 'aws-java-sdk-s3'
exclude module: 'commons-io'
}
}
The Application starts like normal, until it reaches a point where it needs Commons-IO. I still want to use this copyDependencies Task at other times, without changing the code there though.
Can somebody explain me this behaviour ?
I imagine manipulating the configuration.runtime anywhere in the gradle file, changes it for every other task ?
In your from configuration block, you are referencing the runtime configuration, but in the same time you are altering this configuration by adding some exclusion rules. This will alter the original (and unique) runtime configuration which will be used by all other tasks in your build project, as you have guessed. This explains the "Main Class xxx couldn't be found or loaded" error you get when trying to execute the run task, since the runtime configuration (classpath) does not contain the needed library.
If you want to write exclusions rules by group and/or module in your copyDependencies task, one possible way would be to work on a copy of the original runtime configuration; you could define a new Configuration for this purpose:
configurations{
runtimeDeps.extendsFrom runtime
}
task copyDependencies(type: Copy) {
into "$buildDir/libs/dependencies"
from configurations.runtimeDeps {
exclude module: 'groovy'
exclude module: 'aws-java-sdk-s3'
exclude module: 'commons-io'
}
}

How to compile different flavors while building a fat jar using Gradle

I want to include the Java Bindings for V8 ("J2V8") in a Java project. The reasons are that (i) the V8 JavaScript engine is much faster then the JavaScript engine shipped with the JRE and (ii) the library I am using is available in JavaScript only and a port to Java is much effort.
The issue is that J2V8 is compiled for different platforms: linux 64bit, macos 64bit, windows 64 bit, windows 32 bit.
I now want to generate different JARs, containing all the dependencies (fat jars):
jabref-linux_x86_64.jar
jabref-macosx_x86_64.jar
jabref-windows_x86_32.jar
jabref-windows_x86_64.jar
jabref-all.jar - the platform indipendent JAR without v8 engine
I am currently creating fat jars using the shadow plugin.
Note that the project is not an Android project. There, with the Android plugin, it seems to be straight-forward to do that.
The first idea is to introduce configurations and configuration-specific dependencies:
configurations {
linux_x86_64Compile.extendsFrom compile
macosx_x86_64Compile.extendsFrom compile
windows_x86_32Compile.extendsFrom compile
windows_x86_64Compile.extendsFrom compile
}
dependencies {
compile configuration: 'linux_x86_64', group: 'com.eclipsesource.j2v8', name: 'j2v8_linux_x86_x64', version: '4.6.0'
compile configuration: 'macosx_x86_64', group: 'com.eclipsesource.j2v8', name: 'j2v8_macosx_x86_x64', version: '4.6.0'
compile configuration: 'windows_x86_32', group: 'com.eclipsesource.j2v8', name: 'j2v8_win32_x86', version: '4.6.0'
compile configuration: 'windows_x86_64', group: 'com.eclipsesource.j2v8', name: 'j2v8_win32_x86_x64', version: '4.6.0'
...
}
But now I'm stuck. In pseudocode, I'd like to do:
task releaseSingleJar(dependsOn: "shadowJar", name) {
doLast {
copy {
from("$buildDir/libs/JabRef-${project.version}-fat.jar")
into("$buildDir/releases/")
rename { String fileName ->
fileName.replace('-fat', '-$name')
}
}
}
}
task releaseJars() {
forEach name in "linux_x86_64", "macosx_x86_64", "windows_x86_32", "windows_x86_64", "all":
if (name != "all") activate configuration $name
releaseSingleJar($name)
shadowJar is from the shadow plugin.
Background information
Video showing the difference of speed in our setting: https://github.com/JabRef/jabref/pull/2250#issuecomment-264824598
Current state of the integration in JabRef: https://github.com/JabRef/jabref/pull/3180
Related questions
The question Using Gradle to manage Java web app with flavors like Android has a similar title, but asks for source directories, whereas I ask for dependencies. Further, I want to generate a fat JAR and there a plain JAR seems to be enough. However, it might be that the solution is similar. A hint was to use the gradle-java-flavours plugin with the main source being JavaFlavoursExtension.groovy.
Following questions are similar to this one. However, the setting is related to Android apps and not to plain Java apps.
How to define different dependencies for different product flavors
Gradle: add dependency for a specific flavour of the library
Change dependency through a task in gradle
Use different resources for different application flavors using gradle
You might be interested in my gradle-java-flavours plugin which creates source sets, configurations and compile, jar and test tasks for each flavour in a java only project.
eg:
import com.github.jengelman.gradle.plugins.shadow.tasks.*
plugins {
id 'com.lazan.javaflavours' version '1.2'
id 'com.github.johnrengelman.shadow' version '1.2.4'
}
def flavours = ['linux_x86_64', 'macosx_x86_64', ...]
javaFlavours {
flavours.each {
flavour it
}
}
dependencies {
linux_x86_64Compile 'aaa:aaa:1.0'
linux_x86_64Runtime 'bbb:bbb:1.0'
macosx_x86_64TestCompile 'ccc:ccc:3.0'
}
flavours.each { String flavour ->
SourceSet flavourSourceSet = sourceSets.getByName(flavour)
Configuration flavourRuntime = configurations.getByName("${flavour}Runtime")
JavaCompile flavourCompileTask = tasks.getByName("compile${flavour.capitalize()}Java")
Task shadowJarTask = tasks.create(name: "${flavour}ShadowJar", type: ShadowJar) {
classifier = "${flavour}-all"
dependsOn flavourCompileTask
// todo configure (likely based on Configuration and SourceSet)
}
artifacts {
archives shadowJarTask
}
// wire the task into the DAG
assemble.dependsOn shadowJarTask
}

Resources