Search code examples
muleanypoint-studio

Basic flow results in NullPayload


I'm new to Mule and I'm trying to get the very first example from the book "Mule in Action" working.

I'm using Mule 3.9 and Anypoint Studio 6.4.1. In chapter 1 they describe a very basic product_registration flow that I have created as follows:

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:jms="http://www.mulesoft.org/schema/mule/jms" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
    xmlns:spring="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core 
http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http 
http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/jms 
http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd">
    <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8880" basePath="products" doc:name="HTTP Listener Configuration"/>
    <jms:activemq-connector name="Active_MQ" username="admin" password="admin" brokerURL="tcp://localhost:61616" validateConnections="true" doc:name="Active MQ"/>
    <flow name="product_registrationFlow">
        <http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
        <logger level="INFO" doc:name="Logger Before"/>
        <byte-array-to-string-transformer doc:name="Byte Array to String"/>
        <logger level="INFO" doc:name="Logger After"/>
        <jms:outbound-endpoint doc:name="JMS" queue="products"/>
    </flow>
</mule>

and an accompanying functional test:

@Test
public void testCanRegisterProducts() throws Exception {

    LocalMuleClient client = muleContext.getClient();

    String productAsJson = "{ \"name\":\"Widget\", \"price\": 9.99, \"weight\": 1.0, \"sku\": \"abcd-56789\" }";

    MuleMessage source = new DefaultMuleMessage(productAsJson, muleContext);
    client.dispatch("http://localhost:8880/products", source);

    MuleMessage result = client.request("jms://products", RECEIVE_TIMEOUT);

    assertNotNull(result);
    assertFalse(result.getPayload() instanceof NullPayload);
    assertEquals(productAsJson, result.getPayloadAsString());
}

When I run the test it fails at the last assert because the actual payload is:

{NullPayload}

And if I look directly in ActiveMQ I see that payload. If I manually post to Mule (using a tool like Poster in Chrome, setting only the header Content-Type: application/json) the payload is valid JSON and I can get the test to pass (because it is getting the pending message from the queue posted by Poster and the message it creates is at the end of the queue with payload {NullPayload}.

Can someone shed some light on why the flow fails when invoked from the JUnit test, but seems to work when invoked by using a tool like Poster?

Update: With the help of Pierre B. I got it working. The initialization of the MuleMessage in the FunctionalTestCase was updated as follows:

    MuleMessage source = new DefaultMuleMessage(productAsJson, muleContext);
    source.setProperty("Content-Type", "application/json", PropertyScope.INBOUND);
    source.setProperty("Content-Length", Integer.valueOf(productAsJson.length()), PropertyScope.INBOUND);

Solution

  • You said it works when you POST a message using an external tool instead of MuleClient:

    using a tool like Poster in Chrome, setting only the header Content-Type: application/json

    Try adding the same header to your MuleMessage such as:

    MuleMessage source = new DefaultMuleMessage(productAsJson, muleContext);
    
    # define the headers
    Map<String, Object> headers = new HashMap<String, Object>(1);
    headers.put("Content-Type", "application/json");
    headers.put("Content-Length", sourceLength);
    
    # add the headers as function parameter
    client.dispatch("http://localhost:8880/products", source, headers);
    

    EDIT: as @sceaj pointed out, both Content-Type and Content-Length headers are required.