'How to build a fat jar with multi-project and Gradle?

I'm new to Gradle, and is struggling with building a multi-projects fat jar.

I'm trying to do the same as mentioned here: Gradle multiple jars from single source folder

I have three packages, the dependency is: PandaService => PandaServiceDataAccessLayer => PandaDatabaseDocker.

Now I want to build a fat jar for PandaService. However, I'm not able to get ./gradlew build work. Now it is complaining that Plugin with id 'org.springframework.boot' not found. How can I fix it?

Will also appreciate any advises on changing build.gradle to follow the best practices.

More Info

This is the settings.gradle of PandaService:

rootProject.name = "PandaService"
include "PandaService"
include "PandaDatabaseDocker"
include "PandaServiceDataAccessLayer"

This is the build.gradle of PandaService:

subprojects {
    apply plugin: 'java'
}

project(':PandaDatabaseDocker') {
    sourceSets {
        main {
            java {
                srcDir '../src'
                include 'PandaDatabaseDocker/**'
            }
        }
    }
}

project(':PandaServiceDataAccessLayer') {
    sourceSets {
        main {
            java {
                srcDir '../src'
                include 'PandaServiceDataAccessLayer/**'
            }
        }
    }
    dependencies {
        implementation project(':PandaDatabaseDocker')
    }
}

project(':PandaService') {

    repositories {
        mavenCentral()
    }

    group = 'PandaService'
    version = '0.0.1'
    sourceCompatibility = '11'
    targetCompatibility = '11'

    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    sourceSets {
        main {
            java {
                srcDir '../src'
                include 'PandaService/**'
            }
        }
    }
    dependencies {
        // Add depended local modules
        implementation project(':PandaServiceDataAccessLayer')

        // Spring boot + Jersey
        implementation 'org.springframework.boot:spring-boot-starter-parent:2.6.4'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.springframework.boot:spring-boot-starter-jersey'
        compileOnly("org.springframework.boot:spring-boot-devtools")
        testImplementation 'org.springframework.boot:spring-boot-starter-test'

        // Swagger
        implementation 'io.swagger:swagger-jersey2-jaxrs:1.6.5'

        // Lombok
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'

        // log
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.springframework.boot:spring-boot-starter-log4j2'


        // RDS Connection
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        runtimeOnly 'mysql:mysql-connector-java:8.0.27'

        // AWS secretes manager
        implementation 'com.amazonaws.secretsmanager:aws-secretsmanager-jdbc:1.0.6'

        // JOOQ
        implementation 'org.springframework.boot:spring-boot-starter-jooq'

        // HikariCP
        implementation 'com.zaxxer:HikariCP:5.0.1'
    }

    //create a fat Jar with all dependencies
    jar {
        duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
        dependsOn configurations.runtimeClasspath
        from {
            configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
        }
        manifest {
            attributes "Main-Class": "com.PandaService.MainApplication"
        }
    }

    configurations {
        all*.exclude module: 'spring-boot-starter-logging'
        all*.exclude module: "logback-classic"

        compileOnly {
            extendsFrom annotationProcessor
        }
    }

    test {
        useJUnitPlatform()
    }
}




Solution 1:[1]

I sorted it out.

The solution is to use Gradle Composite Builds. In this way, I don't even need to have a wrapper root project.

Reference:

Solution 2:[2]

@/matmul handles batches nicely, but the rules are that for 3d arrays, the first dimension is the batch, and dot is done on the last 2 dimensions, with the usual "last of A with the second to the last of B" pairing.

It took a bit of reading to decipher you description but it appears that you want the first of p to the batch, and last of vec to be the batch. That means vec needs to transformed to a (2,5,1) to work with the (2,5,5) p.

In [176]: [email protected][:,:,None]
Out[176]: 
array([[[  60],
        [ 160],
        [ 260],
        [ 360],
        [ 460]],

       [[ 695],
        [ 820],
        [ 945],
        [1070],
        [1195]]])

The result is (2,5,1). We can squeeze out the the last to get (2,5), but apparently you want a (5,2)

In [179]: ([email protected][:,:,None])[...,0].T
Out[179]: 
array([[  60,  695],
       [ 160,  820],
       [ 260,  945],
       [ 360, 1070],
       [ 460, 1195]])

np.einsum('nij,jn->in', P, vec) does effectively the same, with the n as the batch dimension that is 'carried through' to the result, and sum-of-products on the shared j dimension.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yang Liu
Solution 2 hpaulj