Contact Free trial Login

Argument Transformation - Mule 4

Since Java Module version 1.1, this module transforms the arguments given to a Java invocation even if they don’t exactly match the type expected by the Java method. The Java module infers how to transform a parameter to what a method expects.

When Are Arguments Transformed?

For each specified parameter, the Java module checks whether its type matches the type expected by the method for that parameter or not. If it is not what the method expects, the Java Module transforms it to what is expected.

Simple Example of Argument Transformation

This is a simple example where one of the arguments is transformed.

The static method min from the Math class is called, but the class expects to receive two int parameters. The call is made with a Number and a String:

<flow name="calculateMinimum">
  <java:invoke-static class="java.lang.Math" method="min(int,int)" >
    <java:args>#[{
        'arg0': 5,
        'arg1' : "3"
      }]
    </java:args>
  </java:invoke-static>
</flow>

In this case arg1 would have been passed as a string, but it is transformed into an integer before calling the Java method.

Pass a Custom POJO to a Module as a Map

In Mule 3 when working with custom POJOs, users explicitly declared in the DataWeave expression what type the data to transform. In Mule 4, the Java module automatically recognizes the expected type for an argument and tries to transform the data to the expected type. (POJO stands for plain old Java object.)

In the examples that follow, a Map parameter is transformed into a custom POJO by the Java module.

  1. Define the Car class of the POJO:

    public class Car {
      private String doors;
      private String wheels;
      private String engine;
    
      public String getDoors() {
        return doors;
      }
    
      public String getWheels() {
        return wheels;
      }
    
      public String getEngine() {
        return engine;
      }
    }
  2. Declare the describeCar static method in the class to receive an instance of the POJO:

    public class CarUtils {
      public static String describeCar(Car car) {
        return "This car has: " + car.getEngine() + ", " + car.getWheels() + ", " + car.getDoors();
      }
    }
  3. Invoke the static method in the Mule flow:

    <flow name="describeCarFlow">
        <java:invoke-static class="CarUtils" method="describeCar(Car)" >
            <java:args>
                #[{
                'car': {
                    'doors' : "Four doors",
                    'engine' : "120 Horsepower engine",
                    'wheels' : "17 Inch chromed rims"
                }
                }]
            </java:args>
        </java:invoke-static>
    </flow>

    The Mule operation receives the car map whose key and values are mapped to a Car class instance. This map is transformed into an instance before the invocation of the Java method. Without this improvement, the invocation would fail, stating that the arguments passed to the Java method did not match its signature.

Collections and Maps

The Java module checks that every element of the collection and every entry of the map honors the Java Generic declared by the method signature. If any element fails this check, the Java module creates the whole collection or map repopulates it with the transformed elements.

The Java method is called with another instance of the collection or map if a transformation is needed (not the same as the one passed in the Mule flow). This is particularly important if the method called is expected to add or remove elements in the collection or map, and the ability to reference this collection or map after the invocation is needed.

Transform a List Example

The List given to the operation is transformed and a new List with the transformed items is passed to the Java method.

The IntegerUtils Java class declares the sumNumbers static method:

public class IntegerUtils {
  public static Integer sumNumbers(List<Integer> numbers) {
    return numbers.stream().mapToInt(i -> i.intValue()).sum();
  }
}

This Mule flow invokes the Java class:

<flow name="sumListOfNumbers">
  <java:invoke-static class="IntegerUtils" method="sumNumbers(List)">
    <java:args>
      #[{
        'numbers' : [1, "5", 4, "8", 3]
      }]
    </java:args>
  </java:invoke-static>
</flow>

The numbers list in the flow has two string objects ("5" and "8"), which the module transforms into integers.

Transform a Map Example

A map for the operation is transformed and a new map with the transformed keys and values is passed to the Java method.

This Java class declares the sumMapValues static method:

public class MapUtils {
  public static Integer sumMapValues(Map<String, Integer> map) {
    return map.keySet().stream().mapToInt(i -> i.intValue()).sum();
  }
}

This Mule flow invokes the MapUtils Java class:

<flow name="sumValuesOnMap">
  <java:invoke-static class="MapUtils" method="sumMapValues(Map)">
    <java:args>
      #[{
        'map' :{
          'books': "22",
          'pencils': 33,
          'pens': 12
        }
      }]
    </java:args>
  </java:invoke-static>
</flow>

This map contains values that do not comply with the generics imposed in the Java method. Because of this, a new instance with the transformed key and values is used to call the method.