Last Updated on 03/05/2021 by Patryk Bandurski
In one of my previous posts, I described Java Component and entry point resolvers as a way to invoke Java Code in Mule 3.x. In this article, I will focus on a completely new approach in Mule 4. Mule presents a brand new Java Module capable of creating new instances, invoking methods on those instances, and invoking static methods. Although you can invoke Java using DataWeave 2.0 and Groovy scripting you are losing additional metadata (DataSense). So let’s walk through some sample applications.
Java module overview
Mule 4 has replaced mule expression language with DataWeave 2.x. As DataWeave 1.0 had not allowed easily to call Java classes new Java Module has been introduced. Java Module has the following message processors:
- Invoke – Invokes a method on the provided instance
- Invoke static – Invokes a static method on the provided class
- New – Creates a new instance
- Validate type – Verifies if the provided instance is an instance of the specified class
In order to call the method, we need to provide two pieces of information:
- method signature,
- arguments.
Both elements are fairly simple. By method signature, I mean method name and parameters’ types provided like below
generate() generate(String) generate(String, int)
The first line instructs to invoke the generate method that does not accept any parameters. The second one expects only one String. The last one expects two parameters String and integer.
When the method accepts parameters we also need to provide them. We do it using DataWeave expression like
#[
{
paramName: paramValue
}
]
Invoke own static method
I would like to expose a service that returns gender randomly. I have already prepared a class called GenderGenerator that is in com.ambassadorpatryk.generators package. This class has only one static method with the following signature
public static String generate()
Here is the sample flow that implements it. In order to use Java Module, you need to add it as it is not included by default. Drag Invoke static message processor on canvas and configure like below:
- Class: fully qualified class name (package + class name)
- Method: method signature, empty parentheses are required when the parameters’ list is empty
- Args: leave blank as methods do not accept any parameter
It is time to test how smooth it works :).
Passing parameters
I have prepared another flow. This time it returns a random number. However, we may instruct a range of the generated number. In NumberGenerator class I have two methods with the following signatures:
public static double generateVal() public static double generateVal(int min, int max)
If we send query parameters min and max, mule should invoke the second method; otherwise, the first one. So let’s start with the method without parameters.
- Display Name: Generate Number
- Class: com.ambassadorpatryk.generators.NumberGenerator
- Method: generateVal()
- Args: leave blank
This one is fairly simple and similar to what we have already done. So now we invoke static method that requires two parameters
<java:invoke-static
doc:name="Generate Restricted Number"
class="com.ambassadorpatryk.generators.NumberGenerator"
method="generateVal(int, int)">
<java:args><![CDATA[#[{
arg0: attributes.queryParams.min as Number,
arg1: attributes.queryParams.max as Number
}]]]></java:args>
</java:invoke-static>
As you can see in lines 4 and 5, we defined two parameters. As you may notice, we pass parameters as an object. Each property name starts with the prefix arg and then the ordinal number. Imagine now that I would like to name properties more meaningfully. I created a method with the following signature:
public static double generateVal(int min, int max)
We can name parameters exactly like in the method signature. So the example from down below should work fine as well.
min: attributes.queryParams.min as Number, max: attributes.queryParams.max as Number
When Java Module can not match the parameters it will throw JAVA:ARGUMENT_MISMATCH error.
Call method on an instance
To do the call on the already created instance I use the date and time classes introduced in Java 8. Here is the code that I would like to mimic using Mule:
java.time.LocalDate now = java.time.LocalDate.now();
java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("MMMM dd, yyyy");
now.format(formatter);
To keep it short this code takes the current date and displays it as MMMM dd, yyyy. For example assume that today is the 25th of March 2020 we should receive March 25, 2020.
We have here three steps:
- call static method now() on class java.time.LocalDate
- call static method ofPattern(String) on class java.time.format.DateTimeFormatter
- call method format() on the instance of LocalDate passing instance of java.time.format.DateTimeFormatter
In the second step in order to create an instance of formatter we pass a parameter. So we send the following arguments:
{
arg0: "MMMM dd, yyyy"
}
We also have to set the target property to save the created instance into a variable formatter. As a result in the payload, we have still an instance of LocalDate.
Here is how we configure the last step:
- Instance: #[payload]
- Class: java.time.LocalDate
You need to provide a class of the instance. If the instance is not an instance of the provided class you will receive an error like Expected an instance of type java.time.LocalTime but was java.time.LocalDate
- Method: format(DateTimeFormatter)
Have you noticed that the parameter class has been provided without a package? This is by design. If you try to set it to format(java.time.format.DateTimeFormatter) you will receive error like No public Method found with name [format(java.time.format.DateTimeFormatter)] in class java.time.LocalDate with arguments DateTimeFormatter arg0.
- Args: as arg0 we pass vars.formatter
We should now have the same result as with the previously shown Java code.
Source Code
The source is available at GitHub.
Summary
It is fairly simple to call the method on a static class using invoke–static event processor. We only need to provide class, method signature, and arguments if are expected. Making a method call on the already created instance is fairly the same. We use invoke event processor. This component expects instance, class, and method signature. If the method contains parameters we need to provide them as well. Remember that method signature does not require java packages.
In comparison to Entry Point Resolvers and Java Component in Mule 3. Mule 4 has simplified it by introducing Java Module. We do not need to implement custom interfaces and wonder which resolver to use. We specify clearly in the new event processor what should be invoked. I think that this is a great step for simplification.
Perfect articulation.
Perfect !