Mule 4 new Java Module

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

Flow using Invoke static operation from Java Module
Flow using Invoke static operation from Java Module

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
Invoke Static general settings - arguments, class and method
Invoke Static general settings – arguments, class and method

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)
Generating random number
Generating random number

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.

Computing and formatting current date & time using Java Module
Computing and formatting current date & time using Java Module

We have here three steps:

  1. call static method now() on class java.time.LocalDate
  2. call static method ofPattern(String) on class java.time.format.DateTimeFormatter
  3. 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 invokestatic 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.

Mule 4 new Java Module

2 thoughts on “Mule 4 new Java Module

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top