Last Updated on 30/05/2021 by Patryk Bandurski
Sometimes it is needed to use custom Java code to processes the current message. Developed custom code is known as Java Component. How does the mule know which method should invoke and what parameters should be there passed? There are some rules that your class may full fill in order to work without any additional configuration. However, when you have a more sophisticated use case or class that is fairly complex you would probably need Entry Point Resolver configured. I will explain on simple examples some of them. This is valid for Mule version 3.x. In the next article, I will describe in more detail the new Java Module available in version 4.x.
Java Component
Java Component is designed to invoke custom code written in Java. It may be a simple POJO (Plain Old Java Object) but it needs to meet some requirements in order to be called by Mule. On the other hand, Mule prepared a Callable interface.
It has defined onCall
method accepting MuleEventContext’s instance. Using this instance we are able to alter the Mule message and payload. When mule enters Java Component message processor implementing Callable interface it knows that onCall should be called. Below you can see simple implementation.
import org.mule.api.MuleEventContext;
import org.mule.api.lifecycle.Callable;
public class SimpleComponent implements Callable {
@Override
public Object onCall(MuleEventContext eventContext) throws Exception {
// do some usefull stuff
return null;
}
}
In order to call Java code you need to move Java Component on canvas and configure class. Below you can see XML configuration:
<component doc:name="Java" class="com.ambassadorpatryk.components.SimpleComponent">
</component>
Entry Point Resolver
Mule during processing Java Component tries to find the method that is the best match. You do not need to define any Entry Point Resolver if you have a simple scenario where your method can be matched. However, in more advanced scenarios component’s class is often complex and contains a lot of methods. These methods may have different names, arguments. When Mule can not match methods it will throw an exception:
******************************************************************************** Message : Failed to find entry point for component, the following resolvers tried but failed: [ MethodHeaderPropertyEntryPointResolver: The required property "method" is not set on the event ReflectionEntryPointResolver: Could not find entry point on: "pl.profitonline.components.MultiMethodsComponent" with arguments: "{}" AnnotatedEntryPointResolver: Component: pl.profitonline.components.MultiMethodsComponent@3e9e5a23 doesn't have any annotated methods, skipping. CallableEntryPointResolver: Object "pl.profitonline.components.MultiMethodsComponent@3e9e5a23" does not implement required interface "interface org.mule.api.lifecycle.Callable" ]
Implementation of Callable
If your class implements a Callable interface you do not need to configure a callable entry point resolver. Mule will invoke onCall method. Imagine that you have an empty payload on input and following SimpleComponent. Which method would Mule invoke?
public class SimpleComponent implements Callable {
@Override
public Object onCall(MuleEventContext eventContext) throws Exception {
// do some usefull stuff
Map<String, String> payload = new HashMap<String, String>();
payload.put("Resolver", "Callable");
return payload;
}
public Object onEmpty() {
return "Non-callable method";
}
}
We have here two methods that can be used – lines 4 and 12. The mule would, by default, invoke onCall method. However both these methods could be invoked because onEmpty methods without parameters can be chosen.
I found callable entry point resolver useless, it does not need to be defined explicitly.
Dynamically set method to call
We may decide to choose which method to invoke only based on the variable’s content. This is useful in cases when we change component behavior based on some predefined condition. Mule introduced Property Entry Point Resolver for such scenarios. This resolver requires a variable name that contains a method name to invoke. In the example below, we set the variable just before the Java call.
We need to use this variable somehow. Below in XML markup, you can see that within the component we added property-entry-point-resolver and specified variable name
<component class="com.ambassadorpatryk.components.MultiMethodsComponent" doc:name="Java">
<property-entry-point-resolver property="MethodName"/>
</component>
For component’s class like below we could route incoming message to one of those two methods just based on MethodName variable’s content.
public class MultiMethodsComponent {
public Object performSimpleCall() {
...
}
public Object performComplexCall() {
...
}
}
When we try to send a value that will result in a method name that does not exist we would receive an error like “Failed to find entry point for component, the following resolvers tried but failed“.
Explicitly specifying method name
We have already defined Java class with custom code. It contains two methods like below:
public class StringComponent {
private final static String TEXT = "This is a static text common for both methods";
public Object makeSubstring() {
return TEXT.substring(10);
}
public Object makeToUpperCase(){
return TEXT.toUpperCase();
}
}
For empty payload, these two methods are valid so for Mule this is ambiguous. We could redefine one of the methods in the new class but this is rather a bad practice. We may, however, instruct Mule which exact method we would like to invoke. In order to do this, we use method-entry-point-resolver. In our particular example, we may decide to invoke makeSubstring. It may look as follows:
<component doc:name="Java" class="com.ambassadorpatryk.components.StringComponent">
<method-entry-point-resolver>
<include-entry-point method="makeSubstring"/>
</method-entry-point-resolver>
</component>
Best match based on argument’s type
We may have even more advanced scenario. Image that we have a class like below:
public class ReflectionComponent {
public Object emptyPayload() {
return "This is a string";
}
public Object objectPayload(Object payload) {
return new String[] {"Yet this is an array for object", payload.toString()};
}
public Object stringPayload(String payload) {
return new String[] {"Yet this is an array for string", payload};
}
public Object string2Payload(String[] payload) {
return payload;
}
}
We have flow accepting HTTP GET requests and calling three times the same Java Component. Now, step by step, we will see what Mule is trying to do.
When the GET request is received payload is empty (NullPayload). We have only one method that has no parameters defined. Mule will call emptyPayload method from line 2. As a result, it will return text. Now it is time to call the same Java Component but with a different payload. Which method should Mule call? If you said stringPayload, from line 10, you are right. Why did Mule not chose objectPayload accepting an Object? In the second line we can see that emptyPayload method returns Object type. However, in the next line, we explicitly return a String.
Mule will use Reflection Entry Point Resolver. It will use reflection to match methods based on the parameter type. Java knows that payload is an Object but more precisely speaking it is a String, so it will match stringPayload. Next, we return an array of Strings. This step is similar to the previous one, Mule knows, using reflection-entry-point-resolver that payload is of type String[] (array) as a consequence string2Payload will be matched.
As it was mentioned previously refelction-entry-point-resolver is used by default in Mule, so you do not need to insert it explicitly. However, if you would like to omit checking through different entry point resolver you may declare it like below:
<component doc:name="Java" class="com.ambassadorpatryk.components.ReflectionComponent">
<reflection-entry-point-resolver></reflection-entry-point-resolver>
</component>
Summary
Mule uses Callable and Reflection Entry Point Resolvers by default. To choose dynamically method to call you should use Property Entry Point Resolver. In case of ambiguous methods, you may specify directly which one to use by Method Entry Point Resolver.
Source Code
Source is available at GitHub.