Reclaiming Java “main()”

Did it ever bother you that none of your Java code is directly runnable? Wouldn’t it be nice if your webapp would behave like a regular UNIX command and did not require a container.

Andrus Adamchik
5 min readFeb 8, 2016

Java Containers

Java containers (app / web) were a great idea when they originally appeared back in the old days. In some ways this was a precursor of the modern virtualization, looking to partition physical hardware between multiple apps. Not to mention a whole list of built-in services (centralized data source configuration, clustering, messaging queues, etc.).

Nowadays the case for Java containers is shrinking rapidly, and their shortcoming are becoming painfully obvious:

There’s no true resource isolation. JVM-as-OS abstraction doesn’t quite work. You can’t allocate dedicated memory or CPU to specific apps within the same container. Besides with hardware virtualization and OS containers, this problem is already solved outside of the JVM.

Reliable hot deploys are an ever elusive goal. It is way too easy to introduce PermGen memory leaks. And while your chances have significantly improved as of late, with ephemeral cloud servers and infrastructure-as-code very few people still care about always-on machines.

Configuration is overly complex. Config data is split between the container, the app (web.xml and friends), and some external app-specific store. This isn’t exactly easy to manage or put in version control.

Containers slow down your development. You need to manage a container inside your IDE, deal with redeploys, or use various tricks to bypass the container during the coding process, and then hope the app works when sent to production.

Not every app is a webapp or can be written as EJBs. Hence Java container is not a universal piece of infrastructure. Yet it is tempting to standardize on a certain kind of container for all apps within an organization, stretching the abstraction way too far (containers running “headless” jobs, data migration tasks and such are not that uncommon).

main() Method and Runnable Jar

This brings us back to the original point made in the title — as Java programmers we are better off doing away with the aging container abstraction and moving towards the apps that behave like regular commands. And of course we can — just write a class with the “main()” method, and run it:

java -cp lib1.jar:lib2.jar com.example.Main arg1 arg2

Or package it in a “runnable” jar, and run the jar:

java -cp lib1.jar:lib2.jar -jar app.jar arg1 arg2

Looking at the command line above, the next most obvious improvement is to avoid dealing with classpath by creating a “fat” jar that bundles all dependencies. Unlike .war files, jars were not intended to encapsulate dependencies, but you can still do it. Tools like maven-shade-plugin make it easy, and the resulting jar can be executed with much less drama:

java -jar app-with-dependencies.jar arg1 arg2

So far so good. We are back to the basics, and can control the “main()” method of our app. But now what?

Container-less Frameworks

After coding for so many years against the servlet spec, it may not be obvious how to write a container-less app that needs to access a database, serve JSON/HTML requests or do something else beyond “hello world”. So some patterns (or dare I say “frameworks”) are needed to fill the void. Luckily there are some choices. The two products at the forefront of the “container-less” movement are Dropwizard and Spring Boot.

Now a short personal story. Not so long ago I started on a new container-less architecture for a big client. I began by writing simple prototypes of a few apps with both Dropwizard and Spring Boot. Compared to the traditional web container/war experience, this felt liberating. Self-contained .jars, apps that behave the same in the IDE as they do in deployment, all without any tricks. YAML config file for everything from web connector port to JDBC URL, to the admin email address.

The last point about the config file sounds almost trivial, but it is a very big deal. The file can be version-controlled. A different file can be provided for each execution of your app, all without any infrastructure changes. Also configuration is very easy to consume — it is automatically deserialized into Java “factory” objects.

Opinionated Too Much

After the initial success I went on to prototype something bigger, namely a bunch of reusable parts that can be combined to build various app types — apps running the jobs, web apps, REST apps, etc., plus integration modules for various subsystems (ORM, Zookeeper, etc.).

This is where “opinionated” nature of both Dropwizard and Spring Boot revealed itself. Turning off uneeded services or changing the structure of command line quickly became a losing fight against the framework.
Dependency injection in Dropwizard is an afterthought. While there’s a great third-party lib that integrates Google Guice DI with Dropwizard, it is a DW “bundle”, and does not allow you to manage Dropwizard itself. I ran into that repeatedly.

Dependency injection is of course the main feature of Spring (and Spring Boot), but the way it worked kept me wondering. It was really hard to control which services and features are enabled and included. One example: adding liquibase.jar to classpath to use with my own Liquibase integration automatically enabled Spring Boot Liquibase feature. Had to use an explicit exclude annotation parameter:

@SpringBootApplication( 
exclude = { LiquibaseAutoConfiguration.class }
)

As the app contained a dozen or so libraries, this became unwieldy rather quickly . Also there was no apparent way for the application to “contribute” objects to a DI Map or Collection defined in an upstream library. This is a basis of a plugin system and is trivial to do in Guice, but proved to be hard and ugly with Spring[Boot]. Could be my inexperience with the later.

Can We Do Better?

My conclusion from the above mixed experience is this:

Both Dropwizard and Spring Boot will free you from container and save you a lot of time when writing typical microservices, but only if you do it their way. Trying to be too fancy (in my case implementing a plugin architecture for a big enterprise system), quickly runs against opinionated nature of the frameworks.

So I ended up starting a project with a goal of building a better mousetrap. The project is called Bootique, and intends to provide a lightweight container-less Java app technology that is at least as easy to use as Dropwizard and Spring Boot, but has as little opinion as possible about the way you write your apps.

For more details read part 2 of this blog dedicated to Bootique. Here I should mention that its early working versions are available on Maven Central, with a bunch of integrations and with minimal documentation. Feel free to ping me on Twitter or via GitHub project page if you have questions about it and want to try it out.

--

--

Andrus Adamchik

ObjectStyle ; Open Source: ApacheCayenne, Agrest, Bootique, and more…