Calling Mule4 flows from Java code

Invoke Mule 4 flows from Java code.

[ Note: Please use the below approach only if there are no alternatives left. Drawbacks of the approach-

1) If the Mule flow called from Java returns a stream this approach won't work

2)  Affects performance]




Step 1:

Include the below in the pom.xml of the application


           ....

      ....
      </properties>

      <parent>
             <groupId>org.mule.extensions</groupId>
             <artifactId>mule-modules-parent</artifactId>
             <version>1.1.1</version>
      </parent>

      <build>
            ....
            ....             


Step 2:

In the beans.xml referenced in spring module


<spring:config name="Spring_Config" doc:name="Spring Config" doc:id="6f88cc78-710a-4963-b90d-3b512a1e4365" files="beans.xml" />

beans.xml-

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


<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:ss="http://www.springframework.org/schema/security"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security-4.2.xsd">


<context:annotation-config />

      <context:component-scan base-package="com.utils.mule" />

<bean id="muleContextOnStart" class="com.utils.mule.MuleContextOnStart" scope="prototype" />


</beans>

Step 3.1:

Java code (in package com.utils.mule) to invoke the mule 4 flow -


package com.utils.mule;


import javax.inject.Inject;
import org.mule.runtime.api.artifact.Registry;
import org.mule.runtime.api.event.Event;
import org.mule.runtime.api.message.Message;
import org.mule.runtime.core.api.construct.Flow;
import org.mule.runtime.core.api.event.CoreEvent;
import org.mule.runtime.core.api.event.EventContextFactory;
import org.springframework.stereotype.Component;

@Component
public class MuleFlowController {

      @Inject
      private Registry muleRegistry;

      public Flow getFlow(String flowName) {
             return (Flow) muleRegistry.lookupByName(flowName).orElse(null);
      }

      public Event createEvent(Object payload, Flow flow) {
             Message msg = Message.builder().value(payload).build();
             CoreEvent event = CoreEvent.builder(EventContextFactory.create(flow,
                   org.mule.runtime.dsl.api.component.config.DefaultComponentLocation.fromSingleComponent("add-location")))
                          .message(msg).build();
             return event;
      }

      public Event createEvent(String correlationId, Object payload, Flow flow) {
             Message msg = Message.builder().value(payload).build();
             CoreEvent event = CoreEvent
                          .builder(EventContextFactory.create(flow,
                                      org.mule.runtime.dsl.api.component.config.DefaultComponentLocation
                                                   .fromSingleComponent("add-location")))
                          .addVariable("coorelationId", correlationId).message(msg).build();
             return event;
      }

}



Step 3.2:

Java code initializing MuleFlowController.java (in-package com.utils.mule) to invoke the mule 4 flow -




package com.utils.mule;


import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class MuleContextOnStart {
     
      private static final Logger log = LoggerFactory.getLogger(MuleContextOnStart.class);
     
     
      public static MuleFlowController muleFlowController;
     
      @Autowired
      private MuleFlowController muleFC;
     
      @PostConstruct
      private void initStaticMuleOnStart() {
             muleFlowController = this.muleFC;
      }
     

      public void onStart() {
             log.info("Initialising the MuleFlowController as the application starts.");
      }

}

PS: You can combine Step 3.1 and Step 3.2 to be a part of the same Java class which will be used to execute the mule 4 flow. I have done this so that the MuleFlowController is statically instantiated once and can be called in any Java class which needs to execute the Mule 4 flow.

Step 4:

Call the onStart using a scheduler with the below scheduling frequency

     


 <flow name="mule4-onStart" doc:id="e18a2d87-b140-4166-90e8-d3ae9c5cce6f" >
             <scheduler doc:name="Scheduler" doc:id="2691c5f5-8b18-416d-b8e3-0df693d124cf" >
                   <scheduling-strategy >
          <fixed-frequency timeUnit="DAYS" frequency="10000000000000"/>
      </scheduling-strategy>
             </scheduler>
<logger level="INFO" doc:name="Logger" doc:id="c865822e-38a8-4ceb-8145-d3b06698bc36" message='Call onStart'/>
            <java:invoke doc:id="ace04c62-21aa-46ca-9a37-cfc5976bb058" instance="muleContextOnStart" class="com.utils.mule.MuleContextOnStart" method="onStart()"/>
             <logger level="INFO" doc:name="Logger" doc:id="c865822e-38a8-4ceb-8145-d3b06698bc36" message='Started'/>

 </flow>


Step 5:

Calling the Mule 4 flow -

From the Java code function where you wish to call the mule flow -

public void executeMuleFlow(DTOObject dtoObject, String flowName) {

             TypedValue<Object> returnValue = null;
             Log.info("Executing mule-4 flow - " + flowName);
             try {
                   Flow flow = MuleContextOnStart.muleFlowController.getFlow(flowName);
                   CoreEvent event = (CoreEvent) MuleContextOnStart.muleFlowController.createEvent(dtoObject, flow);
                   CoreEvent result = flow.process(event);
                   returnValue = result.getMessage().getPayload();
                   Log.info("Returned from flow - " + returnValue.getValue().toString());
             } catch (Exception exp) {
                   Log.error("Failed to invoke flow - " + exp);
             }
      }


Input to the mule-4 flow can also be passed as -  


public void executeMuleFlow(String field1, String field2, String flowName) {

             TypedValue<Object> returnValue = null;
             Log.info("Executing mule-4 flow - " + flowName);
             try {
                  JsonObject inputJson = Json.createObjectBuilder().add("field1", field1)
                                .add("field2", field2)
.build();
                   Flow flow = MuleContextOnStart.muleFlowController.getFlow(flowName);
                   CoreEvent event = (CoreEvent) MuleContextOnStart.muleFlowController.createEvent(inputJsonflow);
                   CoreEvent result = flow.process(event);
                   returnValue = result.getMessage().getPayload();
                   Log.info("Returned from flow - " returnValue.getValue().toString());
             } catch (Exception exp) {
                   Log.error("Failed to invoke flow - " + exp);
             }
      }


Step 6:
Once the flow is executed convert the payload to readable JSON



Hope this helps. You may post if you face any difficulties in the comments section. Will be happy to assist.





Comments

  1. This article is wrong. The flow controller is mishandling the EventContext and thus creating an event which is detached from the event that triggered the flow. As a result, if the invoked flow returns a stream, it will be already closed by the time it gets to you.

    There's no API for invoking flows in Mule 4, by design. This maneuver is not correct and unsupported in the product.

    Also, it's a bad practice to access the registry on each event execution

    ReplyDelete
    Replies
    1. I do get your point. This was a client requirement and had to be addressed. I shall update the blog to be used only if there are no alternatives left (Mostly to be used in case of Mule3 to 4 migration where excessive use of java has been done)

      The only limitation of this implementation is that the flow invoked from java should not return a stream.

      Delete
    2. It's not only streaming. It also affects performance and at design time you loose DataSense support as that Java invocation becomes a black box. Out of curiosity, why is the requirement to do the invocation with Java instead of simple flow-ref

      Delete
    3. The client had a Mule 3 application with under which ran a Hibernate application. The sole purpose of mule flows were to instantiate the Hibernate java classes. The java classes made extensive use of Mule flows in the apps.
      Unfortunately, this was done by some Mule PSG expert few years back.
      With minimal changes to the java code, this was the best we could do in the Mule4 version. Refactoring the Java to Mule4 will happen eventually but in the longer run.

      Delete

Post a Comment

Popular posts from this blog

Deep into Dataweave - Mule 4