0

I’m working on a multi-project gradle springboot application using IntelliJ. I’m using Gradle for the dependencies and jars includes.

The main module (springboot) includes the other two, of which one of these uses an external library, out of our control that has to be located in a directory of mine. This external library contains its own settings located in a specific folder.

When I create the main jar (the one I want to execute) it includes all the depending jars, the two modules jars plus the external library jar.

First of all, I tried to add the class-path on the resources/META-INF/MANIFEST.MF of the module who needs the library, but when my intelliJ creates the Jar the resource is removed. So because this library uses the settings from a /configs directory located 2 directories behind where the jar is. I have also tried adding the /config directory with my configurations inside the main jar 2 directories before finding the library jar, but it doesn’t work as It should. The program is unable to locate its configurations.

On the other hand, I have tried removing the library jar from the main jar and running the main jar using the command:

Java –cp (library directory) –jar (main jar)

And also

java -cp myJar.jar;/lib/xxx -Dloader.main=myMainApplicationClass org.springframework.boot.loader.PropertiesLauncher

[xxx represents one more directory before find the library jar]

see message Spring Boot Executable Jar with Classpath)

But I get a ClassNotFoundException error, so the module who uses the library couldn't found it. How can I add the settings that the library jars and one module needs? so when I run the main jar it loads them correctly.

Finally, I must add that if I run the application from IntelliJ the program works correctly. The problem is when I want to run the application from the command line using the main jar.

Thank you!

EXAMPLES:

Example from what I have and what I want (Structure)

Config directory have to be externally from the External Library .JAR

build.gradle from module A:

plugins {
    id 'java'
}

group 'MYCOMPANY.GROUP'
version '0.0.1-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    implementation group: 'org.projectlombok', name: 'lombok', version: '1.18.20'
    annotationProcessor 'org.projectlombok:lombok:1.18.20'

    implementation files('MODULE B.JAR')
    implementation files('EXTERNAL LIBRARY .JAR')
    
    
    implementation group: 'com.android.tools.build', name: 'gradle', version: '2.3.0'
    //Includes 
    implementation group: 'javax.mail', name: 'mail', version: '1.4.7'
    implementation 'org.apache.poi:poi:5.0.0'
    implementation 'org.apache.poi:poi-ooxml:5.0.0'
    //Includes from utils
    implementation group: 'net.sourceforge.jexcelapi', name: 'jxl', version: '2.6.12'
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-s3', version: '1.12.39'


}

test {
    useJUnitPlatform()
}

build.gradle from moduleB:

group 'MYCOMPANY.GROUP'
version '0.0.1-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'

    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.12.3'
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.3'
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.3'
}

test {
    useJUnitPlatform()
}

build.gradle from main

plugins {
    id 'org.springframework.boot' version '2.5.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'MYCOMPANY.GROUP'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {

    implementation files('MODULE A.JAR')
    implementation files('MODULE B.JAR')

    //This includes the external library into main.JAR 
    //Allows to external library find the configs directory into JAR resources 
    implementation files('EXTERNAL LIBRARY .JAR')  

    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-web-services'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    implementation 'org.aspectj:aspectjweaver:1.9.2'

    implementation 'org.apache.poi:poi:5.0.0'
    implementation 'org.apache.poi:poi-ooxml:5.0.0'

    implementation group: 'com.google.guava', name: 'guava', version: '30.1.1-jre'

    implementation group: 'org.seleniumhq.selenium', name: 'selenium-chrome-driver', version: '3.141.59'

    implementation 'org.json:json:20210307'

    implementation group: 'com.android.tools.build', name: 'gradle', version: '2.3.0'


}

test {
    useJUnitPlatform()
}
Marc
  • 1
  • 1
  • Could you show the project structure or give us a minimal example? – chehsunliu Aug 03 '21 at 15:42
  • Here you have it! Thanks! – Marc Aug 05 '21 at 09:14
  • Hi @Marc, could you also paste the `build.gradles` of the main module and module B? It would be much easily for me to reproduce this case. Thanks – chehsunliu Aug 05 '21 at 11:19
  • Here you have it! Thanks! – Marc Aug 06 '21 at 12:13
  • Did you mean your project structure has `/path/to/rooProject/projectA/build.gradle` and `/path/to/rooProject/projectB/build.gradle`, and there is an external library `/path/to/externalProject/build.gradle` and `/path/to/externalProject/src/main/resources/config/application.properties`? – chehsunliu Aug 09 '21 at 04:49
  • The externalProject is a .JAR only, so I don't have the build.gradle from it. I have the `/path/to/rootProject/build.gradle` (main), the both `path/to/rootProject/projectA or B/build.gradle` and a `path/to/rootProject/lib/externalLibrary.JAR`. Also I have the `path/to/rootProject/config/application.properties` from the project properties and `path/to/rootProject/src/main/java/resources/configs/custom.properties` from the externalLibrary.jar properties. What I need is haven't to include this configs inside resources because I need to be able to modify them before running the application. – Marc Aug 09 '21 at 07:20
  • Simply according to the structure in your comment, the default Gradle packaging behavior will make `application.properties` not included in the main JAR and `custom.properties` included. Therefore, your application code can load `custom.properties` via classpath and load `config/application.properties` at the Spring initialization. And for JARs, since they are all declared by `implementation`, their `.class` files will all be packaged into the main JAR. – chehsunliu Aug 09 '21 at 08:25
  • How does `externalLibrary.jar` interact with the two configs `application.properties` and `custom.properties`? Which config will you modify before running the application? – chehsunliu Aug 09 '21 at 08:25
  • Thank you for your dedication. What you describe is the current behavior. I need to modify `configs/custom.properties`. Please be aware that is not `config/application.properties`. As of now I can not modify `configs/custom.properties` because it is inside the `main.jar`. It also happens that the `externalLibrary.jar` is inside the `main.jar` while it will be more confortable for us if it is outside. `externalLibrary.jar` does not interact with `config/application.properties` only with `configs/custom.properties` – Marc Aug 10 '21 at 07:54

1 Answers1

0

You could try to not include externalLibrary.jar by changing the dependencies in main and a's build.gradle files to:

dependencies {
    ...
    compileOnly files("path/to/folder/externalLibrary.jar")
    testImplementation files("path/to/folder/externalLibrary.jar")
    ...
}

Then start your Spring application by this command:

$ java -Dloader.path=path/to/folder/ -jar path/to/main.jar

I've tested this setup and it works. However, I do not know how externalLibrary.jar interacts with configs/custom.properties. If it reads it via classpath instead of filesystem, things could be more compilcated.

chehsunliu
  • 1,559
  • 1
  • 12
  • 22
  • Thank you for your dedication!! Unfortunately it doesn't work properly. I got java.lang.ClassNotFoundException when my program is trying to search the class inside externalLibrary.jar. I have tested it by putting the configs directory, in the same path as `main.jar` and also in the same path as `externalLibrary.jar` but it doesnt work. It only works when I have the `configs/custom.properties` inside the `main.jar` – Marc Aug 16 '21 at 10:28