I am using until-successful to retry calling a web service only when it is down.
Below is what I have tried:
<until-successful maxRetries="10" failureExpression="#[(message.inboundProperties['http.status'] != 200) && (message.inboundProperties['http.status'] != 500)]" synchronous="true" millisBetweenRetries="5000">
<flow-ref name="callSubFlow" doc:name="Flow Reference"/>
If I get a HTTP response 500 I also do not want to retry calling the web service. I have mocked a web service and when it returns a HTTP 500 response the until successful keeps retrying calling the web service. What is wrong with the failureExpression above?
Thanks
There is a lot of confusion around this expression. As per documentation,
FAILURE : "A message processor within the until-successful scope throws an exception or contains an exception payload. Also, if an expression is provided in the attribute failureExpression and it evaluates to true."
https://docs.mulesoft.com/mule-user-guide/v/3.6/until-successful-scope#success-and-failure
The catch here is that with the current implementation of Mule 'failureExpression' is checked and used when no exception is thrown. Otherwise it does always retry in case of exception. Solution for your problem would be to have a catch block for particular exception and then set a property, in failureExpression evaluate that property to retry in until successful. Basically, you would be using recursion technique kind of code for retrying.
Example for your code:
<until-successful maxRetries="10" failureExpression="#[flowVars['errorInActualOutboundFlow']]" synchronous="true" millisBetweenRetries="5000">
<flow-ref name="callActualOutboundFlow" doc:name="Flow Reference"/>
</until-successful>
Actual Outbound Flow:
<flow name="callActualOutboundFlow" processingStrategy="synchronous">
<http:request config-ref="HTTP_Request_Configuration" path="/" method="GET" doc:name="HTTP"/>
<choice-exception-strategy doc:name="Choice Exception Strategy">
<catch-exception-strategy doc:name="Catch Exception Strategy" when="#[exception.causedBy(java.net.ConnectException)]">
<logger message="#### Until-Successful will retry in this case " level="INFO" doc:name="Logger"/>
<set-variable variableName="errorInActualOutboundFlow" value="#[true]" doc:name="Variable"/>
</catch-exception-strategy>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<set-variable variableName="errorInActualOutboundFlow" value="#[false]" doc:name="Copy_of_Variable"/>
Here is how if fixed my issue. I created another flow that catches only the Web Service 500 error. The until -successful then does not retry to call the web service again.
<until-successful maxRetries="${webservice.timeout.max.retries}" failureExpression="#[exception != null && (exception.causedBy(java.net.ConnectException) || exception.causedBy(java.net.SocketTimeoutException) || exception.causedBy(java.util.concurrent.TimeoutException) || exception.causedBy(java.net.SocketException))]"
synchronous="true" millisBetweenRetries="5000" >
<processor-chain doc:name="Processor Chain">
<set-payload value="#[payLoad]" />
<flow-ref name="Flow1" />
</processor-chain>
</until-successful>
<flow name="Flow1">
<ws:consumer config-ref="WSConsumerConfig" operation="execute" />
<choice-exception-strategy doc:name="Choice Exception Strategy">
<catch-exception-strategy doc:name="Catch Exception Strategy" when="#[exception != null && exception.causedBy(org.mule.module.ws.consumer.SoapFaultException)]">
<logger message="SoapFaultException occurred." level="INFO" doc:name="Logger"/>
<set-payload value="#[exception]" doc:name="Set Payload"></set-payload>
</catch-exception-strategy>
</choice-exception-strategy>
</flow>
You also need to tell HTTP Request component that 500 is the Non-failure scenario in this case. Because By default 200 is the success scenario, other than anything needs to be mention in 'success-status-code-validator'.
<until-successful maxRetries="5" synchronous="true" doc:name="Until Successful" failureExpression="#[ message.inboundProperties['http.status'] != 200 && message.inboundProperties['http.status'] != 500 ]" millisBetweenRetries="1000">
<http:request config-ref="HTTP_Request_Configuration" path="test1" method="GET" doc:name="HTTP">
<http:success-status-code-validator values="200,500"/>.
</http:request>
</until-successful>
Instead of Directly using HTTP here, Believe you used callSubFlow there where you used HTTP-request, mention in <http:success-status-code-validator values="200,500"/> as success. So it don't retry in this case, works as expected.
If you want to handle 500 as separate logic other than 200, you can do condition check after http:request by checking its message.inboundProperties['http.status'] and can proceed with logic based on 200 or 500.
It is failed in your case, because Http-request saying '500' as failure and Until mentioned as Non-failure.
Related
i want to treat the case when connection with Camel HTTP component cannot be established
example:
<from uri="timer:tm?period=2000"/>
<to uri="https://URI"/>
when there is no connection i get this exception:
java.net.NoRouteToHostException: No route to host: connect
I want to handle this response and change it to an understandable message by the consumer. How can i do this ?
Camel provides a couple of very powerful error handling mechanisms with very useful redelivery and deadletter functionality. Those include:
Error Handlers: https://camel.apache.org/error-handler.html
Dead Letter Channels: https://camel.apache.org/dead-letter-channel.html
Try, Catch & Finally: https://camel.apache.org/try-catch-finally.html
Exception Clause: https://camel.apache.org/exception-clause.html
For your usecase, Exception Clause is the best fit. Redelivery would help you to retry the HTTP call and avoid failure due to a temporary downtime, e.g. a server restart:
<onException>
<exception>java.net.NoRouteToHostException</exception>
<!-- retry 3 times with a delay of 10 seconds -->
<redeliveryPolicy maximumRedeliveries="3" logStackTrace="true" redeliveryDelay="10000" />
<!-- only logs once redeliveries have failed -->
<to uri="log:classToLog?level=ERROR"/>
</onException>
You can handle exception using try..catch..finally.
http://camel.apache.org/try-catch-finally.html
Example in Spring DSL:
<route id="send_request">
<from uri="timer:tm?period=2000" />
<doTry>
<to uri="https://URI" />
<doCatch>
<exception>java.net.NoRouteToHostException</exception>
<handled>
<constant>true</constant>
</handled>
<log message="Some Message : ${exception.message}"
loggingLevel="WARN" />
</doCatch>
</doTry>
I am trying to write MUnit tests for my Mule flows. I want to write a test for the below flow
<flow name="DownloadFTPFileIntoLocalFlow" processingStrategy="synchronous" tracking:enable-default-events="true">
<quartz:inbound-endpoint jobName="Source-File-Scheduler" cronExpression="${source.pollingfrequency}" startDelay="10000" responseTimeout="10000" doc:name="Quartz">
<quartz:endpoint-polling-job>
<quartz:job-endpoint ref="InputSFTPEndpoint"/>
</quartz:endpoint-polling-job>
</quartz:inbound-endpoint>
<logger message="DownloadFTPFileIntoLocalFlow #[payload.getClass().getName()]" level="INFO" doc:name="Logger"/>
<set-property propertyName="MULE_CORRELATION_GROUP_SIZE" value="#[java.lang.Integer.MAX_VALUE]" doc:name="GroupsizeForExceptionAggregator"/>
<set-property propertyName="MULE_CORRELATION_ID" value="#[java.util.UUID.randomUUID().toString()]" doc:name="corelationIdForExceptionAggregator"/>
<set-variable variableName="originalPayload" value="#[payload]" doc:name="originalPayload"/>
<byte-array-to-object-transformer doc:name="Byte Array to Object"/>
<flow-ref name="ProcessCSVFlow" doc:name="ProcessCSVFlow" />
<exception-strategy ref="Default_Exception_Strategy" doc:name="DownloadFTPFileIntoLocalFlow Strategy"/>
</flow>
<sub-flow name="ProcessCSVFlow" tracking:enable-default-events="true">
<transformer ref="enrichWithHeaderAndEndOfFileTransformer" doc:name="headerAndEOFEnricher" />
<set-variable variableName="outputfilename" value="#['Mercury'+server.dateTime.year+server.dateTime.month+server.dateTime.dayOfMonth+server.dateTime.hours+server.dateTime.minutes+server.dateTime.seconds+'.csv']" doc:name="outputfilename"/>
<!-- <set-variable variableName="outputfilename" value="#['Mercury'+server.dateTime.year+':'+server.dateTime.month+':'+server.dateTime.dayOfMonth+'::'+server.dateTime.hours+':'+server.dateTime.minutes+':'+server.dateTime.seconds+'.csv']" doc:name="outputfilename"/> -->
<sftp:outbound-endpoint exchange-pattern="one-way" connector-ref="DestinationSFTP" host="${destination.host}" port="22" responseTimeout="10000" doc:name="DestinationSFTP"
outputPattern="#[outputfilename]" path="${destination.path}" user="${destination.username}" password="${destination.password}"/>
<gzip-compress-transformer/>
<sftp:outbound-endpoint exchange-pattern="one-way" connector-ref="InputSFTP" host="${source.host}" port="22" responseTimeout="10000" doc:name="SourceArchiveSFTP"
outputPattern="#[outputfilename].gzip" path="Archive" user="${source.username}" password="${source.password}"/>
<component doc:name="Delete Read File">
<singleton-object class="component.DeleteProcessedFileComponent">
<property key="host" value="${source.host}"/>
<property key="username" value="${source.username}"/>
<property key="password" value="${source.password}"/>
<property key="workingDirectory" value="${source.path}"/>
</singleton-object>
</component>
<parse-template location="successmessagetemplate.txt" doc:name="Success Template"/>
<smtp:outbound-endpoint host="${smtp.host}" port="${smtp.port}" user="${smtp.from.address}" password="${smtp.from.password}"
to="${smtp.to.address}" from="${smtp.from.address}" subject="${mail.success.subject}" responseTimeout="10000"
doc:name="SuccessEmail" connector-ref="Gmail"/>
<logger message="Process completed successfully" level="INFO" doc:name="Logger"/>
</sub-flow>
Exception handling block
<catch-exception-strategy name="Default_Exception_Strategy">
<flow-ref name="ExceptionHandlingSubflow" doc:name="ExceptionHandlingSubflow"/>
</catch-exception-strategy>
<sub-flow name="ExceptionHandlingSubflow" tracking:enable-default-events="true">
<collection-aggregator timeout="60000" failOnTimeout="false" doc:name="Exception Aggregator"/>
<logger message="Exception has occured Payload is #[payload] and Message is #[message]" level="ERROR" doc:name="Logger"/>
<parse-template location="errormessagetemplate.txt" doc:name="Error Template"/>
<smtp:outbound-endpoint host="${smtp.host}" port="${smtp.port}" user="${smtp.from.address}" password="${smtp.from.password}"
to="${smtp.to.address}" from="${smtp.from.address}" subject="${mail.failure.subject}" responseTimeout="10000"
doc:name="ErrorEmail" connector-ref="Gmail"/>
</sub-flow>
The interesting bit is the exception sub-flow, especially the collection-aggregator
My unit test is
#Test
public void whenMultipleExceptionsOccurInFlow_itShouldSendOnlyOneFailureEmail() throws Exception {
whenMessageProcessor("collection-aggregator")
.withAttributes(attribute("name").ofNamespace("doc").withValue("Exception Aggregator")).thenReturnSameEvent();
destinationSFTP.thenThrow(new RuntimeException("Dummy Exception destinationSFTP"));
MuleEvent testEvent = PropertyEnricher.enrich(testEvent(IOUtils.toInputStream("hello,dummy,payload"))).get();
runFlow("DownloadFTPFileIntoLocalFlow", testEvent);
verifyCallOfMessageProcessor("outbound-endpoint").ofNamespace("smtp")
.withAttributes(attribute("name").ofNamespace("doc").withValue("ErrorEmail"))
.times(1);
}
Now if I do not mock the collection aggregator out my test does not pass, I can understand that this is tricky as the aggregator has a "pause" within it and hence is not an ideal candidate for a unit test, however from a technical standpoint I want to understand what is causing the unit test to fail (when collection-aggregator is not mocked).
My test fails when the collection-aggregator is not mocked.
junit.framework.AssertionFailedError: On smtp:outbound-endpoint.Expected 1 but got 0 calls
at junit.framework.Assert.fail(Assert.java:50)
at org.mule.munit.common.mocking.MunitVerifier.times(MunitVerifier.java:86)
at nz.co.mightyriver.ProcessCsvTest.whenMultipleExceptionsOccurInFlow_itShouldSendOnlyOneFailureEmail(ProcessCsvTest.java:100)
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:606)
So I've been trying to reproduce this issue:
Production Code
<http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="9090" doc:name="HTTP Listener Configuration"/>
<flow name="stack-munit-and-aggregationFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<set-payload value="#['lalero_' + new java.util.Date().toString()]" doc:name="Set Payload"/>
<flow-ref name="stack-munit-and-aggregationSub_Flow" doc:name="stack-munit-and-aggregationSub_Flow"/>
<set-payload doc:name="Set Payload" value="#[payload.toString()]"/>
</flow>
<sub-flow name="stack-munit-and-aggregationSub_Flow">
<collection-aggregator failOnTimeout="true" doc:name="Collection Aggregator" timeout="10"/>
</sub-flow>
Test Code
package org.mule.munit;
import org.junit.Assert; import org.junit.Test; import org.mule.api.MuleEvent; import org.mule.api.MuleException; import org.mule.munit.runner.functional.FunctionalMunitSuite;
public class TheTest extends FunctionalMunitSuite {
#Test
public void aTest() throws MuleException, Exception { MuleEvent event = runFlow("stack-munit-and-aggregationFlow", testEvent(""));
String payload = (String) event.getMessage().getPayload();
Assert.assertTrue(payload.contains("lalero"));
} }
If you check this code you'll notice that I'm not mocking out the collection aggregator. After a few tests I wasn't able to reproduce your error.
I think the issue could somewhere else.
Could you please share you're code so I can investigate further?
World of warning though, due to an issue discovered in:
How to mock a Java component within Mule Flow using MUnit
You may find a problem if you try to directly test your sub-flow ExceptionHandlingSubFlow. But as you were not doing that in your example code I don't think those two are related.
Cheers!
I just have a little experience with Mule ESB 3.5, and I found that most of Mule examples only create SOAP Web Service with one parameter. For example, you can see that in SOAP Web Service Security example.
http://www.mulesoft.org/documentation/display/current/SOAP+Web+Service+Security+Example
So I have one question, acroding to above example, after using CHOICE flow control, how to pass multi parameter to method of SOAP web service.
Some suggests for me is to use object array to pass multi parameter, but I still have no clue at all.
Thanks to David. I just try your suggestion. But I think I should update my question to make it clearly.
Firstly, I create web service
#WebService
public interface Greeter
{
public String greet(String name);
public String welcome( String name1,String name2);
}
Then I have a control flow for web service configuration
<flow name="UnsecureServiceFlow" doc:name="UnsecureServiceFlow">
<http:inbound-endpoint address="http://localhost:63081/services/unsecure" exchange-pattern="request-response" doc:name="HTTP Inbound Endpoint"/>
<cxf:jaxws-service serviceClass="com.mulesoft.mule.example.security.Greeter" doc:name="Unsecure service"/>
<component class="com.mulesoft.mule.example.security.GreeterService" doc:name="Greeter Service" />
</flow>
Next is the sub flow using jax-ws client to call the method of web service
<flow name="SecurityClients" doc:name="SecurityClients">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="63080" path="client" doc:name="HTTP Inbound Endpoint"/>
<set-payload value="#[message.inboundProperties['http.query.params']['name']]" doc:name="Set payload with 'name' query param"/>
<set-variable variableName="clientType" value="#[message.inboundProperties['http.query.params']['clientType']]" doc:name="Set clientType"/>
<choice doc:name="Choice">
<when expression="#[clientType == 'unsecure']">
<flow-ref name="unsecure" doc:name="Invoke unsecure sub-flow"/>
</when>
<when expression="#[clientType == 'usernameToken']">
<flow-ref name="usernameToken" doc:name="Invoke usernameToken sub-flow"/>
</when>
<when expression="#[clientType == 'usernameTokenSigned']">
<flow-ref name="usernameTokenSigned" doc:name="Invoke usernameToken Signed sub-flow"/>
</when>
<when expression="#[clientType == 'usernameTokenEncrypted']">
<flow-ref name="usernameTokenEncrypted" doc:name="Invoke usernameToken Encrypted sub-flow"/>
</when>
<when expression="#[clientType == 'samlToken']">
<flow-ref name="samlToken" doc:name="Invoke samlToken sub-flow"/>
</when>
<when expression="#[clientType == 'samlTokenSigned']">
<flow-ref name="samlTokenSigned" doc:name="Invoke samlToken Signed sub-flow"/>
</when>
<otherwise>
<set-payload value="Client type is not supported" doc:name="Client type is not supported"/>
</otherwise>
</choice>
<set-property propertyName="Content-Type" value="text/plain" doc:name="Set response Content-Type"/>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<set-payload value="There has been an Error processing the request" doc:name="Set Payload"/>
<set-property propertyName="Content-Type" value="text/plain" doc:name="Set response Content-Type"/>
</catch-exception-strategy>
</flow>
<sub-flow name="unsecure" doc:name="unsecure">
<cxf:jaxws-client operation="greet" serviceClass="com.mulesoft.mule.example.security.Greeter" doc:name="Unsecure SOAP client" doc:description="Unsecure SOAP client"/>
<http:outbound-endpoint exchange-pattern="request-response" host="localhost" port="63081" path="services/unsecure" doc:name="Invoke unsecure Web Service"/>
</sub-flow>
It's ok to use that address to invoke greet method, it only has one parameter.
localhost:63080/client?clientType=usernameToken&name=John
However when I change greet method to welcome method, I do not know how to pass more parameter to it or have to change anything , because payload only contains name parameter
Generate the JAX-WS client classes from the remote web service WSDL and use them in a cxf:jaxws-client configuration element.
In your case, you need to set-payload inside each when in order to create the request object that is needed by cxf:jaxws-client.
Suppose that you need to create a org.saml.SamlToken object for the samlToken case, you would do:
<set-payload value="#[st=new org.saml.SamlToken();st.field1=message.inboundProperties.field1; ... ; st]" />
in the when right before flow-ref.
PS. You can use #[message.inboundProperties.clientType] instead of #[message.inboundProperties['http.query.params']['clientType']]
I am trying to consume a webservice produced on by following the tutorials in the mule Documentation. have been able to build the webservice successfully, but having issues consuming it. I have two Java Clasess "HelloWorld" and "HelloWorldImpl". This is my flow
<flow name="helloService" doc:name="helloService">
<http:inbound-endpoint address="http://localhost:63081/hello" exchange-pattern="request-response" doc:name="HTTP">
<cxf:jaxws-service serviceClass="com.test.HelloWorld"/>
</http:inbound-endpoint>
<component class="com.test.HelloWorldImpl" doc:name="Java"/>
<cxf:jaxws-client serviceClass="com.test.HelloWorld" operation="sayHi" doc:name="SOAP" />
<outbound-endpoint address="http://localhost:63081/services/greeter" doc:name="Generic"/>
</flow>
What am I doing wrong?
When I access the outbound endpoint I get
Cannot bind to address "http://activate.adobe.com:63081/services/greeter" No component registered on that endpoint
You have to make your endpoint accept all sub-paths and then handle wrong ones with message routing:
Example:
<flow name="jfeed_fill_data">
<http:inbound-endpoint address="http://localhost:1212" />
<choice>
<when evaluator="header" expression="INBOUND:http.request.path=/jcore/insert/feed/">
<component class="main.java.com.joshlabs.jcore.Feed"/>
</when>
<otherwise>
<message-properties-transformer>
<add-message-property key="http.status" value="404"/>
</message-properties-transformer>
<expression-transformer>
<return-argument evaluator="string" expression="{Exception: "Invalid URL"}"/>
</expression-transformer>
</otherwise>
</choice>
</flow>
First issue: How can there be two services running on the same port (63081) on your localhost.
http://localhost:63081/hello
http://localhost:63081/services/greeter
Also As mentioned in your post, the web-service you have created is Hello service with the endpoint
http://localhost:63081/hello
So you web sevice should be as follows.
<flow name="helloService" doc:name="helloService">
<http:inbound-endpoint address="http://localhost:63081/hello" exchange-pattern="request-response" doc:name="HTTP">
<cxf:jaxws-service serviceClass="com.test.HelloWorld"/>
</http:inbound-endpoint>
<component class="com.test.HelloWorldImpl" doc:name="Java"/>
</flow>
In order to consume you can write another flow which has got the cxf:jaxws-client
<flow name="helloclient" >
<some inbound endpoint. >
....
<cxf:jaxws-client serviceClass="com.test.HelloWorld" operation="sayHi" doc:name="SOAP" />
<outbound-endpoint address="http://localhost:63081/hello" doc:name="Generic"/>
.....
</flow>
Hope this helps.
Your inbound endpoint is http://localhost:63081/hello which is the address you should call to consume your webservice.
Also your outbound endpoint seems to point to a address where there is no webservice to consume. Unless you have a second flow in your mule config that you do not show.
You've defined a flow which has a listener on service end-point http://localhost:63081/hello. In this flow request comes in and then it is forwarded using jaxws-client to another service listening at http://localhost:63081/services/greeter.
Now the error message says Cannot bind to address which means it cannot call the end-point. Is there a service running anywhere at the end-point you're trying to send request to? If you want to send request locally as look like from your flow, then you need another flow listening at that end-point similar to one you have but with different http-endpoint
I'am using Mule Community Edition 3.4.
I have a problem with the UntilSuccessful component. The scenario is now exposed:
I have a flow composed by a UntilSuccessful component in which there's a SOAP component that makes a request to a Web Service. In this flow there is an ExcpetionStrategy, too. The problem that I have is that when an exception occurs inside the UntilSuccessful (i.e in the SOAP component) the ExcpetionStrategy is not able to handle it because it (the Exception thrown) is handled by some mechanism inside the UntilSuccessful component.
Because I need to handle the Exception in the ExcpetionStrategy, I thought to build a custom outbound interceptor (inside the SOAP component) that intercept the SOAP response (an exception if it's thrown) and that is able to throw an Exception in order to trigger the ExcpetionStrategy.
Could anyone help me with this problem? I tried to read the documentation but it is sparse and does not explain very well how to build a custom outbound exception.
What I would to do is to save somewhere the name of Exception thrown (i.e if server thrown a NumberFormatException, I would save its name somewhere in order to use it in the ExceptionStrategy)
Below you can see a snippet of mule configuration file:
<flow name="ProvaClient" doc:name="ProvaClient">
<quartz:inbound-endpoint jobName="TalendJob" repeatInterval="5000" repeatCount="0" responseTimeout="10000" doc:name="Quartz">
<quartz:event-generator-job>
<quartz:payload>error</quartz:payload>
</quartz:event-generator-job>
</quartz:inbound-endpoint>
<object-to-string-transformer doc:name="Object to String"/>
<until-successful objectStore-ref="OS_Bean" maxRetries="2" secondsBetweenRetries="2" doc:name="Until Successful" deadLetterQueue-ref="myQueue">
<processor-chain doc:name="Processor Chain: Wait For Web Service Response">
<processor-chain doc:name="Processor Chain: Web Service">
<cxf:jaxws-client operation="getCode" clientClass="it.aizoon.prova.client.ProvaService" port="ProvaPort" enableMuleSoapHeaders="true" doc:name="SOAP">
</cxf:jaxws-client>
<http:outbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="service/prova" method="POST" doc:name="HTTP"/>
</processor-chain>
<logger message="PAYLOAD: #[payload]" level="INFO" doc:name="Logger"/>
</processor-chain>
</until-successful>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<!-- <processor ref="myExceptionHandler_id"/> -->
<logger message="EXCEPTION STRATEGY" level="INFO" doc:name="Logger"/>
</catch-exception-strategy>
</flow>
Here you can see the server which exposes a web service:
<flow name="provaServer" doc:name="provaServer">
<http:inbound-endpoint exchange-pattern="request-response" doc:name="HTTP" host="localhost" path="service/prova" port="8081"/>
<logger message="SERVER" level="INFO" doc:name="Logger"/>
<cxf:jaxws-service serviceClass="it.aizoon.prova.Prova" doc:name="Process SOAP Request" />
<component class="it.aizoon.prova.ProvaImpl" doc:name="Java"/>
</flow>
And here there is the ProvaImpl.java, the implementation of Web Service. How you can see, if the string passed as argument in getCode() function is error, an exception in thrown and I would that it is managed by the exception strategy defined in the client
#WebService(endpointInterface = "it.aizoon.prova.Prova",
serviceName = "Prova")
public class ProvaImpl implements Prova{
#Override
public String getCode(String code) throws NumberFormatException{
// TODO Auto-generated method stub
if(code.equals("error")) throw new NumberFormatException();
String str = "Andato a buon fine!";
return str;
}
}
I would change the approach rather than using an interceptor. If you need to invoke the exception strategy wihout triggering the until-succesful router first, I would move your cxf:jaxws-client etc. to a private flow. To quote Mule in Action 2nd edition on private flows:
This decoupling allows defining processing and error handling
strategies that are local to the private flow.
<flow name="ProvaClient" doc:name="ProvaClient">
...
<until-successful objectStore-ref="OS_Bean"
maxRetries="2" secondsBetweenRetries="2" doc:name="Until Successful"
deadLetterQueue-ref="myQueue">
<processor-chain doc:name="Processor Chain: Wait For Web Service Response">
<processor-chain doc:name="Processor Chain: Web Service">
<flow-ref name="externalCallFlow" />
</processor-chain>
<logger message="PAYLOAD: #[payload]" level="INFO" doc:name="Logger" />
</processor-chain>
</until-successful>
...
</flow>
<flow name="externalCallFlow">
<cxf:jaxws-client operation="getCode"
clientClass="it.aizoon.prova.client.ProvaService" port="ProvaPort"
enableMuleSoapHeaders="true" doc:name="SOAP">
</cxf:jaxws-client>
<http:outbound-endpoint exchange-pattern="request-response"
host="localhost" port="8081" path="service/prova" method="POST"
doc:name="HTTP" />
<catch-exception-strategy doc:name="Catch Exception Strategy">
<!-- Handle exception here locally and return custom exception or error
message for the unil-successful router -->
</catch-exception-strategy>
</flow>
You can then handle exceptions locally and return a custom exception or error
message for the until-successful router to catch using the following attribute: failureExpression="exception-type:java.lang.NumberFormatException"
Here's a dummy example I knocked up to throw a NumberFormatException, log the exception in the exception strategy and retry:
<flow name="test" doc:name="test">
<http:inbound-endpoint address="http://localhost:8081/test"
doc:name="HTTP" />
<until-successful objectStore-ref="OS_Bean"
maxRetries="2" secondsBetweenRetries="2" doc:name="Until Successful">
<processor-chain doc:name="Processor Chain: Wait For Web Service Response">
<processor-chain doc:name="Processor Chain: Web Service">
<flow-ref name="externalCallFlow" doc:name="Flow Reference" />
</processor-chain>
</processor-chain>
</until-successful>
</flow>
<flow name="externalCallFlow" doc:name="externalCallFlow">
<scripting:component>
<scripting:script engine="groovy">
throw new java.lang.NumberFormatException();
</scripting:script>
</scripting:component>
<default-exception-strategy>
<processor-chain>
<logger level="ERROR"
message="NumberFormatException Occurred : #[message.payload.getException().getCause()]" />
<scripting:component>
<scripting:script engine="groovy">
throw message.payload.getException().getCause();
</scripting:script>
</scripting:component>
</processor-chain>
</default-exception-strategy>
</flow>