UNPKG

@asyncapi/generator

Version:

The AsyncAPI generator. It can generate documentation, code, anything!

558 lines (456 loc) 20.7 kB
--- title: "Creating a template - Java" weight: 190 --- 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](https://www.asyncapi.com/docs/tools/generator/generator-template) 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: 1. Create a new directory to run Java code. 2. Create the Java client. 3. Test the Java Client 4. Output Java template code. 5. 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. 1. Create a new subdirectory called `src` 2. Change into `src` and create two new subdirectories: `fixtures` and `main/java`. 3. Create a file named `asyncapi.yml` in your fixtures directory and paste the `asyncapi.yml` document mentioned [here](https://www.asyncapi.com/docs/tools/generator/generator-template#background-context) into it. 4. 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. 5. 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. 6. 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: ``` java-mqtt-client-template ├── src | └── fixtures | └── asyncapi.yml └── main/java ├── template | └── index.js └── package.json └── 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: ```json { "name": "java-mqtt-client-template", "version": "0.0.1", "description": "A template that generates a Java MQTT client using MQTT.", "generator": { "apiVersion": "v1", "generator": ">=1.10.0 <2.0.0", "supportedProtocols": ["mqtt"] }, "dependencies": { "@asyncapi/generator-react-sdk": "^0.2.25" }, "devDependencies": { "rimraf": "^5.0.0" } } ``` 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: ```js //1 import { File } from '@asyncapi/generator-react-sdk' //2 export default function ({ asyncapi }) { //3 return <File name="Client.java">{asyncapi.info().title()}</File> } ``` 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`. ``` cmd Generation in progress. Keep calm and wait a bit... done Check 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. ```groovy plugins { id 'java' id 'application' } repositories { mavenCentral() } dependencies { implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5' } application{ mainClass = project.hasProperty('mainClass') ? project.mainClass : 'Client' // Default to 'Client' if no property is passed } ``` 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. ```java import org.eclipse.paho.client.mqttv3.*; public class Client { private static final String BROKER_URL = "tcp://test.mosquitto.org:1883"; private static final String TOPIC = "temperature/changed"; private MqttClient client; public Client() { try { // Generate a unique client ID String clientId = MqttClient.generateClientId(); // Create and connect the MQTT client client = new MqttClient(BROKER_URL, clientId); MqttConnectOptions options = new MqttConnectOptions(); options.setCleanSession(true); client.connect(options); System.out.println("Connected to MQTT broker: " + BROKER_URL); } catch (MqttException e) { e.printStackTrace(); } } public void sendTemperatureChange(String id) { try { // Publish the message with the temperature change MqttMessage message = new MqttMessage(id.getBytes()); client.publish(TOPIC, message); } catch (MqttException e) { e.printStackTrace(); } } } ``` ### 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: ``` java-mqtt-client-template ├── src | └── fixtures | └── asyncapi.yml └── main/java | └── Client.java | └── TestClient.java ├── template | └── index.js └── package.json └── build.gradle ``` ```java import java.util.Random; import java.util.concurrent.TimeUnit; public class TestClient { public static void main(String[] args) { Client client = new Client(); Random random = new Random(); int idLength = 8; int minValue = (int) Math.pow(10, idLength - 1); // Minimum 8-digit number (e.g., 10000000) int maxValue = (int) Math.pow(10, idLength) - 1; // Maximum 8-digit number (e.g., 99999999) System.out.println("Validating generated generated Client.java"); System.out.println("Running tests in TestClient.java"); System.out.println("Sending temperature changes to the broker..."); System.err.println("\n"); while (true) { int randomId = random.nextInt(maxValue - minValue + 1) + minValue; client.sendTemperatureChange(String.valueOf(randomId)); System.out.println("New temperature detected " + randomId + " sent to temperature/changed"); try { TimeUnit.SECONDS.sleep(1); // Sleep for 1 second } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 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: ``` cmd New temperature detected 64250266 sent to temperature/changed New temperature detected 36947728 sent to temperature/changed New 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: ```js //1 import { File } from '@asyncapi/generator-react-sdk' //2 export default function ({ asyncapi }) { //3 return <File name="Client.java"> {` import org.eclipse.paho.client.mqttv3.*; public class Client { private static final String BROKER_URL = "tcp://test.mosquitto.org:1883"; private static final String TOPIC = "temperature/changed"; private MqttClient client; public Client() { try { // Generate a unique client ID String clientId = MqttClient.generateClientId(); // Create and connect the MQTT client client = new MqttClient(BROKER_URL, clientId); MqttConnectOptions options = new MqttConnectOptions(); options.setCleanSession(true); client.connect(options); System.out.println("Connected to MQTT broker: " + BROKER_URL); } catch (MqttException e) { e.printStackTrace(); } } public void sendTemperatureChange(String id) { try { // Publish the message with the temperature change MqttMessage message = new MqttMessage(id.getBytes()); client.publish(TOPIC, message); } catch (MqttException e) { e.printStackTrace(); } } }` }</File> } ``` #### 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: ``` json { "name": "java-mqtt-client-template", "version": "0.0.1", "description": "A template that generates a Java MQTT client using MQTT.", "generator": { "apiVersion": "v1", "generator": ">=1.10.0 <2.0.0", "supportedProtocols": ["mqtt"], "parameters": { "server": { "description": "The server you want to use in the code.", "required": true } } }, "scripts": { "test:clean": "rimraf src/main/java/Client.java", "test:generate": "asyncapi generate fromTemplate src/fixtures/asyncapi.yml ./ --output src/main/java --force-write --param server=dev", "test:start": "gradle run -PmainClass=TestClient", "test": "npm run test:clean && npm run test:generate && npm run test:start" }, "dependencies": { "@asyncapi/generator-react-sdk": "^0.2.25" }, "devDependencies": { "rimraf": "^5.0.0" } } ``` 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: ```js /* * This component returns a block of functions that users can use to send messages to specific topics. * As input it requires a list of Channel models from the parsed AsyncAPI document. */ export function TopicFunction({ channels }) { const topicsDetails = getTopics(channels); let functions = ''; topicsDetails.forEach((t) => { functions += ` public void send${t.name}(String id) { String topic = "${t.topic}"; try { MqttMessage message = new MqttMessage(id.getBytes()); client.publish(topic, message); System.out.println("${t.name} change sent: " + id); } catch (MqttException e) { e.printStackTrace(); } }\n`; }); return functions; } /* * This function returns a list of objects, one for each channel, each containing two properties: `name` and `topic`. * name - holds information about the `operationId` definedin the AsyncAPI document * topic - holds information about the topic's address. * * It requires as input, a list of Channel models from the parsed AsyncAPI document. */ function getTopics(channels) { const channelsCanSendTo = channels let topicsDetails = [] channelsCanSendTo.forEach((ch) => { const topic = {} const operationId = ch.operations().filterByReceive()[0].id() topic.name = operationId.charAt(0).toUpperCase() + operationId.slice(1) topic.topic = ch.address() topicsDetails.push(topic) }) return topicsDetails } ``` 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: ```js import { File, Text } from '@asyncapi/generator-react-sdk'; import { TopicFunction } from '../components/TopicFunction' export default function ({ asyncapi, params }) { let channels = asyncapi.channels().filterByReceive(); // Get all the channels that receive messages // Generate Java code for each topic dynamically using TopicFunction const topicMethods = TopicFunction({ channels }); // This will return Java methods as text return ( <File name="Client.java"> { `import org.eclipse.paho.client.mqttv3.*; public class Client { private static final String BROKER_URL = "${asyncapi.servers().get(params.server).url()}"; private static final String TOPIC = "temperature/changed"; private MqttClient client; public Client() { try { // Generate a unique client ID String clientId = MqttClient.generateClientId(); // Create and connect the MQTT client client = new MqttClient(BROKER_URL, clientId); MqttConnectOptions options = new MqttConnectOptions(); options.setCleanSession(true); client.connect(options); System.out.println("Connected to MQTT broker: " + BROKER_URL); } catch (MqttException e) { e.printStackTrace(); } } ${topicMethods} public static void main(String[] args) { Client serviceClient = new Client(); // Simulate sending a temperature change //serviceClient.sendTemperatureDrop("Sensor-1: 25°C"); } }` } </File> ); } ``` Now your directory should look like this: ``` java-mqtt-client-template ├── components | └── TopicFunction.js ├── src | └── fixtures | └── asyncapi.yml └── main/java | └── Client.java | └── TestClient.java ├── template | └── index.js └── package.json └── build.gradle ``` #### 5b. Update AsyncAPI document with more channels Add the following AsyncAPI document to have more channels: ```yaml asyncapi: 2.6.0 info: title: Temperature Service version: 1.0.0 description: This service is in charge of processing all the events related to temperature. servers: dev: url: tcp://test.mosquitto.org:1883 protocol: mqtt channels: temperature/dropped: description: Notifies the user when the temperature drops past a certain point. publish: operationId: temperatureDrop message: description: Message that is being sent when the temperature drops past a certain point. payload: type: object additionalProperties: false properties: temperatureId: type: string temperature/risen: description: Notifies the user when the temperature rises past a certain point. publish: operationId: temperatureRise message: description: Message that is being sent when the temperature rises past a certain point. payload: type: object additionalProperties: false properties: temperatureId: type: string components: schemas: temperatureId: type: object additionalProperties: false properties: temperatureId: 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: ```java import java.util.Random; import java.util.concurrent.TimeUnit; public class TestClient { public static void main(String[] args) { Client client = new Client(); Random random = new Random(); int idLength = 8; int minValue = (int) Math.pow(10, idLength - 1); // Minimum 8-digit number (e.g., 10000000) int maxValue = (int) Math.pow(10, idLength) - 1; // Maximum 8-digit number (e.g., 99999999) System.out.println("Validating generated generated Client.java"); System.out.println("Running tests in TestClient.java"); System.out.println("Sending temperature changes to the broker..."); System.err.println("\n"); while (true) { int randomId = random.nextInt(maxValue - minValue + 1) + minValue; client.sendTemperatureDrop(String.valueOf(randomId)); System.out.println("Temperature drop detected " + randomId + " sent to temperature/dropped"); client.sendTemperatureRise(String.valueOf(randomId)); System.out.println("Temperature risen detected " + randomId + " sent to temperature/risen"); try { TimeUnit.SECONDS.sleep(1); // Sleep for 1 second } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` Run `npm test` to validate that everything works as expected. You should see logs similar to the snippet below in your terminal: ```cmd Connected to MQTT broker: tcp://test.mosquitto.org:1883 Validating generated generated Client.java Running tests in TestClient.java Sending temperature changes to the broker... TemperatureDrop change sent: 43289900 Temperature drop detected 43289900 sent to temperature/dropped TemperatureRise change sent: 43289900 Temperature 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](https://github.com/ssala034/Java-MQTT-client-template). You can also check out the accompanying [article about creating MQTT client code](https://www.brainfart.dev/blog/asyncapi-codegen-python). You can also check out the [MQTT beginners guide](https://medium.com/python-point/mqtt-basics-with-python-examples-7c758e605d4) tutorial to learn more about asynchronous messaging using MQTT.