This tutorial teaches you how to create a simple generator template using a Java MQTT client. You'll use the AsyncAPI document and the template you develop to generate Java code. Additionally, you'll create template code with a reusable component to reuse the custom functionality you create and test your code using an MQTT client.
This section guides you through creating a flexible MQTT-supported template that will generate a Java client from the template and the AsyncAPI document referenced above. The following steps are similar to the Creating a template - Python but with a few differences and Java flair. This will help developers practice with AsyncAPI generator tool using statically typed language Java.
Prerequisites
To run it, ensure you have Java JDK 8 or higher, Gradle, and the AsyncAPI generator.
- Gradle - Get gradle at https://gradle.org/install/
- JDK - Get jdk at https://www.oracle.com/ca-en/java/technologies/downloads/
Overview of Java Template
In this section, you'll:
- Create a new directory to run Java code.
- Create the Java client.
- Test the Java Client
- Output Java template code.
- Create more channels
1. Create a new directory to run Java code
Create a new directory called java-mqtt-client-template at the root of your project. This is where all your Java templating work will go.
Once that is done, you should create some new sub-directories to begin building your Java client.
- Create a new subdirectory called
src - Change into
srcand create two new subdirectories:fixturesandmain/java. - Create a file named
asyncapi.ymlin your fixtures directory and paste theasyncapi.ymldocument mentioned here into it. - Create a new file named package.json in your java-mqtt-client-template directory. This file is used to define the dependencies for your template.
- Create a new file called build.gradle in your java-mqtt-client-template directory. This file is used to build your generated java code for your template.
- Create a new file named index.js in a
templatefolder from root directory. This file is used to define the logic for your template.
Now your directory should look like this:
1java-mqtt-client-template2├── src3| └── fixtures4| └── asyncapi.yml5│ └── main/java6├── template7| └── index.js8└── package.json9└── build.gradle
Note: The client.java code must be in your src/main/java directory, or else Gradle won't build your application.
Java - package.json file
Add the following code snippet to your package.json file:
1{2 "name": "java-mqtt-client-template",3 "version": "0.0.1",4 "description": "A template that generates a Java MQTT client using MQTT.",5 "generator": {6 "renderer": "react",7 "apiVersion": "v1",8 "generator": ">=1.10.0 <2.0.0",9 "supportedProtocols": ["mqtt"]10 },11 "dependencies": {12 "@asyncapi/generator-react-sdk": "^0.2.25"13 },14 "devDependencies": {15 "rimraf": "^5.0.0"16 }17}
Navigate to the java-mqtt-client-template directory and run the command npm install on your terminal to install the dependencies specified in package.json.
Java - index.js file
The index.js file is used to define the logic for your template. Inside the template folder, create an index.js file and add the code snippet below:
1//12import { File } from '@asyncapi/generator-react-sdk'3//24export default function ({ asyncapi }) {5//36 return <File name="Client.java">{asyncapi.info().title()}</File>7}
To see this in action, navigate to the java-mqtt-client-template directory. Then, run asyncapi generate fromTemplate src/fixtures/asyncapi.yml ./ --output src/main/java command in your terminal. You should get a sucess message as shown below and a Client.java file in src/main/java.
1Generation in progress. Keep calm and wait a bit... done2Check out your shiny new generated files at test/project.
2. Create the Java client
2a. Setting up Gradle
The first step in creating the Java client to send messages using the MQTT protocol is to ensure that your build.gradle file includes the correct dependencies. Add the code snippet below into your build.gradle file.
1plugins {2 id 'java'3 id 'application'4}56repositories {7 mavenCentral()8}910dependencies {11 implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'12}1314application{15 mainClass = project.hasProperty('mainClass') ? project.mainClass : 'Client' // Default to 'Client' if no property is passed16}
Here's what is contained in the code snippet above:
- plugins - This section defines the plugins applied to the Gradle project.
- id 'java' - This applies the Java plugin, which allows you to compile and run Java code.
- id 'application' - This plugin is used to support building and running applications. It helps with creating an executable JAR.
- repositories - This section tells you app to fetch dependencies from Maven Central to retrieve the MQTT client library.
- dependencies - This specifies that the project depends on the Eclipse Paho MQTT client version 1.2.5, which is needed to compile and run.
- application - This section defines how the application should be executed. mainClass specifies the main class to be executed in a Java application. It can be specified via the command line else it defaults to the Client.java file.
Navigate to the java-mqtt-client-template directory. Run the command gradle build in your terminal to build your Java application. Note: Every time you update the build.gradle file, you must recompile it to get the new changes.
2b. Beefing up Client.java
Here is the sample code to pasted into the Client.java file you generated above running the asyncapi generate fromTemplate src/fixtures/asyncapi.yml ./ --output src/main/java command.
1import org.eclipse.paho.client.mqttv3.*;23public class Client {4 private static final String BROKER_URL = "tcp://test.mosquitto.org:1883";5 private static final String TOPIC = "temperature/changed";67 private MqttClient client;89 public Client() {10 try {11 // Generate a unique client ID12 String clientId = MqttClient.generateClientId();1314 // Create and connect the MQTT client15 client = new MqttClient(BROKER_URL, clientId);16 MqttConnectOptions options = new MqttConnectOptions();17 options.setCleanSession(true);1819 client.connect(options);20 System.out.println("Connected to MQTT broker: " + BROKER_URL);21 } catch (MqttException e) {22 e.printStackTrace();23 }24 }25 public void sendTemperatureChange(String id) {26 try {27 // Publish the message with the temperature change28 MqttMessage message = new MqttMessage(id.getBytes());29 client.publish(TOPIC, message);30 } catch (MqttException e) {31 e.printStackTrace();32 }33 }34}
3. Test the Java Client
Create a src/main/java/TestClient.java file in your project and add the code snippet below.
Your directory should now look like this:
1java-mqtt-client-template2├── src3| └── fixtures4| └── asyncapi.yml5│ └── main/java6| └── Client.java7| └── TestClient.java8├── template9| └── index.js10└── package.json11└── build.gradle
1import java.util.Random;2import java.util.concurrent.TimeUnit;34public class TestClient {5 public static void main(String[] args) {6 Client client = new Client();7 Random random = new Random();89 int idLength = 8;10 int minValue = (int) Math.pow(10, idLength - 1); // Minimum 8-digit number (e.g., 10000000)11 int maxValue = (int) Math.pow(10, idLength) - 1; // Maximum 8-digit number (e.g., 99999999)12 System.out.println("Validating generated generated Client.java");13 System.out.println("Running tests in TestClient.java");14 System.out.println("Sending temperature changes to the broker...");15 System.err.println("\n");16 while (true) {17 int randomId = random.nextInt(maxValue - minValue + 1) + minValue;18 client.sendTemperatureChange(String.valueOf(randomId));19 System.out.println("New temperature detected " + randomId + " sent to temperature/changed");2021 try {22 TimeUnit.SECONDS.sleep(1); // Sleep for 1 second23 } catch (InterruptedException e) {24 e.printStackTrace();25 }26 }27 }28}
Run the command gradle run -PmainClass=TestClient to run your Java program with Gradle on your terminal. You should see output similar to the snippet below logged on your terminal:
1New temperature detected 64250266 sent to temperature/changed2New temperature detected 36947728 sent to temperature/changed3New temperature detected 72955029 sent to temperature/changed
4. Output Java template code.
Open index.js and copy the content below so your file looks like the code snippet below:
1//12import { File } from '@asyncapi/generator-react-sdk'3//24export default function ({ asyncapi }) {5//36 return <File name="Client.java">7 {`89import org.eclipse.paho.client.mqttv3.*;10public class Client {11 private static final String BROKER_URL = "tcp://test.mosquitto.org:1883";12 private static final String TOPIC = "temperature/changed";1314 private MqttClient client;1516 public Client() {17 try {18 // Generate a unique client ID19 String clientId = MqttClient.generateClientId();2021 // Create and connect the MQTT client22 client = new MqttClient(BROKER_URL, clientId);23 MqttConnectOptions options = new MqttConnectOptions();24 options.setCleanSession(true);2526 client.connect(options);27 System.out.println("Connected to MQTT broker: " + BROKER_URL);28 } catch (MqttException e) {29 e.printStackTrace();30 }31 }32 public void sendTemperatureChange(String id) {33 try {34 // Publish the message with the temperature change35 MqttMessage message = new MqttMessage(id.getBytes());36 client.publish(TOPIC, message);37 } catch (MqttException e) {38 e.printStackTrace();39 }40 }41}`42 }</File>43}
4a. Write the script to run the test code
In package.json define a script property that you invoke by calling npm run <your_script>. After adding these scripts in package.json, it will look like the following code snippet:
1 {2 "name": "java-mqtt-client-template",3 "version": "0.0.1",4 "description": "A template that generates a Java MQTT client using MQTT.",5 "generator": {6 "renderer": "react",7 "apiVersion": "v1",8 "generator": ">=1.10.0 <2.0.0",9 "supportedProtocols": ["mqtt"],10 "parameters": {11 "server": {12 "description": "The server you want to use in the code.",13 "required": true14 }15 }16 },17 "scripts": {18 "test:clean": "rimraf src/main/java/Client.java",19 "test:generate": "asyncapi generate fromTemplate src/fixtures/asyncapi.yml ./ --output src/main/java --force-write --param server=dev",20 "test:start": "gradle run -PmainClass=TestClient",21 "test": "npm run test:clean && npm run test:generate && npm run test:start"22 },23 "dependencies": {24 "@asyncapi/generator-react-sdk": "^0.2.25"25 },26 "devDependencies": {27 "rimraf": "^5.0.0"28 }29 }
Run npm test to see if everything is working.
5. Create more channels
5a. Creating more reusable components
Similar to the previous TopicFunction function we will create a function to make reusable components regardless of the number of channels in the asyncAPI document.
Create a components directory at the root of your project and create a file named TopicFunction.js and add the code snippet below:
1/*2 * This component returns a block of functions that users can use to send messages to specific topics.3 * As input it requires a list of Channel models from the parsed AsyncAPI document.4 */5export function TopicFunction({ channels }) {6 const topicsDetails = getTopics(channels);7 let functions = '';89 topicsDetails.forEach((t) => {10 functions += `11 public void send${t.name}(String id) {12 String topic = "${t.topic}";13 try {14 MqttMessage message = new MqttMessage(id.getBytes());15 client.publish(topic, message);16 System.out.println("${t.name} change sent: " + id);17 } catch (MqttException e) {18 e.printStackTrace();19 }20 }\n`;21 });2223 return functions;24}2526 /*27 * This function returns a list of objects, one for each channel, each containing two properties: `name` and `topic`.28 * name - holds information about the `operationId` definedin the AsyncAPI document29 * topic - holds information about the topic's address.30 *31 * It requires as input, a list of Channel models from the parsed AsyncAPI document.32 */33 function getTopics(channels) {34 const channelsCanSendTo = channels35 let topicsDetails = []3637 channelsCanSendTo.forEach((ch) => {38 const topic = {}39 const operationId = ch.operations().filterByReceive()[0].id()40 topic.name = operationId.charAt(0).toUpperCase() + operationId.slice(1)41 topic.topic = ch.address()4243 topicsDetails.push(topic)44 })4546 return topicsDetails47 }
Import the TopicFunction component in your template code in index.js and add the template code to generate the functions for the topics which the Temperature Service application is subscribed to. In your case, the final version of your template code should look like this:
1import { File, Text } from '@asyncapi/generator-react-sdk';2import { TopicFunction } from '../components/TopicFunction'34export default function ({ asyncapi, params }) {5 let channels = asyncapi.channels().filterByReceive(); // Get all the channels that receive messages67 // Generate Java code for each topic dynamically using TopicFunction8 const topicMethods = TopicFunction({ channels }); // This will return Java methods as text910 return (11 <File name="Client.java">12 {1314`import org.eclipse.paho.client.mqttv3.*;1516public class Client {17 private static final String BROKER_URL = "${asyncapi.servers().get(params.server).url()}";18 private static final String TOPIC = "temperature/changed";1920 private MqttClient client;2122 public Client() {23 try {24 // Generate a unique client ID25 String clientId = MqttClient.generateClientId();2627 // Create and connect the MQTT client28 client = new MqttClient(BROKER_URL, clientId);29 MqttConnectOptions options = new MqttConnectOptions();30 options.setCleanSession(true);3132 client.connect(options);33 System.out.println("Connected to MQTT broker: " + BROKER_URL);34 } catch (MqttException e) {35 e.printStackTrace();36 }37 }3839 ${topicMethods}4041 public static void main(String[] args) {42 Client serviceClient = new Client();4344 // Simulate sending a temperature change45 //serviceClient.sendTemperatureDrop("Sensor-1: 25°C");46 }47}`48 }49 </File>50 );51}
Now your directory should look like this:
1java-mqtt-client-template2├── components3| └── TopicFunction.js4├── src5| └── fixtures6| └── asyncapi.yml7│ └── main/java8| └── Client.java9| └── TestClient.java10├── template11| └── index.js12└── package.json13└── build.gradle
5b. Update AsyncAPI document with more channels
Add the following AsyncAPI document to have more channels:
1asyncapi: 2.6.023info:4 title: Temperature Service5 version: 1.0.06 description: This service is in charge of processing all the events related to temperature.78servers:9 dev:10 url: tcp://test.mosquitto.org:188311 protocol: mqtt1213channels:14 temperature/dropped:15 description: Notifies the user when the temperature drops past a certain point.16 publish:17 operationId: temperatureDrop18 message:19 description: Message that is being sent when the temperature drops past a certain point.20 payload:21 type: object22 additionalProperties: false23 properties:24 temperatureId:25 type: string2627 temperature/risen:28 description: Notifies the user when the temperature rises past a certain point.29 publish:30 operationId: temperatureRise31 message:32 description: Message that is being sent when the temperature rises past a certain point.33 payload:34 type: object35 additionalProperties: false36 properties:37 temperatureId:38 type: string3940components:41 schemas:42 temperatureId:43 type: object44 additionalProperties: false45 properties:46 temperatureId:47 type: string
5c. Update TestClient.java
We must now update the TestClient.java file to test the different channels in the AsyncAPI document above. The tests will be similar to the previous ones you performed earlier. Paste the following code snippet into your TestClient.java file:
1import java.util.Random;2import java.util.concurrent.TimeUnit;34public class TestClient {5 public static void main(String[] args) {6 Client client = new Client();7 Random random = new Random();89 int idLength = 8;10 int minValue = (int) Math.pow(10, idLength - 1); // Minimum 8-digit number (e.g., 10000000)11 int maxValue = (int) Math.pow(10, idLength) - 1; // Maximum 8-digit number (e.g., 99999999)12 System.out.println("Validating generated generated Client.java");13 System.out.println("Running tests in TestClient.java");14 System.out.println("Sending temperature changes to the broker...");15 System.err.println("\n");16 while (true) {17 int randomId = random.nextInt(maxValue - minValue + 1) + minValue;18 client.sendTemperatureDrop(String.valueOf(randomId));19 System.out.println("Temperature drop detected " + randomId + " sent to temperature/dropped");2021 client.sendTemperatureRise(String.valueOf(randomId));22 System.out.println("Temperature risen detected " + randomId + " sent to temperature/risen");2324 try {25 TimeUnit.SECONDS.sleep(1); // Sleep for 1 second26 } catch (InterruptedException e) {27 e.printStackTrace();28 }29 }30 }31}
Run npm test to validate that everything works as expected. You should see logs similar to the snippet below in your terminal:
1Connected to MQTT broker: tcp://test.mosquitto.org:188323Validating generated generated Client.java4Running tests in TestClient.java5Sending temperature changes to the broker...6TemperatureDrop change sent: 432899007Temperature drop detected 43289900 sent to temperature/dropped8TemperatureRise change sent: 432899009Temperature risen detected 43289900 sent to temperature/risen
Where to go from here?
Great job completing this tutorial! You have learnt how to use an AsyncAPI file to create a Java MQTT template and used it with the Paho-MQTT library in Java to connect to an MQTT broker and publish messages.😃
If you want to tinker with a completed template and see what it would look like in production, check out the Java-MQTT-client-template. You can also check out the accompanying article about creating MQTT client code.
You can also check out the MQTT beginners guide tutorial to learn more about asynchronous messaging using MQTT.