Sometime it is needed to use custom Java code to processes current message. Developed custom code is known as Java Component. How mule knows 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 more sophisticated use case or class 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 next article I will describe in more detail new Java Module available in version 4.x.

Mule version

Tools

I am going to use following tools:

  • Anypoint Studio with Mule 3.9 runtime
  • Postman to perform tests

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 Callable interface.

 

It has defined onCall method accepting MuleEventContext’s instance. Using this instance we are able to alter 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="pl.profitonline.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 oft 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"
]

Order of execution

When you do not define any resolver and mule can not match any method at first it will try use resolver on its own. Mule uses following resolvers:

  • MethodHeaderPropertyEntryPointResolver
  • ReflectionEntryPointResolver
  • AnnotatedEntryPointResolver
  • CallableEntryPointResolver

Each resolver is configured using default settings. So if you need more control you need to define them explicitly.

Implementation of Callable

If your class implements Callable interface you do not need to configure 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&amp;lt;String, String&amp;gt; payload = new HashMap&amp;lt;String, String&amp;gt;();
   payload.put("Resolver", "Callable");
 
   return payload;
 }
 
 public Object onEmpty() {
   return "Non-callable method";
 }
}

We have here two methods that can be used – line 4 and 12. Mule would, by default, invoke onCall method. However both these methods could be invoked because for empty payload 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 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 variable name that contains method name to invoke. In the example below we set variable just before Java call.

 

We need to use this variable somehow. Below in XML markup you can see that within component we added property-entry-point-resolver and specified variable name


<component class="pl.profitonline.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 value that will result in method name that does not exists 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 methods in new class but this is rather a bad practice. We may, however, instruct Mule which exactly 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 (line 3). It may look as follows:

<component doc:name="Java" class="pl.profitonline.components.StringComponent">
  <method-entry-point-resolver&gt;
    <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 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 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 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 parameter type. Java knows that payload is 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 known, 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="pl.profitonline.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.

Tagged on:             

Leave a Reply

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