Can an application make a call on a proxy created via component binding on a non-Mule thread i.e. a thread created by the application? I am trying to do that and I get a NullPointerException on org.mule.DefaultMuleEvent:268.
That’s on Mule EE 3.3.0
Thanks.
UPDATE:
The Mule code
<mule ...>
<vm:endpoint path="entryPoint" name="entryPoint" />
<flow name="entryPoint.Flow">
<inbound-endpoint ref="entryPoint" exchange-pattern="request-response" />
<component class="foo.Component">
<binding interface="foo.Interface" method="echo">
<vm:outbound-endpoint path="foo.Interface.echo" exchange-pattern="request-response" />
</binding>
</component>
</flow>
<flow name="foo.Interface.echo">
<vm:inbound-endpoint path="foo.Interface.echo" exchange-pattern="request-response" />
<logger level="INFO" />
</flow>
</mule>
The Java component
package foo;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class Component {
private Interface i;
public String foo(final String input) {
return callInterfaceOnAWorkerThreadWith(input);
}
public void setInterface(final Interface i) {
this.i = i;
}
private String callInterfaceOnAWorkerThreadWith(final String input) {
ExecutorService executorService = newSingleThreadExecutor();
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return i.echo(input);
}
});
executorService.shutdown();
try {
executorService.awaitTermination(60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
return future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
}
The Java interface
package foo;
public interface Interface {
String echo(String input);
}
The test fixture to execute the mule app
package foo;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.api.client.MuleClient;
import org.mule.tck.junit4.FunctionalTestCase;
@RunWith(MockitoJUnitRunner.class)
public class ATest extends FunctionalTestCase {
@Test
public void echo() {
final MuleClient client = muleContext.getClient();
MuleMessage reply = send(client, "entryPoint", "a string");
assertEquals("a string", reply.getPayload());
}
@Override
protected String getConfigResources() {
return "app/componentbindingonanotherthread.xml";
}
private MuleMessage send(final MuleClient client, final String url, final Object payload) {
try {
return client.send(url, payload, null, RECEIVE_TIMEOUT);
} catch (final MuleException e) {
throw new RuntimeException(e);
}
}
}
Executing the code above shows the following exception in the logs:
Root Exception stack trace:
java.lang.NullPointerException
at org.mule.DefaultMuleEvent.<init>(DefaultMuleEvent.java:268)
at org.mule.component.BindingInvocationHandler.invoke(BindingInvocationHandler.java:96)
at $Proxy14.echo(Unknown Source)
The problem boils down to the fact that Mule’s
BindingInvocationHandlerlooks-up the currentMuleEventby usingRequestContext.getEvent(), which isThreadLocal-bound. In other words, since you’re performing the binding in another thread, the Mule context is lost and you get the above failure.Using Mule’s worker manager (that you can get from the MuleContext), which would be a better design that use your own
ExecutorService, would still not solve the problem because of the reliance ofBindingInvocationHandleron the (deprecated!)RequestContext.getEvent()construct.I think you need to rethink your design because, when you really look into it, it doesn’t make much sense IMO. Executing the binding on another thread doesn’t buy you anything since the
entryPointis request-response so an inbound thread is mobilized out of the pool. This thread remains blocked until the binding is finished, whether the binding itself is performed by this thread or another thread.If, for a reason that escapes me, you really need to perform the binding in another thread, then move the binding in another flow and invoke it with a
request-replymessage processor.The lesson here is: Mule has a rich threading model already and it’s better to play within it by using the threading constructs it offers (
request-reply,async,one-wayVM queues) rather than coding your own.