HiveMQ Blog - Restful HTTP APIs with HiveMQ

HiveMQ Blog - Restful HTTP APIs with HiveMQ

author HiveMQ Team

Written by HiveMQ Team

Category: HiveMQ

Published: May 2, 2016


These days no enterprise software is complete without providing a way to integrate with other components in a software landscape. While MQTT is an awesome way to integrate backend systems, often this will be achieved by using HTTP-APIs, sometimes also called “Webservices”.

Of course, with HiveMQ the integration into other systems can be done purely in Java using the open source plugin system. HiveMQ also provides a RestService which allows you to create a custom HTTP-API or even a REST-API which can be consumed by other applications.

This blog post will give an introduction into the latter one and show you how you can add a HTTP-API to HiveMQ with only a few lines of code.

RESTService

Plugin developers can utilize HiveMQs RestService for creating accessible HTTP APIs directly within HiveMQ. If you are getting started with HiveMQ plugins and plugin services, take a look at the Plugin Developer Guide to get a jumpstart with HiveMQ plugins.

The RestService enables you to serve HTTP content directly from HiveMQ, namely:

  • Multiple HTTP endpoints
  • JAX-RS based HTTP/REST-APIs
  • Servlets and ServletFilters

But HiveMQ not only gives a plugin developer a simple way to add such APIs, it also makes all other plugin services available for the use within these APIs. In the following example code these services are used to get the subscriptions of all currently known MQTT clients. There is a lot more you can do with those services, see the Plugin Developer Guide for a list of those services.

HTTP Listeners

HiveMQ can serve different HTTP endpoints called listeners. These listeners can be added to HiveMQ either by configuring them in HiveMQ’s config file or by declaring them programatically.

You can serve different content on different listeners or on all available listeners. You can even serve some content on a specific listener and some other content on all listeners.

Example HiveMQ configuration file with HTTP listener on port 8080:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0"?>
<hivemq xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="hivemq-config.xsd">

    <listeners>
        <tcp-listener>
            <port>1883</port>
            <bind-address>0.0.0.0</bind-address>
        </tcp-listener>
    </listeners>
   
    <rest-service>
        <listeners>
            <http-listener>
                <name>default</name>
                <port>8080</port>
                <bind-address>0.0.0.0</bind-address>
            </http-listener>
        </listeners>
    </rest-service>

</hivemq>

Adding a listener on all interfaces with port 8888 programatically:

1
restService.addListener(new HttpListener("listener-name", "0.0.0.0", 8888));

By default HiveMQ will serve JAX-RS Applications or JAX-RS Resources with the root path / and serve Servlets under the root path /servlet.

HTTP-API

HiveMQ utilizes the JAX-RS API to provide a simple and well-known principle for implementing Webservices with Java. The easiest way to create such a service is to create a JAX-RS Resource and make it known to the RESTService.

Adding a Resource to the RESTService:

1
restService.addJaxRsResources(ExampleResource.class);

Example API implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Path("/example")
@Produces("application/json")
public class ExampleResource {

    private final BlockingSubscriptionStore subscriptionStore;

    @Inject
    public ExampleResource(final BlockingSubscriptionStore subscriptionStore) {
        this.subscriptionStore = subscriptionStore;
    }

    @GET
    @Path("/subscriptions")
    public Response getSubscriptions() {
        final Multimap<String, Topic> subscriptions = subscriptionStore.getSubscriptions();
        return Response.ok(subscriptions.asMap()).build();
    }
}

These few lines of code now create a HTTP API endpoint at http://broker-ip:8080/example/subscriptions/ which will return a JSON response containing a list of clients and their subscriptions on a HTTP GET request.

Servlets

HiveMQ’s RESTService can also serve Servlets directly from HiveMQ. An example Servlet which serves a HTML page with a list of all connected MQTT clients would look like this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class ExampleServlet extends HttpServlet {

    private final BlockingClientService clientService;

    @Inject
    public ExampleServlet(final BlockingClientService clientService) {
        this.clientService = clientService;
    }

    public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException {

        final Set<String> connectedClients = clientService.getConnectedClients();
        final PrintWriter writer = response.getWriter();

        writer.write("<html><body>");
        for (String clientId : connectedClients) {
            writer.write("* " + clientId + "");
        }
        writer.write("</body></html>");
    }
}

It can be added to RESTService by calling:

1
restService.addServlet(ExampleServlet.class, "/clients");

It can then be viewed in the browser at the URL

http://broker-ip:8080/servlet/clients

Async HTTP-API

HiveMQ’s plugin services also offer an async API for every service so it makes a lot of sense to also serve a HTTP-API in an async fashion. By using the async APIs you get better scalability and the general resource consumption of your plugin will also be lower than using the blocking APIs.

To show how the async APIs can be used together with JAX-RS the following is implementing the same example in an asychronous way.

JAX-RS example with async API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Path("/example")
@Produces("application/json")
public class ExampleResource {

    private final AsyncSubscriptionStore subscriptionStore;

    @Inject
    public ExampleResource(final AsyncSubscriptionStore subscriptionStore) {
        this.subscriptionStore = subscriptionStore;
    }

    @GET
    @Path("/subscriptions")
    public void getSubscriptions(@Suspended final AsyncResponse asyncResponse) {

        //since the information could be extremely large and will be collected from all cluster nodes
        //choose a very large timeout to handle worst-case delays.
        asyncResponse.setTimeout(5, TimeUnit.SECONDS);

        final ListenableFuture<Multimap<String, Topic>> future = subscriptionStore.getSubscriptions();

        Futures.addCallback(future, new FutureCallback<Multimap<String, Topic>>() {
            @Override
            public void onSuccess(final Multimap<String, Topic> result) {
                final Response response = Response.ok(result.asMap()).build();
                asyncResponse.resume(response);
            }

            @Override
            public void onFailure(final Throwable t) {
                asyncResponse.resume(t);
            }
        });
    }
}

Async Servlet

Of course HiveMQ supports the current Servlet 3.0 API which provides asynchronous execution of a request.

Servlet example with async API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class ExampleServlet extends HttpServlet {

    private final AsyncClientService clientService;

    @Inject
    public ExampleServlet(final AsyncClientService asyncClientService) {
        this.clientService = asyncClientService;
    }

    public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException {

        final AsyncContext asyncContext = request.startAsync(request, response);

        //since the information could be extremely large and will be collected from all cluster nodes
        //choose a very large timeout to handle worst-case delays.
        asyncContext.setTimeout(5000);

        final ListenableFuture<Set<String>> future = clientService.getConnectedClients();

        Futures.addCallback(future, new FutureCallback<Set<String>>() {

            @Override
            public void onSuccess(final Set<String> result) {
                try {
                    final PrintWriter writer = asyncContext.getResponse().getWriter();

                    writer.write("<html><body>");

                    for (String clientId : result) {
                        writer.write("* " + clientId + "");
                    }
                    writer.write("</body></html>");

                    asyncContext.complete();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(final Throwable t) {
                final HttpServletResponse httpResponse = (HttpServletResponse) asyncContext.getResponse();
                try {
                    httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                asyncContext.complete();
            }
        });
    }
}

Example Project

For more example code there is a rest-example-plugin which contains the blocking as well as the asynchronous examples and and shows how to add subscriptions via HTTP-API.

The example project is available on Github.

Conclusion

HTTP-APIs are a good and well-known way to integrate different software systems, especially if these systems do not support MQTT (yet). With the RESTService, HiveMQ provides an easy option for everyone who need their MQTT broker to communicate with other backend systems that don’t support MQTT in just a few lines of code while still providing scalability in a non-blocking fashion.

author HiveMQ Team

About HiveMQ Team

We love writing about MQTT, IoT protocols and architecture in general. Our experts are here to help, so reach out to us if we can help!

mail icon Contact HiveMQ
newer posts Your MQTT Applications: Are they resilient enough?
Load Balancing With Shared Subscriptions - MQTT Client older posts