You are currently browsing the archives for the rpc category


MessagePack-RPC with Clojure and the AOT compilation

A few of days ago, during a night without sleep, I decided to play a little bit with MessagePack-RPC for Java. It was absolutely a fun time but I ended feeling like it might be better. So I decided to make it more fun and I rewrote those client/server programs in Clojure.

As usual, those codes are on my GitHub account:

Those are two really simple samples, far from those of the Real World, of course, but them still worth enough to try the underlying concept and have fun.

One or two interesting things there

During my experiment, I realized some interesting things about compiling Clojure code ahead of time (AOT) and Java interop that I thought I might share briefly here. It happened due to the nature of the implementation of the MessagePack-RPC for Java library once it does data serialization and deserialization.

Here you will see compilation of Clojure code and what it results. If you want to run the samples, which is worth,you can find instructions in the project’s README.

So let’s look on…

Getting the Clojure project’s code

Once the whole project’s code is on GitHub, this is quite simple task:

$ git clone https://github.com/leandrosilva/msgpackrpc-sample-clojure

Important files in the project:

In this blog post we are going to focus only on server.clj and client.clj.

Inspecting the server

Now you have the code, compile only the server code at first time.

$ lein compile msgpackrpc-sample.server

Ok. So what did the compilation above generated?

$ cd target/classes

$ ls -la

  MathServer.class
  msgpackrpc_sample

The compilation generated:

  • MathServer class
  • And msgpackrpc_sample directory – which is the sanitized name for mesgpackrpc.sample namespace, as you can imagine
Right. This is going to be interesting. Let’s inspect msgpackrpc_sample directory.
$ ls -la msgpackrpc_sample/

  server$_add.class
  server$_div.class
  server$_main.class
  server$_mul.class
  server$_sub.class
  server$loading__4784__auto__.class
  server__init.class

Its content is:

  • A class to load the namespace code
  • A class to initialize the namespace
  • And a class for every function included in the namespace
Really interesting, non? But let’s go further more and inspect MathServer class now.
$ javap -c MathServer

Output:

  public class MathServer extends java.lang.Object {
      public static {};
        Code:
        ...
      public MathServer();
        Code:
        ...
      public java.lang.Object clone();
        Code:
        ...
      public int hashCode();
        Code:
        ...
      public java.lang.String toString();
        Code:
        ...
      public boolean equals(java.lang.Object);
        Code:
        ...
      public int add(int, int);
        Code:
        ...
      public int sub(int, int);
        Code:
        ...
      public int mul(int, int);
        Code:
        ...
      public double div(int, int);
        Code:
        ...
  }

I’m not sure whether you are familiarized with Clojure’s gen-class macro or not but it is the magic that has generated the Java code above.

(gen-class
  :name MathServer
  :methods [[add [int int] int]
            [sub [int int] int]
            [mul [int int] int]
            [div [int int] double]])

Putting it simple, the code above define like a public interface for MathServer class and the code below implements them, as follow.

(defn -add [this a b]
  (+ a b))

(defn -sub [this a b]
  (- a b))

(defn -mul [this a b]
  (* a b))

(defn -div [this a b]
  (/ a b))

This is a pretty much exciting thing, non? This is because if you need to interop your Clojure program/library with any Java program/library (legacy or not) which requires specific interface, you can design it quickly and unpainfuly using gen-class. Further more, you can design it up front, compile AOT, and finally delivery it as any regular Java .class file. Awesome!

Are you curious about the hyphen prefix to every function?

This is the notation to say that a given method implementation should bind to a function with its name plus hyphen prefix. It is possible to define a prefix other than hyphen, which is the default one, using :prefix option of gen-class macro. So if you define :prefix option as “banana-” for MathServer class, every function that implements a method of MathServer class should start with “banana-“.

You can find an example here.

Inspecting the client

The same way you did for server code, you have to do to the client code.

$ lein compile msgpackrpc-sample.client

Ok. Since you already did it before let’s move a little fast here (without verbiage of my end, which is good to you. :)).

$ cd target/classes

$ ls -la

  IMath.class
  MathServer.class
  msgpackrpc_sample

$ ls -la msgpackrpc_sample/

  client$_main.class
  client$loading__4784__auto__.class
  client__init.class
  ifaces$loading__4784__auto__.class
  server$_add.class
  server$_div.class
  server$_main.class
  server$_mul.class
  server$_sub.class
  server$loading__4784__auto__.class
  server__init.class

I’m pretty sure you got it, since it is close to what you saw for server before, but I’d like to just comment a little bit. The compilation generated:

  • IMath class, which is an interface actually
  • Classes for client and ifaces namespaces, pretty much the same happened for server – note that those two are declared in the same file client.clj

So let’s inspect the IMath interface.

$ javap -c IMath

Output:

  public interface IMath {
      public abstract int add(int, int);
      public abstract int sub(int, int);
      public abstract int mul(int, int);
      public abstract double div(int, int);
  }

This is the magic of gen-interface macro!

(gen-interface
  :name IMath
  :methods [[add [int int] int]
            [sub [int int] int]
            [mul [int int] int]
            [div [int int] double]])

And after that “magic”, this interface is imported like any other regular Java class, as follow.

(ns msgpackrpc-sample.client
  (:import IMath)
  (:import [org.msgpack.rpc.loop EventLoop])
  (:import [org.msgpack.rpc Client]))

Wow! I love it, baby! Don’t you?

The End

There are yet other many interesting things on MessagePack, MessagePack-RPC and, of course, Clojure compilation, so I hope you have get interested on them as well, and go learn and try further more, because I definitely will do.

Enjoy it!