Hello,
I'm developing a REST API interface that, in some parts, has to interact with MQTT through Paho client library. By design, Paho client can only one callback for every message received:
mqttClient = new MqttClient(MQTT_ADDRESS, MQTT_CLIENT_ID);
mqttClient.setCallback(new MqttCallbackImpl());
...
private static class MqttCallbackImpl implements MqttCallback {
@Override
public void connectionLost(Throwable cause) { }
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
switch(topic) {
// Endless list of cases...
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) { }
}
I am struggling in figuring out the "right" way to process the received message and react accordingly -- how would I write my callback by avoiding a gigantic switch() on some parts of the payload or the topic?
I'm not familiar with MQTT, but I would guess that you should have a listener per queue that you're listening on.
Looking at the docs, I'm seeing that you can only listen on topics ... not great, but let's work with that.
If you're working with Java8, you can setup a Hashmap like this:
HashMap<String, Callable<String>> map = new HashMap<>();
Now you should be able to put methods for different topics in there as you need them.
map.put("topic1", () -> {return doSomething1();}
map.put("topic2", () -> {return doSomething2();}
Inside your messageArrived you would simply execute whatever is returned from map.get(topic)
Your map can be sitting somewhere as a static variable, each module in the application can then map.put their own topics, so if you remove the module, those topics simply won't be in the map anymore and new modules can be added without modifying your listener.
If you're not using Java8 or Java9, you can get creative with Reflection.
I did something similar sometime ago, and I think that the implementation was nice.
Let's say that you want to have a thin and generic messageArrived method, something like this:
@Override public void messageArrived(String topic, MqttMessage message) throws Exception { messageHandler.getMessageHandler(topic).handle(message); }This is nice right? So how we can achieve something like that?
First let's define an annotation and also an interface
@Retention(RUNTIME) @Target({TYPE}) public @interface MessageHandler { String value() } public interface Handler { void handle(MqttMessage message); }and now let's use this annotation in a class
@MessageHandler("myTopic") public class MyTopicHandler implements Handler { @override public void handle(MqttMessage message) { // do your somthing with the message } }The last part it's to write the MessageHandler class, in this class you just need to store in a map all your handlers instances, the key of the map will be the topic name and the value and instance of the class. Basically you getMessageHandler method will look something like:
public Handler getMessageHandler(String topic) { if (handlerMap == null) { loadClassesFromClassPath(); // This method should load from classpath all the classes with the annotation MessageHandler and build the handlerMap } return handlerMap.get(topic); }The good thing with this solution it's that if you need to add a new handler for a new message, you just need to write the new class, add the annotation to the class and that's all. The rest of the code remains the same because it's generic and it's using reflection to load and create instances.
Hope this helps you a bit.