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
src
and create two new subdirectories:fixtures
andmain/java
. - Create a file named
asyncapi.yml
in your fixtures directory and paste theasyncapi.yml
document 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
template
folder from root directory. This file is used to define the logic for your template.
Now your directory should look like this:
1java-mqtt-client-template
2├── src
3| └── fixtures
4| └── asyncapi.yml
5│ └── main/java
6├── template
7| └── index.js
8└── package.json
9└── 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//1
2import { File } from '@asyncapi/generator-react-sdk'
3//2
4export default function ({ asyncapi }) {
5//3
6 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... done
2Check 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}
5
6repositories {
7 mavenCentral()
8}
9
10dependencies {
11 implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
12}
13
14application{
15 mainClass = project.hasProperty('mainClass') ? project.mainClass : 'Client' // Default to 'Client' if no property is passed
16}
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.*;
2
3public class Client {
4 private static final String BROKER_URL = "tcp://test.mosquitto.org:1883";
5 private static final String TOPIC = "temperature/changed";
6
7 private MqttClient client;
8
9 public Client() {
10 try {
11 // Generate a unique client ID
12 String clientId = MqttClient.generateClientId();
13
14 // Create and connect the MQTT client
15 client = new MqttClient(BROKER_URL, clientId);
16 MqttConnectOptions options = new MqttConnectOptions();
17 options.setCleanSession(true);
18
19 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 change
28 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-template
2├── src
3| └── fixtures
4| └── asyncapi.yml
5│ └── main/java
6| └── Client.java
7| └── TestClient.java
8├── template
9| └── index.js
10└── package.json
11└── build.gradle
1import java.util.Random;
2import java.util.concurrent.TimeUnit;
3
4public class TestClient {
5 public static void main(String[] args) {
6 Client client = new Client();
7 Random random = new Random();
8
9 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");
20
21 try {
22 TimeUnit.SECONDS.sleep(1); // Sleep for 1 second
23 } 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/changed
2New temperature detected 36947728 sent to temperature/changed
3New 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//1
2import { File } from '@asyncapi/generator-react-sdk'
3//2
4export default function ({ asyncapi }) {
5//3
6 return <File name="Client.java">
7 {`
8
9import 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";
13
14 private MqttClient client;
15
16 public Client() {
17 try {
18 // Generate a unique client ID
19 String clientId = MqttClient.generateClientId();
20
21 // Create and connect the MQTT client
22 client = new MqttClient(BROKER_URL, clientId);
23 MqttConnectOptions options = new MqttConnectOptions();
24 options.setCleanSession(true);
25
26 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 change
35 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": true
14 }
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 = '';
8
9 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 });
22
23 return functions;
24}
25
26 /*
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 document
29 * 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 = channels
35 let topicsDetails = []
36
37 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()
42
43 topicsDetails.push(topic)
44 })
45
46 return topicsDetails
47 }
48
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'
3
4export default function ({ asyncapi, params }) {
5 let channels = asyncapi.channels().filterByReceive(); // Get all the channels that receive messages
6
7 // Generate Java code for each topic dynamically using TopicFunction
8 const topicMethods = TopicFunction({ channels }); // This will return Java methods as text
9
10 return (
11 <File name="Client.java">
12 {
13
14`import org.eclipse.paho.client.mqttv3.*;
15
16public class Client {
17 private static final String BROKER_URL = "${asyncapi.servers().get(params.server).url()}";
18 private static final String TOPIC = "temperature/changed";
19
20 private MqttClient client;
21
22 public Client() {
23 try {
24 // Generate a unique client ID
25 String clientId = MqttClient.generateClientId();
26
27 // Create and connect the MQTT client
28 client = new MqttClient(BROKER_URL, clientId);
29 MqttConnectOptions options = new MqttConnectOptions();
30 options.setCleanSession(true);
31
32 client.connect(options);
33 System.out.println("Connected to MQTT broker: " + BROKER_URL);
34 } catch (MqttException e) {
35 e.printStackTrace();
36 }
37 }
38
39 ${topicMethods}
40
41 public static void main(String[] args) {
42 Client serviceClient = new Client();
43
44 // Simulate sending a temperature change
45 //serviceClient.sendTemperatureDrop("Sensor-1: 25°C");
46 }
47}`
48 }
49 </File>
50 );
51}
Now your directory should look like this:
1java-mqtt-client-template
2├── components
3| └── TopicFunction.js
4├── src
5| └── fixtures
6| └── asyncapi.yml
7│ └── main/java
8| └── Client.java
9| └── TestClient.java
10├── template
11| └── index.js
12└── package.json
13└── build.gradle
5b. Update AsyncAPI document with more channels
Add the following AsyncAPI document to have more channels:
1asyncapi: 2.6.0
2
3info:
4 title: Temperature Service
5 version: 1.0.0
6 description: This service is in charge of processing all the events related to temperature.
7
8servers:
9 dev:
10 url: tcp://test.mosquitto.org:1883
11 protocol: mqtt
12
13channels:
14 temperature/dropped:
15 description: Notifies the user when the temperature drops past a certain point.
16 publish:
17 operationId: temperatureDrop
18 message:
19 description: Message that is being sent when the temperature drops past a certain point.
20 payload:
21 type: object
22 additionalProperties: false
23 properties:
24 temperatureId:
25 type: string
26
27 temperature/risen:
28 description: Notifies the user when the temperature rises past a certain point.
29 publish:
30 operationId: temperatureRise
31 message:
32 description: Message that is being sent when the temperature rises past a certain point.
33 payload:
34 type: object
35 additionalProperties: false
36 properties:
37 temperatureId:
38 type: string
39
40components:
41 schemas:
42 temperatureId:
43 type: object
44 additionalProperties: false
45 properties:
46 temperatureId:
47 type: string
48
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;
3
4public class TestClient {
5 public static void main(String[] args) {
6 Client client = new Client();
7 Random random = new Random();
8
9 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");
20
21 client.sendTemperatureRise(String.valueOf(randomId));
22 System.out.println("Temperature risen detected " + randomId + " sent to temperature/risen");
23
24 try {
25 TimeUnit.SECONDS.sleep(1); // Sleep for 1 second
26 } 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:1883
2
3Validating generated generated Client.java
4Running tests in TestClient.java
5Sending temperature changes to the broker...
6TemperatureDrop change sent: 43289900
7Temperature drop detected 43289900 sent to temperature/dropped
8TemperatureRise change sent: 43289900
9Temperature 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.