AsyncAPI Java Tools 0.0.4 released


It started a as proof-of-concept, checking out AsyncAPI and learning something about it. Actually that topic came to me from two different angles. We where looking into solutions for RPC over messaging (namely AMQP 1.0) instead of HTTP/REST. The second angle was a discussion with a very good friend of mine of “how do you define today an API about messages from the server to the client”. Everyone is talking about Swagger and OpenAPI, but this seems to be all request/response for HTTP based APIs.

And then someone mentioned AsyncAPI, I had to look into this. And this is what I came up with so far.

AsyncAPI tools

ctron/asyncapi is a set of tools, written in Java, around AsyncAPI in general. Reading the specification file into a Java model, a code generator for client and server side code, a few base classes which the generate code makes use of.

The definition file parser still is not optimal. The idea is that the definition is based on JSON schema, but I couldn’t find any good support for JSON schema in Java at the moment. So right now it all home-brew and although it does get the job done, I don’t think it is pretty.

Code generation

The code generation is a bit more sophisticated. The idea of taking this out of the Maven plugin came pretty early. I guess it would be cool to have a gradle plugin at some time in the future and the Maven plugin really is just a small wrapper around the code generator. So that should easily be possible.

The code generation is backed by Eclipse JDT, which allows you to create and parse Java code in a DOM style way. I am constantly torn apart between liking and hating this at the same time. I did work with generic model-to-text tools in the past, and tools like Acceleo or Xpand are way easier to read than Java code generating Java code in that way. On the other side you would need a full blown Ecore model before and then wrap this again by a Maven plugin. I am not sure this is fun either. Also, using the DOM approach, it is quite simple to write extension modules for the code generation, which allow to actually process the generated Java DOM and extend it, without the requirement to write an awfully complex code generation template. So let’s see where this is going in the future.

What works?

Don’t expect productive code … yet ;-) Currently the code generator will create the defined types/schemas, the messages and their payload. Topics will be parsed into services, versions and actions and generate Client and Server interfaces. Also is there a client and server implementation using AMQP and Qpid JMS. This already allows you to communicate between client and server. Right now GSON will be used for creating JSON payload serialization. But I think that it should be pretty simple to swap this with e.g. Jackson.

What does not work?

The AsyncAPI specification actually defines a bit more than the tooling can currently handle. The server section is missing, various meta data like license and descriptions are not supported. But the most important thing which is currently missing IMHO is some implementation backed on MQTT. Not that I am a big fan on MQTT, I would prefer AMQP over that in this case. But I would like to see two different AsyncAPI partners communicate via MQTT/JSON. One of them being generated by this tool-set and the other one based on something completely different.

Also is JSON schema more powerful than what the parser can currently handle. I am not sure if JSON schema and Java is a very good fit, but if you want to go AsyncAPI, then you need to work with JSON schema. And this toolset needs to do a better job here.

AsyncAPI Maven Plugin

The AsyncAPI Maven plugin takes the code generator from the main tools project and wraps in into a Maven plugin. The idea is to simply drop in you AsyncAPI YAML file and let the Maven plugin generate the code for it. Of course this is Eclipse M2E aware, so that you can simply safe your YAML file and Eclipse will on the fly generate new code for you.

Examples

Interested in how this would look like in the form of source code? Well, here are some examples. Also be sure to check out my examples repository.

First we need to create a builder for either the server or the client implementation:

Builder<JmsClient> builder = JmsClient.newBuilder()
  .host("localhost")
  .profile(AmqpProfile.DEFAULT_PROFILE)
  .payloadFormat();

Next create an instance from it, the following is a client instance which will listen to some server-side event:

try (JmsClient client = builder.build();
     ListenerHandle listener =
        client.accounts()
          .eventUserSignup().subscribe(System.out::println)) {

  System.out.println("Waiting for messages…");
  Thread.sleep(Long.MAX_VALUE);

}

The of course we need a server to publish messages:

try (JmsServer client = builder.build()) {

  client.accounts().eventUserSignup()
    .publish(newUserMessage())
      .toCompletableFuture()
      .get();

}

Of course there is no need to actively wait for the message to be sent with get() if you don’t need to. But you can use the AsyncAPI in sync way if you like to ;-)

What is next?

There is a lot to do. Really, a lot! I would like to make an interop test with MQTT. There are several fields from the specification which are currently not supported. The server side JMS API isn’t really suitable for JEE style programming, especially when it comes to container managed JMS.

So I hope I will find some time to work on the MQTT backed implementation, because that would validate that two different AsyncAPI tools could work together. And I think this is what it is all about.

Before I forget…

The initial idea of why I looked into AsyncAPI was to get message based request/response. Well, that is something which AsyncAPI doesn’t really provide. But, to be fair, it would only require a few changes to add this to the specification.

See also