We speed up the assembly and delivery of java web applications

TLTD


  1. removed jar from project build
  2. replaced it with task, which is 7 times faster


Details and result under the cut.


about the project


A web service in java that gives out rest api and websockets , but inside it can go to a distributed database and distributed cache .


The project uses embedded jetty to start, runs through public status void method .


It is delivered to the server as fat jar and is launched through java -jar myapp.jar app.yaml


We profile


Gradle is a great tool that comes out of the box with a profiler. Run the build with the parameter --profile and wait for the result.


./gradlew clean build --profile

I think the result does not need commenting:



We study the problem


First of all, I decided to see how fat jar is being created:


jar {
    manifest {
        attributes "Main-Class": "com.baeldung.fatjar.Application"
    }

    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

We write the class with the main method in the manifest and unpack the jar dependencies into the root folder of the jar archive.


This can be checked if you do it by unpacking the jar unzip myapp.jar and see the tree of the current folder tree .


It is not surprising that this is slow, many small files must first be unzipped and then back packed.


Optimize


Next, I tried to google a faster version of creating a jar file.


I tried gradle plugins:


gradle-fatjar-plugin -
shadow is no longer supported
- they managed to assemble them, but it uses the same method as above, so this did not give an increase in speed
gradle-one-jar - could not start at all, honestly, maybe it was necessary just spend more time


Then I got an idea, you can even say a challenge. And how to run applications without jar? I just had an unpacked archive, in order to try this.


It turned out not difficult:


java -cp . com.example.Main app.yml

The project started perfectly, picked up the desired config.


The -cp parameter is a classpath that tells java the process where all the project classes are located.


So the project can live without jar? Using a little help from the gradle community, I got a task that creates an exploded version of the jar:


task explodedJar(type: Copy) {
    with jar
    into "${buildDir}/exploded"
    into('lib') {
        from configurations.runtimeClasspath
    }
}
jar.enabled = false
assemble.dependsOn explodedJar

Task


  1. puts all classes and resources in a exploded folder
  2. puts all runtime dependencies in the lib folder
  3. adds explodedJar and excludes task jar from ./gradle build

Run again


./gradlew build --profile

Enjoy the result


I think comments are not needed here again.



Here it may still be worth duplicating the histogram from the beginning of the article, but I will not do this.


But how to deploy?


In order not to make this article very long, I just leave one command to copy the project to the server:


rsync --delete -r build/exploded api.example.com:/opt/myapp

Summary


  • The project has become easier due to the fact that we removed from it such an entity as jar
  • You can always see what exactly gets into our assembly by simply opening the folder build/exploded
  • And of course, the project began to assemble and share faster