Containerless applications, or more precisely applications with functionality formerly provided by a container bundled directly, are starting to be a hot topic these days. Deliery of enterprise Java application could of course be achieved for a long time. But today, we find ourselves in a situation where all major players support such approach directly.
Applications with container functionality welded directly onto the application are for some reasons mentioned together with microservices - probably because the process of “dockerization” or just cloud deployment is somewhat more straight forward. The mere fact that we no longer need to create single uberjar seems interesting.
I myself did not make it into production yet. It just happened that now I have a suitable project to try the whole new package in real world. I need REST API, the whole system will be broken into two smaller services. I don’t hide my fascination with GraphQL, but whole world is now focused on REST and since those services will be publicly available, our clients probably do not yet known Graph-oriented approach is here :)
My task was to have a look at few well-known all-in-one solutions and create a simple application with REST API, with more functionality available prospectively. This blogpost just shared first-ride impressions and thoughts. If there is something incorrect, please do contact me and correct me !
Java EE provides more “micro” containers. Each one is vendor-specific, but the core principle of standard compatibility remains. Therefore, micro-containers are interchangeable with little effort, if needed. First one to try is Wildfly Swarm. It’s main goal is claimed to be “rightsizing” applications. I’ll get to the “rightsize” part later.
Wildfly Swarm offers a project generator, that looks like an exact copy of Spring Boot Initializr. With the provided generator, developers are able to include just the right components used. The generated POM is clean and dependencies are well formatted. Here are just the dependencies required for JAX-RS. I know the CDI part is probably unnecessary, but who would use JAX-RS without dependency injection anyway ? Can’t image real-world non-trivial application.
The application generated by the generator was complex and contained and example JAX-RS endpoint.
WildFly works with the specification API, rather than with implementation. A traditional Java EE way to do things. The resulting code will not become WildFly-Swarm dependent. It can be easily ran on classical application server, as well as swapped for different “righsized” solution.
The JAX-RS part is really simple and pleasant. The endpoint was automatically recognizd. There is only one single class in the whole project. No configuration classes or files, nothing. Just pure simplicity well known in Java EE world.
- Startup time 3.1 seconds (measured manually)
Creating a microservice with REST interface was especially easy. The generated project demonstrated no need for any configuration at all.
- JAX-RS integration
- Fully Java EE compliant
- Generated example application is complex
- No Gradle support in generator
Spring boot’s website comes second. Easy and straightforward. I was able to start in seconds, eventhough I had never seen the website before. My eyes were immediately attracted by Spring’s generator called SPRING INITIALIZR, similar to Wildfly Swarm. I don’t know who was first (I bet Spring was), but those websites are pretty identical. However, Spring’s support for Gradle is what I like. I am completely against using Maven nowadays on new projects (yet I do it) and I appreciate Spring’s support for Gradle - a great tool to work with ! Anyway, looks like Spring also provides “rightsizing” like WildFly does, without the buzzwords.
Spring + JAX-RS
Spring also supports JAX-RS side by side to Spring Rest MVC. Because I consider JAX-RS to be much better designed, I tried to give it a shot. Spring Boot uses Jersey as JAX-RS implementation, an excellent choice indeed ! However, my eye saw some hk2 dependencies included. I wonder if those are really needed, but I don’t want to go that deep.
First disappointment came after the generate example was downloaded. The pom.xml is clean, possible the cleanest one among it’s rivals. However, no JAX-RS demo was present. Only a DemoApplication class with main method. The main method was especially simple and clean. Thumbs up here.
Anyway, let’s get back to the missing JAX-RS endpoint in the downloaded demo. The JAX-RS dependencies were present in pom.xml, but no JAX-RS endpoint ! When I added one, it was not discovered. After googling the documentation and having a look at part 2.7, I found what I needed. More configuration is required. Looks like in case of Spring Boot, we have to work with Jersey directly (no JAX-RS abstraction) and register the endpoint manually. Or write/google some piece of code or configuration to do it for you. I found the missing link in under one minute, but the magic was gone. It was the same old Spring feeling, where finding missing pieces of code or configuration was a routine. Thumbs down with this. I could see how a more junior developer could struggle here. It’s no big deal for experienced guys and I must admit the documentation is good.
- Startup time 2.004 seconds (measured by Spring itself)
Spring MVC Rest
Eventhough JAX-RS is not integrated as seamlessly (without any configuration required) as in pure Java EE solutions, I admit Spring’s main focus is it’s own MVC Rest. Here, the integration is working out of the box without any additional configuration. In other words, The REST controller added to the project was discovered seamslessly.
Spring’s REST MVC architecture is somewhat less pretty for me, but this is probably a matter of personal taste.
- Startup time 2.54 seconds (measured by Spring itself)
From the two options, I would go with Spring REST MVC instead of Spring + JAX-RS integration. Spring itself introduces high degree of vendor lock-in and there is no point of working with Jersey directly, when MVC Rest integration is so much better and probably more tried and tested over the time. Overall, I was satisfied once the application started.
- Easy to start & understand
- Clean pom.xml
- Excellent support for Gradle in generator
- Fast start
- Test stub in generated example
- Generator did not generate working JAX-RS example
- Still requires unnecessary configuration for JAX-RS
- Vendor lock
Payara Micro approach is more opinionated than any other solutions on first sight. There are two ways to operate with Payara Micro documented directly. A more classical way of starting the instance programatically is of course fully supported. This process is easy and straightforward as it is in case of the others.
An instance can be also started by just starting a new JVM process with Payara micro in it.
Wars can be deployed very simply. There are many options, even exploded wars can be deployed, multiple applications can be deployed at once … everything seems to be covered at first sight. Applications can even be deployed directly from a Maven repository ! Thumbs up ! Programatic deployment at runtime can also be done !
Creating the endpoint
I wanted the implementation to be similar to other solutions, so I chose the programatic startup. However, there is no example project generator. Looks like Payara Micro just comes as a well-picked set of functionalities, so no generator is really necessary. It supports just about everything needed and the old stuff that is no longer used on new project nowadays is just removed. However, a simple link to an example application available for download or just a link to an official GitHub example would be nice. Could not find such thing easily. There seems to be support for both Maven and Gradle, because I can see people on GitHub using it this way. Good.
Anyway, I want to start the application already, not to explore any more options, since there are obviously many with Payara Micro :) Finding out what to addinto Maven’s POM takes few clicks and few quick minutes of documentation reading to reveal the right link. The Payara Micro jar with
--outputUberJar option is called directly as a part of Maven build, producing a runnable fat jar.
- Startup time 3.3s
All values are of course after warmup.
Payara Micro has slightly different approach. The result is the same, but it does not try to hide everything before the user. The user is directly in contact with the wrapping mechanism. At first, this was not as easy as Spring Boot or WildFly Swarm. But, there is an upside to this. The deployed WAR is just any ordinary WAR. Absolutely no changes to existing solutions are required. Using Payara Micro is a matter of setting-up the build process. I liked the directly documented ways of runtime programmatic deployment. This is Payaras strong side. I also noticed there are [docker images] with just enough to run Java EE applications on payara. Well, these are some supreme services.
- Many ways of starting the container
- Deploy/run process well designed
- No project generator for newbies
Having application server / container abstracted, be it full-profile Java EE server or “just” a container like Tomcat, might come in handy. There is always an old server/container running somewhere that is hard to many reasons, like strict company policy. Giving developers the chance to ship everything right in the app makes things easier. The size of the deployment unit is probably irrelevant nowadays unless really astronomical, but I can already think of one person that would probably not agree with me - Adam Bien, who is a fan of thin wars. Watch Adam’s video, he has a strong point. The programmatic API seems interesting and there are many possibilities coming with it, yet I am now very cautious about next steps.
Of course, there are more ways to achieve the feeling all-in-one packaging. Purely dockering the whole solution is one way to go. A servlet container with regular Spring or a regular Application Server of your choice with regular Java EE application can also be dockerized. Combination of Docker and an uberjar is also possible. Mainly due to the ability to ship latest dependencies with the application, for example JDK. Why would one want to bundle JDK ? Because real-world servers are often administrated by people who do not understand Java and are able to come up with a million and one reasons not to update Java/Tomcat/Whatever on their machines.
All solutions performed about the same way with approximately 30,000 requests per second on the same machine after warmup period. Result variance was extremely low and probably negligible. But no serious performance tests were done and no serious statistical methods were used to come to such conclusion :)
We’re heading towards architectural changes. Smells like fresh air to me. The era of “micro” buzzword is here. Which one is better ? I am just glad to see progress everywhere. By the way, Spring guys are already working on GraphQL integration. Nobody uses it now, yet I believe and also kind-of hope this will be the next big thing soon.
A little note at the end, I did not even think about measuring package size. Nobody cares about disk size these days and the results were perfectly fine in all cases, be it Spring or the Java EE solutions.