JVM in the uWSGI server (updated to 1.9)

Introduction

As of uWSGI 1.9, you can have a full, thread-safe and versatile JVM embedded inthe core. All of the plugins can call JVM functions (written in Java, JRuby,Jython, Clojure, whatever new fancy language the JVM can run) via the RPCsubsystem or using uWSGI The uWSGI Signal Framework The JVM plugin itself canimplement request handlers to host JVM-based web applications. CurrentlyThe JWSGI interface and The Clojure/Ring JVM request handler (Clojure) apps are supported. A long-term goal issupporting servlets, but it will require heavy sponsorship and funding (feelfree to ask for more information about the project at info@unbit.it).

Building the JVM support

First of all, be sure to have a full JDK distribution installed. The uWSGIbuild system will try to detect common JDK setups (Debian, Ubuntu, Centos,OSX…), but if it is not able to find a JDK installation it will need someinformation from the user (see below). To build the JVM plugin simply run:

  1. python uwsgiconfig.py --plugin plugins/jvm default

Change ‘default’, if needed, to your alternative build profile. For example ifyou have a Perl/PSGI monolithic build just run

  1. python uwsgiconfig.py --plugin plugins/jvm psgi

or for a fully-modular build

  1. python uwsgiconfig.py --plugin plugins/jvm core

If all goes well the jvm_plugin will be built. If the build system cannot finda JDK installation you will ned to specify the path of the headers directory(the directory containing the jni.h file) and the lib directory (the directorycontaining libjvm.so). As an example, if jni.h is in /opt/java/includes andlibjvm.so is in /opt/java/lib/jvm/i386, run the build system in that way:

  1. UWSGICONFIG_JVM_INCPATH=/opt/java/includes UWSGICONFIG_JVM_LIBPATH=/opt/java/lib/jvm/i386 python uwsgiconfig --plugin plugins/jvm

After a successful build, you will get the path of the uwsgi.jar file. Thatjarball contains classes to access the uWSGI API, and you should copy it intoyour CLASSPATH or at the very least manually load it from uWSGI’sconfiguration.

Exposing functions via the RPC subsystem

In this example we will export a “hello” Java function (returning a string) andwe will call it from a Python WSGI application. This is our base configuration(we assume a modular build).

  1. [uwsgi]
  2. plugins = python,jvm
  3. http = :9090
  4. wsgi-file = myapp.py
  5. jvm-classpath = /opt/uwsgi/lib/uwsgi.jar

The jvm-classpath is an option exported by the JVM plugin that allows youto add directories or jarfiles to your classpath. You can specify as manyjvm-classpath options you need. Here we are manually adding uwsgi.jaras we did not copy it into our CLASSPATH. This is our WSGI example script.

  1. import uwsgi
  2.  
  3. def application(environ, start_response):
  4. start_response('200 OK', [('Content-Type','text/html')])
  5. yield "<h1>"
  6. yield uwsgi.call('hello')
  7. yield "</h1>"

Here we use uwsgi.call() instead of uwsgi.rpc() as a shortcut (littleperformance gain in options parsing). We now create our Foobar.java class. Itsstatic void main() function will be run by uWSGI on startup.

  1. public class Foobar {
  2. static void main() {
  3.  
  4. // create an anonymous function
  5. uwsgi.RpcFunction rpc_func = new uwsgi.RpcFunction() {
  6. public String function(String... args) {
  7. return "Hello World";
  8. }
  9. };
  10.  
  11. // register it in the uWSGI RPC subsystem
  12. uwsgi.register_rpc("hello", rpc_func);
  13. }
  14. }

The uwsgi.RpcFunction interface allows you to easily write uWSGI-compliantRPC functions. Now compile the Foobar.java file:

  1. javac Foobar.java

(eventually fix the classpath or pass the uwsgi.jar path with the -cp option)You now have a Foobar.class that can be loaded by uWSGI. Let’s complete theconfiguration…

  1. [uwsgi]
  2. plugins = python,jvm
  3. http = :9090
  4. wsgi-file = myapp.py
  5. jvm-classpath = /opt/uwsgi/lib/uwsgi.jar
  6. jvm-main-class = Foobar

The last option (jvm-main-class) will load a java class and will executeits main() method. We can now visit localhost:9090 and we should see theHello World message.

Registering signal handlers

In the same way as the RPC subsystem you can register signal handlers. Youwill be able to call Java functions on time events, file modifications, cron…Our Sigbar.java:

  1. public class Sigbar {
  2. static void main() {
  3.  
  4. // create an anonymous function
  5. uwsgi.SignalHandler sh = new uwsgi.SignalHandler() {
  6. public void function(int signum) {
  7. System.out.println("Hi, i am the signal " + signum);
  8. }
  9. };
  10.  
  11. // register it in the uWSGI signal subsystem
  12. uwsgi.register_signal(17, "", sh);
  13. }
  14. }

uwsgi.SignalHandler is the interface for signal handlers.

Whenever signal 17 is rased, the corresponding JVM function will be run.Remember to compile the file, load it in uWSGI and to enable to master process(without it the signal subsystem will not work).

The fork() problem and multithreading

The JVM is not fork() friendly. If you load a virtual machine in the masterand then you fork() (like generally you do in other languages) the children JVMwill be broken (this is mainly because threads required by the JVM are notinherited). For that reason a JVM for each worker, mule and spooler isspawned. Fortunately enough, differently from the vast majority of otherplatforms, the JVM has truly powerful multithreading support. uWSGI supportsit, so if you want to run one of the request handlers (JWSGI, Clojure/Ring)just remember to spawn a number of threads with the —threads option.

How does it work?

uWSGI embeds the JVM using the JNI interface. Unfortunately we cannot rely onJVM’s automatic garbage collector, so we have to manually unreference all ofthe allocated objects. This is not a problem from a performance and usage pointof view, but makes the development of plugins a bit more difficult compared toother JNI-based products. Fortunately the current API simplifies that task.

Passing options to the JVM

You can pass specific options to the JVM using the —jvm-opt option.

For example to limit heap usage to 10 megabytes:

  1. [uwsgi]
  2. ...
  3. jvm-opt = -Xmx10m

Loading classes (without main method)

We have already seen how to load classes and run their main() method onstartup. Often you will want to load classes only to add them to the JVM(allowing access to external modules needing them) To load a class you can use—jvm-class.

  1. [uwsgi]
  2. ...
  3. jvm-class = Foobar
  4. jvm-class = org/unbit/Unbit

Remember class names must use the ‘/’ format instead of dots! This rule appliesto —jvm-main-class too.

Request handlers

Although the Java(TM) world has its J2EE environment for deploying webapplications, you may want to follow a different approach. The uWSGI projectimplements lot of features that are not part of J2EE (and does not implementlot of features that are a strong part of J2EE), so you may find its approachmore suited for your setup (or taste, or skills).

The JVM plugin exports an API to allow hooking web requests. This approachdiffers a bit from “classic” way uWSGI works. The JVM plugin registers itselfas a handler for modifier1==8, but will look at the modifier2 value to knowwhich of its request handlers has to manage it. For example the The Clojure/Ring JVM request handlerplugin registers itself in the JVM plugin as the modifier2 number ‘1’. So topass requests to it you need something like that:

  1. [uwsgi]
  2. http = :9090
  3. http-modifier1 = 8
  4. http-modifier2 = 1

or with nginx:

  1. location / {
  2. include uwsgi_params;
  3. uwsgi_modifier1 8;
  4. uwsgi_modifier2 1;
  5. uwsgi_pass /tmp/uwsgi.socket;
  6. }

Currently there are 2 JVM request handlers available:

As already said, the idea of developing a servlet request handler is there, butit will require a sponsorship (aka. money) as it’ll be a really big effort.

Notes

  • You do not need special jar files to use UNIX sockets – the JVM plugin hasaccess to all of the uWSGI features.
  • You may be addicted to the log4j module. There is nothing wrong with it, butdo take a look at uWSGI’s logging capabilities (less resources needed, lessconfiguration, and more NoEnterprise)
  • The uWSGI API access is still incomplete (will be updated after 1.9)
  • The JVM does not play well in environments with limited address space. Avoidusing —limit-as if you load the JVM in your instances.