The process of deploying a piece of software to run on a computer is, perhaps surprisingly, not trivial. An application is a complicated creature that, when deployed, may find itself in unfamiliar environments where it interacts with different hardware, different infrastructure software, and other neighboring programs. Ensuring that an application survives and thrives is the responsibility of both its code and its deployment process. The balance between the two often depends on the languages, runtime and tools used to construct the program, and therefore, different deployment tools may be appropriate for different technology stacks.
While JVM applications require very little from their environment – just a JVM and a kernel – surprisingly, a good general-purpose deployment tool/mechanism for JVM applications did not exist – so far. Fat JARs don’t always work and even when they do they can require platform-specific scripting . More recently some have used Docker to deploy Java apps, but Docker is ill-suited to the task: one of its main purposes is to provide general application portability – something that JVM applications already have – and so it requires downloading, deploying and managing a variety of full-OS images and repositories. As a runtime-agnostic tool, Docker also fails to use the JVMs strengths.
Today, after a year of maturation, we are happy to announce the release of Capsule 1.0, a simple, robust and flexible deployment tool for JVM applications. Capsule caters to the unique strengths and requirements of JVM applications, and we believe it is both the simplest and most powerful way to deploy a JVM application, whether it is a desktop application, a microservice, or complex web application. Capsule works well not just for Java applications but for all JVM languages, from Jruby, Jython and Groovy, through Kotlin, Clojure and Scala, to Frege and OCaml-Java. If you’re writing programs for the JVM, you should give Capsule a try.
One way of thinking about a capsule is as a fat JAR on steroids (that also allows native libraries and never interferes with your dependencies) and a declarative startup script rolled into one; another, is to see it is as the deploy-time counterpart to your build tool. Just as a build tool manages your build, Capsule manages everything from the build to the launch of your application.
We have designed Capsule with the following principles in mind:
- Launching must be deterministic yet flexible, possibly secure and optionally restricted. Launching a capsule requires no startup scripts. The capsule looks for the requested JVM version, sets up the classpath and necessary agents and sets the JVM flags. Capsules can also create their own container when launched – to limit resource use, or to use well-known ports without interference from other programs – and as JVM applications can run in unprivileged containers, those containers are safe. Alternatively, security can be provided through the JVM’s own security mechanism. Additionally, capsules require only a kernel and a JVM – not even a shell – so they can run on JVM microkernels like OSv. All of this functionality is entirely programmable and composable through caplets, components that customize a capsule’s behavior.
- Don’t invent new tools and new standards when existing ones will do. Capsule is written in Java and can be extended in Java. It respects the JVM ecosystem, doesn’t reinvent the wheel, and uses existing tools and standards. A capsule is packaged in an executable JAR, and stores all metadata as simple JAR-manifest attributes; if needed, it is downloaded – fully or partially – from Maven repositories, and it is built using popular JVM build tools like Maven, Gradle or Leiningen. Capsule itself is a simple Maven dependency, as are all the build-tool plugins. There is no need to install new tools.
Capsule Magic with Caplets
The way Capsule provides all this functionality while still staying simple is through caplets, which are modules that customize a capsule’s behavior. Caplets can be embedded in a capsule, or packaged separately and used at the command line to wrap and modify the behavior of an existing capsule.
Capsule’s first caplet was the Maven caplet, that lets you declare some or all dependencies of your application in manifest attributes instead of embedding them in the capsule JAR. While this may not be necessary for many applications, here are two examples of its usage, which demonstrate Capsule’s potential.
The first example is a simple Hello World servlet. When built, it creates a standard WAR file that can be deployed to any servlet container. A closer examination reveals that the WAR is a bit special. Its contents are:
247 META-INF/MANIFEST.MF 1124 WEB-INF/classes/co/paralleluniverse/examples/HelloWorldServlet.class 653 WEB-INF/web.xml 161596 Capsule.class 1467463 capsule-maven-1.0.jar
As you can see, the WAR contains the
Capsule class, which means its a capsule, as well as an embedded JAR,
capsule-maven-1.0.jar which is the Maven caplet. The JAR manifest looks so:
Manifest-Version: 1.0 Main-Class: Capsule Premain-Class: Capsule Caplets: co.paralleluniverse:capsule-maven:1.0 Application: org.eclipse.jetty:jetty-runner:9.3.3.v20150827 Allow-Snapshots: true Min-Java-Version: 1.7.0 Args: $CAPSULE_JAR
If instead of deploying the WAR to a servlet container you execute it directly,
java -jar build/libs/capsule-runnable-war.war (or, even simply
./capsule-runnable-war.war if the capsule is made “really executable” – see the user documentation for instructions), it will automatically download Jetty, and use it to launch the servlet. The Jetty artifact will be cached and shared among other caplets that use it.
Capsule class and the Maven caplet:
608 META-INF/MANIFEST.MF 161596 Capsule.class 1467463 capsule-maven-1.0.jar 266 app.js
When the capsule is launched, the Avatar runtime – including the native libraries specific to the local OS – will be downloaded from a Maven repository, cached locally, and shared with other Avatar capsules.
Other caplets include a daemon caplet, which launches the capsule as a Unix or Windows daemon, a secure caplet, which launches the capsule in a Java sandbox (defined by a specified security policy), a desktop caplet, which turns a capsule containing a GUI application into a native (including icon!) executable for Windows, Mac or Linux, a container caplet, which runs a capsule inside a container and a couple more.
Lightweight Containers for Capsules
Containers are an effective way to sandbox applications as well as to streamline deployment and consolidate servers, so they are a useful dev-ops and security tool regardless of the software stack. However, since JVM applications have minimal environmental requirements (that is, a kernel and a JVM) and are generally portable, using a container solution like Docker is a waste of time, space and convenience. On the other hand, the shield caplet creates a lightweight container for a capsule, without requiring the creation of large images .
For example we can easily run the quasar-stocks web application in a container with simple bridged networking:
java -jar capsule-shield-0.1.0.jar quasar-stocks-thin.jar
We can then easily retrieve the IP address of the container in which the application is running with:
lxc-attach -P ~/.capsule/apps/quasarstocks.Application_0.1.0-SNAPSHOT/capsule-shield/ -n lxc -- /sbin/ifconfig
When everything works as expected, launching the same command on the final deployment server (possibly as a daemon) with port-forwarding configured to make the service public will allow us to have our web app running in a strong security sandbox with near-zero effort.
Head over to capsule.io and start launching capsules!
- They require shadowing to avoid collisions, and often even that is not enough, and they don’t support native libraries.
- They might require non-portable launch scripts to be executed by an OS shell to pick the right JRE version, set up the classpath, agents and JVM parameters.
- All platforms that support the JVM, of course, and the needed scripts and native artifacts that might have been included.
- Apart from potentially some longer launch time because e.g. of dependencies re-download.
- The shield caplet uses LXC to place a capsule into a container.