I'm trying to use the expression #[process:processName:valueToProcess] in my flow.
This is my flow config:
<flow name="exampleFlow">
<quartz:inbound-endpoint jobName="job1" cronExpression="0/5 * * * * ?" connector-ref="quartzConnector">
<quartz:event-generator-job>
<quartz:payload>foo</quartz:payload>
</quartz:event-generator-job>
</quartz:inbound-endpoint>
<logger message="#[process:test:#[payload]]" level="INFO"/>
</flow>
<sub-flow name="test" doc:name="test">
<component class="com.myComp.ComponentTest" doc:name="Java"/>
</sub-flow>
And this is my Java Component:
package com.myComp;
public class ComponentTest
{
public String test(String s)
{
return s + "bar";
}
}
The problem comes when the expression #[process:test:#[payload]] is evaluated. It throws this exception:
java.lang.NullPointerException
at org.mule.component.AbstractComponent.invokeInternal(AbstractComponent.java:112)
at org.mule.component.AbstractComponent.process(AbstractComponent.java:156)
at org.mule.processor.chain.DefaultMessageProcessorChain.doProcess(DefaultMessageProcessorChain.java:99)
at org.mule.processor.chain.AbstractMessageProcessorChain.process(AbstractMessageProcessorChain.java:66)
at org.mule.processor.chain.InterceptingChainLifecycleWrapper.doProcess(InterceptingChainLifecycleWrapper.java:56)
at org.mule.processor.chain.AbstractMessageProcessorChain.process(AbstractMessageProcessorChain.java:66)
at org.mule.processor.chain.InterceptingChainLifecycleWrapper.process(InterceptingChainLifecycleWrapper.java:87)
at org.mule.api.processor.MessageProcessors$LifecyleAwareMessageProcessorWrapper.process(MessageProcessors.java:114)
at org.mule.expression.MessageProcessorExpressionEvaluator.evaluate(MessageProcessorExpressionEvaluator.java:87)
at org.mule.expression.DefaultExpressionManager.evaluate(DefaultExpressionManager.java:273)
at org.mule.expression.DefaultExpressionManager.evaluate(DefaultExpressionManager.java:209)
at org.mule.expression.DefaultExpressionManager$1.match(DefaultExpressionManager.java:399)
at org.mule.util.TemplateParser.parse(TemplateParser.java:156)
at org.mule.util.TemplateParser.parse(TemplateParser.java:133)
at org.mule.expression.DefaultExpressionManager.parse(DefaultExpressionManager.java:395)
at org.mule.expression.DefaultExpressionManager.parse(DefaultExpressionManager.java:379)
at org.mule.api.processor.LoggerMessageProcessor.log(LoggerMessageProcessor.java:89)
at org.mule.api.processor.LoggerMessageProcessor.process(LoggerMessageProcessor.java:71)
at org.mule.processor.chain.DefaultMessageProcessorChain.doProcess(DefaultMessageProcessorChain.java:99)
at org.mule.processor.chain.AbstractMessageProcessorChain.process(AbstractMessageProcessorChain.java:66)
at org.mule.processor.AbstractInterceptingMessageProcessorBase.processNext(AbstractInterceptingMessageProcessorBase.java:105)
at org.mule.interceptor.AbstractEnvelopeInterceptor.process(AbstractEnvelopeInterceptor.java:55)
at org.mule.processor.AsyncInterceptingMessageProcessor.processNextTimed(AsyncInterceptingMessageProcessor.java:111)
at org.mule.processor.AsyncInterceptingMessageProcessor$AsyncMessageProcessorWorker.doRun(AsyncInterceptingMessageProcessor.java:158)
at org.mule.work.AbstractMuleEventWork.run(AbstractMuleEventWork.java:43)
at org.mule.work.WorkerContext.run(WorkerContext.java:310)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
The same happens with any other scripting component.
I'm guessing it has something to do with my component lifecycle, but I couldn't find a way to make it work.
If I replace my component with any other Mule processor (e.g. logger, stringAppend) it works fine.
Any help will be appreciated!
You're right when you say it's an initialization issue: for any reason, components in a sub-flow are not initialized the same way they are in a flow, ie. this works:
<flow name="test" doc:name="test">
<component class="com.myComp.ComponentTest" doc:name="Java"/>
</flow>
I believe this is a bug hence I reported it. Please upvote the issue.
To make this work with an asynchronous event, like the ones the Quartz inbound endpoint generates, you will need to add processingStrategy="synchronous" to the called flow in order to invoke in synchronously, otherwise by default it will be invoke asynchronously (even if process:test:#[payload] expects a synchronous response). That's a known quirk in Mule 3.
So the end solution is:
<flow name="test" processingStrategy="synchronous" doc:name="test">
<component class="com.myComp.ComponentTest" doc:name="Java"/>
</flow>
Related
A little description of the flow: I have a flow that is making an HTTP call to a REST Webservice. There are a couple of errors that we get as statusCode=500 but with different "errorCodes" inside the response body. This request connector is wrapped inside a try block with multiple on-error-continue based on the content of the response body in its "when" attribute. eg: error.errorMessage.payload.errorCode==CODE_A. Adding an image and source code of the flow below
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="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:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config" doc:id="dee432d7-a176-448c-a1ac-a374a86fa85b" >
<http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>
<flow name="test2Flow" doc:id="be602af1-cfcf-46f1-a312-ea6a5544503b" >
<http:listener doc:name="Listener" doc:id="f160fa61-15be-4744-b823-973e20740e73" config-ref="HTTP_Listener_config" path="/test"/>
<logger level="INFO" doc:name="Some logic before request" doc:id="6354a117-e918-4937-a1d9-df4ae0ef6386" message="Some logic before request"/>
<try doc:name="Try" doc:id="15bd106a-c116-4d87-958e-8ad698026739" >
<http:request method="GET" doc:name="Request" doc:id="a046919e-f149-45d6-9a70-db37cac0540f" url="http://example.com/some-resource" />
<error-handler >
<on-error-continue enableNotifications="true" logException="true" doc:name="when error.errorMessage.payload.errorCode==CODE_A" doc:id="20fc0575-da65-41b4-81f7-d364227b1639" when='error.errorMessage.payload.errorCode=="CODE_A"'>
<logger level="INFO" doc:name="Some logic for CODE_A" doc:id="a699ccd0-952c-415f-b288-4ea027f2cff2" message="Some logic for CODE_A" />
</on-error-continue>
<on-error-continue enableNotifications="false" logException="false" doc:name="when error.errorMessage.payload.errorCode==CODE_B" doc:id="63dc8aa5-5fae-4235-a4ae-a80903a76362" when='error.errorMessage.payload.errorCode=="CODE_B"' >
<logger level="INFO" doc:name="Some logic for CODE_B" doc:id="9364ff1e-4bcd-48d5-a37a-a207828cda9d" message="Some logic for CODE_B" />
</on-error-continue>
</error-handler>
</try>
<logger level="INFO" doc:name="Some logic After request" doc:id="fd754198-d6b5-40dd-b23f-c8619faee7e6" message="Some logic After request" />
</flow>
</mule>
What I am trying to do: I am trying to write MUnit to verify if the correct error-handler block is executed based on response. However, I do not see any option to mock an error with its errorMessage part filled.
What I am looking for: I am trying to find out one of these
Anyway to mock error with errorMessage using the simple mock-when'sthen-return element. I do not think it is possible, but if there is a way please let me know.
Maybe there is a way to use a Java class for raising complex errors. I am thinking if I can use mock-when'sthen-call to call a flow and then raise an error using it.
Any other way to do it.
Note:
Mule version: 4.3.0 MUnit version: 2.3.0
I can not make change in the REST webservice, I do not own that.
I am trying to avoid any code change, For example, I can ignore status code in the message validator and do the error handling using response directly, with something like choice. But I will do it as the last resort
You can try using something like this when mocking error for HTTP requests:
Create the following DW file:
httpError.dwl
var detailMessage = 'Internal server error'
var myJsonResponse = '{"errorCode":"ERROR_A"}'
var typedValue = java!org::mule::runtime::api::metadata::TypedValue::new(myJsonResponse, java!org::mule::runtime::api::metadata::DataType::JSON_STRING)
var errorMessage = java!org::mule::runtime::api::message::Message::of(typedValue)
var errorType = java!org::mule::extension::http::api::error::HttpError::INTERNAL_SERVER_ERROR
---
java!org::mule::extension::http::api::request::validator::ResponseValidatorTypedException::new(detailMessage, errorType, errorMessage)
In your MUnit test suite file, create the following flow:
<flow name="munit-set-error-code-event-flow" doc:id="3b7e266c-2f3b-4105-8fdb-8ea44d5f128e" >
<munit:set-event doc:name="Generate error" doc:id="4ee8b870-6c8b-4570-bc70-7ec0086a1bca" cloneOriginalEvent="true">
<munit:error id="HTTP:INTERNAL_SERVER_ERROR" exception="#[${file::httpError.dwl}]" />
</munit:set-event>
</flow>
And, finally, in your actual MUnit test flow, configure the then-call option to call the above flow. Eg:
<munit:test name="test-test-suite-testFlowTest" doc:id="5670a9cd-758a-4f2c-a55f-57b48a9f3b41" description="Test" timeOut="1200000">
<munit:behavior >
<munit-tools:mock-when doc:name="Mock when" doc:id="466fd55c-80b4-4e27-82ed-f561223a3b4f" processor="http:request">
<munit-tools:with-attributes >
<munit-tools:with-attribute whereValue="08c4fba6-ef4b-4006-bd7d-deb1fd0f5304" attributeName="doc:id" />
</munit-tools:with-attributes>
<munit-tools:then-call flow="munit-set-error-code-event-flow" />
</munit-tools:mock-when>
</munit:behavior>
<munit:execution >
<flow-ref doc:name="Flow-ref to testFlow" doc:id="c57b3265-b453-432c-88fd-db2df7125fdc" name="testFlow"/>
</munit:execution>
</munit:test>
Although I'm not sure if this is something recommended to do, it should work for what you want to achieve.
Let me know if it works.
I am facing problem with mule testing. I just tries out simple functional testing example.
following is my flow
<flow name="muletestFlow1" doc:name="muletestFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="test" doc:name="HTTP"/>
<response>
<object-to-string-transformer />
</response>
<component class="com.org.Square" doc:name="Square"/>
<component class="com.org.Neg doc:name="Neg"/>
</flow>
and following is my testing class
#Test
public void testSquareAddInverse() throws Exception {
MuleClient client = muleContext.getClient();
MuleMessage reply = client.send ("http://localhost:8081/test", "3", null, 5000);
assertNotNull(reply);
assertNotNull(reply.getPayload());
assertTrue(reply.getPayload() instanceof String);
String result = (String)reply.getPayload();
assertEquals("-9", result);
If i run above class as junit test then got following error.
java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:92)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at com.test.TestEx.testSquareAddInverse(TestEx.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:622)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:46)
at org.junit.internal.runners.statements.FailOnTimeout$1.run(FailOnTimeout.java:28)
What is the solution please help me.
If i comment this line
assertTrue(reply.getPayload() instanceof String);
then got following error.
java.lang.ClassCastException: org.mule.transport.http.ReleasingInputStream cannot be cast to java.lang.String
What is the wrong in this code? How can i solve this problem. please help me
Thanks in advance
Since you call the flow through http protocol, which implicitly transforms the payload type. So the reply in the unit test will not contain a String but a InputStream. This is expected so you should test for a InputStream instead of a String. To get the value as a String you can use the method MuleMessage#getPayload(java.lang.Class) that will transform the payload to a String using the registered transformers.
#Test
public void testGetNegaiveNine() throws Exception {
MuleClient client = muleContext.getClient();
MuleMessage reply = client.send ("http://localhost:8081/test", "3", null, 5000);
assertNotNull(reply);
assertNotNull(reply.getPayload());
assertTrue(reply.getPayload() instanceof InputStream);
String result = reply.getPayload(String.class);
assertEquals("-9", result);
}
The exception you get in the flow
after that.
"DefaultJavaComponent{muletestFlow1.component.3418143}. Message payload is of type: ContentLengthInputStream"
is because the components can not handle the payload that the message inside your flow has. Since you are sending a HTTP POST into the flow the payload of the incoming message is a ContenLengthInputStream and you components expects a String.
So you will have to transform the payload into a String by adding a object-to-string transformer before the components. The complete flow would then be something like this.
<flow name="muletestFlow1" doc:name="muletestFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="test" doc:name="HTTP"/>
<response>
<!-- Ensure that the response is a String -->
<object-to-string-transformer />
</response>
<!-- Ensure that the incoming request is a String -->
<object-to-string-transformer />
<component class="com.org.Square" doc:name="Square"/>
<component class="com.org.Neg doc:name="Neg"/>
</flow>
I'm trying to make use of #attachment http properties
I have a devkit method called validate in a flow that is called like so
<http:connector name="httpConnector" doc:name="HTTP\HTTPS">
<service-overrides
messageFactory="org.mule.transport.http.HttpMultipartMuleMessageFactory" />
</http:connector>
<flow name="AttachmentTestFlow1" doc:name="AttachmentTestFlow1">
<http:inbound-endpoint connector-ref="httpConnector" doc:name="HTTP" exchange-pattern="request-response" host="localhost" port="8100"/>
<Foo:validate config-ref="TestMessageSizeHappy" />
</flow>
In Devkit:
#Processor
public Object validate(#Payload InputStream in
,#InboundAttachments("*") Map<String, DataHandler> inboundAttachments
,#OutboundAttachments Map<String, DataHandler> outboundAttachments
) {
however when running my mule application this is thrown:
ERROR 2013-07-30 09:06:39,225 [main]
org.mule.module.launcher.application.DefaultMuleApplication: null
org.xml.sax.SAXParseException: cvc-complex-type.2.4.b: The content of
element 'xmlthreatprotection:validate' is not complete. One of
'{"http://www.mulesoft.org/schema/mule/core":annotations,
"http://www.mulesoft.org/schema/mule/xmlthreatprotection":inbound-attachments}'
is expected. at
org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown
Source)
It seems to me that mule expects the attachments to be put in as an attribute some how !
When I remove the #attachment stuff I get no errors at runtime.
Are you sure this is supported by DevKit? I can not find a single integration test in the source code that exercises #InboundAttachments and #OutboundAttachments annotations, while #InboundHeaders and #OutboundHeaders are both test covered.
Alternatively you can receive the MuleEvent and access the attachments through it:
#Processor
#Inject
public Object validate(#Payload InputStream in, MuleEvent muleEvent) {
I'm using mule 3.2.0 and
I'm invoking muleclient.send method in java-class which sends soap-request to a mule flow(not present here), which sends it to the http endpoint which receives request, logs it to my db with proxy pattern and propagates it to "ServiceFlow" which has Dispatcher transformer - java class, which uses invocation of MuleClient.send(String url, Object payload, Map messageProperties) method to dispatch one object of the payload array to an endpoint.
Then it gets processed by cxf:jax-ws-client, logged to a db once again and transferred to another instance of Dispatcher (where incoming payload is again of type Object[]). There I have http endpoint which does the service invocation. This part works okay.
But trouble appears at the receiving of the response. I've put Test1(2,3,4) transformers to print the invocation chain in my flows (They just do the System.out.println()), and saw that they're invoked in a weird sequence: I have it like 1,3 then TestTransformer (which is also the sysouter) then I get the error "NullPayload" in the caller of the main flow "Service flow" and then I receive the Test 4 and Test 2 outs. The responseTransformer-refs in patterns "1Proxy" are ignored, so as in "ServiceProxy". I've been looking for the solution for like a week now, and can't find it. In debugging, I can see that transformer called "TestTransformer" in has expected payload (Object[]) but when I receive it in my class caller, it appears as "NullPayload". I can see now that one of my endpoints has the path element instead of ref, not sure if this cause any impact on flow, but will check it. Also tried to use the "response" block to ensure my flow runs as expected. Any suggestions appreciated. Thanks
Here is what my config looks like:
<http:endpoint name="httpService" address="${service.soap}" exchange-pattern="request-response" responseTimeout="${timeout}" />
<vm:endpoint name="vmService" path="vmService" exchange-pattern="request-response"/>
<pattern:web-service-proxy
name="ServiceProxy"
inboundEndpoint-ref="httpService"
transformer-refs="to-string logging"
responseTransformer-refs="to-string logging"
outboundEndpoint-ref="vmService" />
<flow name="ServiceFlow" >
<inbound-endpoint ref="vmService"/>
<cxf:jaxws-service serviceClass="pkg.ServiceImpl" wsdlLocation="${service.wsdl}" enableMuleSoapHeaders="false" validationEnabled="true"/>
<custom-transformer class="pkg.Dispatcher">
<spring:property name="vmFlowPath" value="vm.logService"/>
</custom-transformer>
<custom-transformer name="TestTransformer" class="pkg.TestTransformer"/>
</flow>
<vm:endpoint name="vm1In" path="vm1In" exchange-pattern="request-response"/>
<vm:endpoint name="vm1Out" path="vm1Out" exchange-pattern="request-response"/>
<custom-transformer name="arrayGenerator" class="pkg.ArrayGenerator"/>
<custom-transformer name="objectExtractor" class="pkg.ObjectExtractor"/>
<custom-transformer name="faultChecker" class="pkg.FaultChecker"/>
<custom-transformer name="objectLogging" class="pkg.ObjectLogger">
<pattern:web-service-proxy
name="1Proxy"
inboundEndpoint-ref="vm1In"
transformer-refs="arrayGenerator objectLogging"
responseTransformer-refs="objectLogging objectExtractor faultChecker"
outboundEndpoint-ref="vm1Out" />
<flow name="logService">
<vm:inbound-endpoint path="vm.logService exchange-pattern="request-response"/>
<custom-transformer class="Test1"/>
<vm:outbound-endpoint ref="vm1In">
<cxf:jaxws-client
serviceClass="pkg.ServiceImpl"
operation="import"
enableMuleSoapHeaders="false"/>
<object-to-string-transformer/>
</vm:outbound-endpoint>
<object-to-xml-transformer>
<xm:xml-to-object-transformer returnClass="pkg.WSResponseClass"/>
<custom-transformer class="Test2"/>
</flow>
<flow name="DispatcherToServiceFlow">
<custom-transformer class="Test3"/>
<vm:inbound-endpoint path="vm1.Out"/>
<custom-transformer class="pkg.Dispatcher">
<spring:property name="vmFlowPath" value="vm.import"/>
</custom-transformer>
</flow>
<flow name="import">
<vm:inbound-endpoint path="vm.import" exchange-pattern="request-response"/>
<http:outbound-endpoint address="${importService}" responseTimeout="${timeout}" exchange-pattern="request-response" />
<object-to-string-transformer/>
<custom-transformer class="Test4"/>
</flow>
well, my problem really was the "path" element instead of referring to an existing vm endpoint with "ref" element like
<inbound-endpoint ref="vm1Out"/>
I am relatively new to mule and trying to define a mule flow which takes request XML via soap-based Web service. The XML is based on a complex schema and I have generated classes using WSDL2Java
After receiving the request cxf:jaxws-service executes the method submitOrder(SubmitOrderRequest parameters). After this method's execution I would like to transform the request XML to a little bit different format. Then this XML needs to be forwarded to another web service. The problem is that the mule message that comes out of ServiceImpl contains SubmitOrderResponse whereas I still want to work on SubmitOrderRequest.
<flow name="testService">
<http:inbound-endpoint address="http://localhost:62005/test"
exchange-pattern="request-response">
<cxf:jaxws-service serviceClass="com.test.ServicePortType" />
</http:inbound-endpoint>
<component class="com.test.ServiceImpl" />
<!-- transformer ref="MVIRequestTransformer" / -->
<!-- xm:object-to-xml-transformer / -->
<!-- logger message="XML payload is #[payload]" level="INFO" / -->
<!-- SEND TRASNFORMED MESSAGE TO ANOTHER SERVICE -->
</flow>
#WebService(endpointInterface = "com.pennmutual.services.mvi.MVIServicePort")
public class ServiceImpl implements ServicePortType {
...
#Override
public SubmitOrderResponse submitOrder(SubmitOrderRequest parameters) {
...
}
...
}
What are my options are. I can think of the following –
1. Put the request object somewhere in the context and retreive it later on for processing.
2. Change the return type of submitOrder to Object and return SubmitOrderRequest instead of SubmitOrderResponse.
Please suggest the best possible way to handle this situation. I am using mule 3.2.
I think there are two elegant way to do that (excluding the one that involves changing the webservice interface)
Store the request into a session variable and restore it afterwards.
Here is how your flow would look like:
<flow name="testService">
<http:inbound-endpoint address="http://localhost:62005/test" exchange-pattern="request-response">
<cxf:jaxws-service serviceClass="com.test.ServicePortType" />
</http:inbound-endpoint>
<message-properties-transformer scope="session">
<add-message-property value="payload" key="originalPayload" />
</message-properties-transformer>
<component class="com.test.ServiceImpl" />
</flow>
Use the enricher around the component to store the returned value into a variable so that it won't become the payload of your flow. Following an example of how to achieve this
<flow name="Echo" doc:name="Echo">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="6090" path="echo" encoding="UTF-8" />
<cxf:jaxws-service serviceClass="org.mule.example.echo.Echo" />
<enricher target="#[variable:echo]">
<component class="org.mule.example.echo.Echo" />
</enricher>
<logger level="ERROR" message="#[variable:echo]"/>
</flow>
You can find more informations on the enricher here