Search code examples
javatomcatgradlebuild.gradlemulti-project

In a multi-project gradle build how do I pack the content of the subproject-A.war into the subproject-B.jar


The problem: Given the following multi-project gradle build

superproject
    subproject-A -> war
    subproject-B -> jar

I am looking for a way to configure subproject-B to unpack the content of the war generated by subproject-A and have the content of the unpacked webapp directory (with classes and resources for a deployment into a container) packed into the application distribution of subproject-B at the root level along with the classes, resources and dependencies of the latter. So the structure of the distribution produced for subproject-B should look like this:

subproject-B-0.1.0
    bin/...
    lib/
        META-INF/ (<-- from B)
        WEB-INF/  (<-- from A.war)
        css/      (<-- from A.war)
        js/       (<-- from A.war)
        *.jar     (code and dependencies of B)

The question of copying a resource from A to B was answered here. Here I need to copy the content of a dynamically generated build artifact (and generally it is ok if that is copied into main/resources as the latter will be packed into the jar).

The rationale: subproject-B is a standalone Java app running tomcat-embed-server with a JavaFX WebView accessing the web app of subproject-B on the localhost turning the otherwise web app into a sort of a standalone desktop app. It is 20 lines of code that run nicely in Eclipse, but seem to pose a packaging challenge for distribution.


Solution

  • I have finally found a solution so posting it here for the benefit of those facing a similar problem:

    subprojectA/build.gradle

    apply plugin: "java"
    apply plugin: "war"
    
    archivesBaseName = "subprojectA"
    
    sourceCompatibility = 1.8
    compileJava.options.encoding = "utf-8"
    
    war {
        manifest {
            archiveName = "$baseName.$extension"
            attributes "Implementation-Title": archivesBaseName, 
                       "Implementation-Version": version
        }
    }
    
    // declare configuration to refer to in superprojectB
    configurations {
        subprojectAwar
    }
    // make this configuration deliver the generated war
    dependencies {
        subprojectAwar files(war.archivePath)
    }
    

    subprojectB/build.gradle

    apply plugin: "java"
    apply plugin: 'application'
    
    archivesBaseName = "subprojectB"
    
    sourceCompatibility = 1.8
    compileJava.options.encoding = "utf-8"
    
    // declare configuration to take files from
    configurations {
        subprojectAwar
    }
    
    dependencies {
        // bind the configuration to the respective configuration in subprojectA
        subprojectAwar project(path: ":subprojectA", configuration: "subprojectAwar")
    
        compile "org.apache.tomcat.embed:tomcat-embed-core:$tomcatEmbedVersion"
        compile "org.apache.tomcat.embed:tomcat-embed-websocket:$tomcatEmbedVersion"
        compile "org.apache.tomcat.embed:tomcat-embed-logging-log4j:$tomcatEmbedVersion"
        compile "org.apache.tomcat.embed:tomcat-embed-jasper:$tomcatEmbedVersion"
    }
    
    mainClassName = 'org.project.subprojectB.StartServer'
    
    jar {
        // make sure this project is assembled after the war is generated
        dependsOn(":subprojectA:assemble")
    
        manifest {
            archiveName = "$baseName.$extension"
            attributes "Implementation-Title": archivesBaseName,
                       "Implementation-Version": version
            attributes 'Main-Class': '$mainClassName'
        }
        // if you need to copy the content of the war into the jar:
        // (otherwise only to the distribution, see below)
        /*
        from(zipTree(configurations.subprojectAwar.collect { it }[0])) {
            into ""
            exclude '**/META-INF/**'
        }
        */
    }
    
    // copy the content of the war excluding META-INF into the lib of superprojectB
    applicationDistribution.from(zipTree(configurations.subprojectAwar.collect { it }[0])) {
         into "lib"
         exclude '**/META-INF/**'
    }