I have a scenario where in I need to call the service until the http status of the webservice call is 200.
The subflow(SearchSubFlow) works fine when it is not in Until Successful but when it is inside Until Sucessful it falls over with the below error at Byte Array to String in SearchSubFlow.
org.mule.api.transformer.TransformerMessagingException: null (java.lang.NullPointerException). Message payload is of type: WeaveMessageProcessor$WeaveOutputHandler.
<flow name="CheckFlow">
<http:listener config-ref="HTTP_Internal_Listener" path="${CONNECTION_URI}" doc:name="HTTP"/>
<cxf:proxy-service payload="envelope" doc:name="CXF" namespace="http://abc/RetrieveService" service="RetrieveService" soapVersion="1.2" wsdlLocation="${WSDL_RETRIEVE}"/>
<mulexml:dom-to-xml-transformer doc:name="DOM to XML"/>
<set-variable variableName="Ref" value="#[xpath3('//*:CheckRequest/*:Ref')]" doc:name="Get External Ref Number"/>
<until-successful objectStore-ref="objectStore" maxRetries="${RESEND_COUNTER}" millisBetweenRetries="${RESEND_INTERVAL}" doc:name="Retry to check policy status" >
<flow-ref name="SearchSubFlow" doc:name="SearchSubFlow"/>
</until-successful>
</flow>
<sub-flow name="SearchSubFlow">
<dw:transform-message doc:name="PolicySearchWS Request">
<dw:set-payload><![CDATA[%dw 1.0
%output application/xml
%namespace soapenv http://schemas.xmlsoap.org/soap/envelope/
---
{
soapenv#Envelope: {
soapenv#Header: {
InsureJHeader:{
role: "${HEADER_ROLE}"
}
},
soapenv#Body: {
arguments:{
externalQuoteReference: flowVars.Ref
}
}
}
}]]></dw:set-payload>
</dw:transform-message>
<byte-array-to-string-transformer doc:name="Byte Array to String"/>
<message-properties-transformer scope="invocation" doc:name="Set Service Endpoint">
<add-message-property key="serviceEndPoint" value="${APP_SYSTEM}/${SELECT_SERVICE_BASE_URL}/${.CREATE.ADDRESS}"/>
</message-properties-transformer>
<message-properties-transformer doc:name="Set SOAP Action">
<add-message-property key="SOAPAction" value="${CREATE.SOAPACTION}"/>
</message-properties-transformer>
<cxf:proxy-client payload="envelope" doc:name="Proxy"/>
<http:request config-ref="HTTP_Select_Request" path="#[serviceEndPoint]" method="POST" doc:name="Call Search WS">
<http:success-status-code-validator values="200,500"/>
</http:request>
<mulexml:dom-to-xml-transformer doc:name="DOM to XML"/>
<message-properties-transformer scope="invocation" doc:name="Message Properties">
<add-message-property key="PolicySearchErrorMessage" value="#[xpath3('//*:Fault/*:ProcessingFault/*:message')]"/>
<add-message-property key="httpStatus" value="#[message.inboundProperties.'http.status']"/>
</message-properties-transformer>
</sub-flow>
Highly appreciate your help!
Not referencing to the objectstore as below resolved the issue
<until-successful maxRetries=5 doc:name="Retry to check status" >
<flow-ref name="SearchSubFlow" doc:name="SearchSubFlow"/>
</until-successful>
use <mulexml:dom-to-xml-transformer doc:name="DOM to XML"/> instead of <byte-array-to-string-transformer doc:name="Byte Array to String"/>
Hope this helps.
Related
I have this mule flow , where its polling the source folder to read the text file which I am adding as a attachment and sending through REST call , the same attachment I am trying to read in the different flow but inbound attachment is coming as null , please have a look into the code and help me on this.
<flow name="createAttachment" doc:description="Reading file and sending as attachment.">
<file:inbound-endpoint path="src/test/resources/in/attachment/" responseTimeout="10000" doc:name="File"/>
<file:file-to-byte-array-transformer doc:name="File to Byte Array"/>
<!-- <set-attachment attachmentName="#[originalFilename]" value="#[payload]" contentType="multipart/form-data" doc:name="Attachment"/> -->
<set-attachment attachmentName="#[originalFilename]" value="#[payload]" contentType="multipart/form-data" doc:name="Attachment" />
<http:request config-ref="HTTP_Request_Configuration" path="attachment/excel" method="POST" doc:name="HTTP"/>
</flow>
<flow name="readAttachment">
<http:listener config-ref="HTTP_Listener_Configuration" path="attachment/excel" allowedMethods="POST" parseRequest="false" />
<set-payload value="#[message.inboundAttachments['myattachment.txt']]" doc:name="Retrieve Attachments"/>
<set-payload value="#[payload.getInputStream() ]" doc:name="Get Inputstream from Payload"/>
<file:outbound-endpoint path="src/test/resources/out/attachment" responseTimeout="10000" doc:name="File" outputPattern="#[server.dateTime.toString()].pdf"/>
</flow>
I used the following:
<flow name="readAttachment">
<http:listener config-ref="HTTP_Listener_Configuration"
path="/" allowedMethods="POST" parseRequest="false" doc:name="HTTP" />
<byte-array-to-string-transformer doc:name="Byte Array to String"/>
<logger message="#[payload]" level="INFO" doc:name="Logger" /><file:outbound-endpoint path="src/test/resources" connector-ref="File" responseTimeout="10000" doc:name="File"/>
</flow>
When the attachment was received it was automatically parsed to be the payload, so it was just a case of turning the byte array to string.
I hope this helps
I need to query several tables and at the end of the scatter-gather flow I want to have a payload of this form
{table_1: [ list of results ], table_2: [ list of results ]}
I tried this:
<scatter-gather doc:name="Scatter-Gather">
<processor-chain>
<db:select config-ref="dataSrouce" doc:name="Table 1">
<db:parameterized-query><![CDATA[// my query
</db:select>
<set-payload value="#[['table_1': #[payload]]]" doc:name="Set Payload"/>
</processor-chain>
<processor-chain>
<db:select config-ref="dataSrouce" doc:name="Table 2">
<db:parameterized-query><![CDATA[// my query
</db:select>
<set-payload value="#[['table_2': #[payload]]]" doc:name="Set Payload"/>
</processor-chain>
</scatter-gather>
<set-payload value="#[groovy:payload.inject([:]) {result, entry -> result += entry; result}]" doc:name="Set Payload"/>
But getting an exception:
Exception was found for route(s): 1. Message payload is of type: CaseInsensitiveHashMap (org.mule.routing.CompositeRouting
Exception)
org.mule.routing.CollectAllAggregationStrategy:51 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/routing/Comp
ositeRoutingException.html)
In each branch of your Scatter-Gather place an ObjectToString and an ObjectToJSON Transformer.
Then after the Scatter-Gather place a SetPayload with the following value:
{
"table_1": #[payload[0]],
"table_2": #[payload[1]]
}
Example:
<scatter-gather doc:name="Scatter-Gather">
<processor-chain>
<flow-ref name="Subflow_1" doc:name="Subflow_1"/>
<object-to-string-transformer doc:name="Object to String"/>
<json:object-to-json-transformer doc:name="Object to JSON"/>
</processor-chain>
<processor-chain>
<flow-ref name="Subflow_2" doc:name="Subflow_2"/>
<object-to-string-transformer doc:name="Object to String"/>
<json:object-to-json-transformer doc:name="Object to JSON"/>
</processor-chain>
</scatter-gather>
<set-payload value="{
"table_1": #[payload[0]],
"table_2": #[payload[1]]}"
doc:name="Set Payload" mimeType="application/json"/>
<flow name="listobjects">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="listobjects" contentType="text/plain" doc:name="HTTP"/>
<s3:list-objects config-ref="Amazon_S3" bucketName="demo" doc:name="Amazon S3" maxKeys="5" />
<!-- <payload-type-filter expectedType="java.util.List" doc:name="Payload"/> -->
<foreach collection="#[payload]" doc:name="For Each">
<!-- <foreach doc:name="For Each file"> -->
<logger message=" inside foreach...... #[payload.getKey()] ...." level="INFO" doc:name="Logger" />
<s3:get-object-content config-ref="Amazon_S3" bucketName="demo" key="#[payload.getKey()]" doc:name="Amazon S3"/>
<object-to-byte-array-transformer/>
<file:outbound-endpoint path="C:\output" responseTimeout="10000" doc:name="File" outputPattern="#[payload.getKey()] "></file:outbound-endpoint>
</foreach>
</flow>
I have bucket name called demo.
In that bucket I have 3 pdf files. I want to download all files and put it in c:\output folder.
I hit my url like http://localhost:8081/listobjects.
But I got the error:
Could not find a transformer to transform "CollectionDataType{type=org.mule.module.s3.simpleapi.SimpleAmazonS3AmazonDevKitImpl$S3ObjectSummaryIterable, itemType=com.amazonaws.services.s3.model.S3ObjectSummary, mimeType='/'}" to "SimpleDataType{type=org.mule.api.transport.OutputHandler, mimeType='/'}". (org.mule.api.transformer.TransformerException) (org.mule.api.transformer.TransformerException). Message payload is of type: SimpleAmazonS3AmazonDevKitImpl$S3ObjectSummaryIterable
The error occurs because after the foreach processor the payload is an instance of an S3 class, and you haven't specified any Content-Type to return. So Mule tries to transform the S3 instance to the default SimpleDataType and fails.
One way to solve it is simply to add something like
<set-property propertyName="Content-Type" value="application/json" doc:name="Content-Type" />
<set-payload value="{'result': 'ok'}"/>
at the end to make it explicit.
Also note that in your flow after running:
<object-to-byte-array-transformer/>
the S3 payload is gone, so #[payload.getKey()] will fail in the next processor:
<file:outbound-endpoint path="C:\output" responseTimeout="10000" doc:name="File" outputPattern="#[payload.getKey()] "></file:outbound-endpoint>
I've run this without problems:
<flow name="listobjects">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8083" path="listobjects" contentType="text/plain" doc:name="HTTP"/>
<s3:list-objects config-ref="Amazon_S3" bucketName="mule_test" doc:name="Amazon S3" maxKeys="5" />
<foreach collection="#[payload]" doc:name="For Each">
<logger message=" inside foreach...... #[payload.getKey()] ...." level="INFO" doc:name="Logger" />
<set-variable variableName="fileKey" value="#[payload.getKey()]" doc:name="Variable" />
<s3:get-object-content config-ref="Amazon_S3" bucketName="#[payload.getBucketName()]" key="#[payload.getKey()]" doc:name="Amazon S3"/>
<object-to-byte-array-transformer/>
<file:outbound-endpoint path="/tmp" responseTimeout="10000" doc:name="File" outputPattern="#[flowVars.fileKey] "></file:outbound-endpoint>
</foreach>
<set-property propertyName="Content-Type" value="application/json" doc:name="Content-Type" />
<set-payload value="{'result': 'ok'}"/>
</flow>
How can I get Mule to download just attachments using pop3? I tried following the example at http://www.mulesoft.org/documentation/display/current/POP3+Transport+Reference as closely as possible, but I keep getting two files: one with the e-mail body and one with the actual attachment. Here's the flow I'm using:
<pop3:connector name="pop3Connector" checkFrequency="5000" doc:name="POP3"/>
<expression-transformer name="returnAttachments" doc:name="Expression">
<return-argument evaluator="attachments-list" expression="*" />
</expression-transformer>
<file:connector name="fileName" doc:name="File">
<file:expression-filename-parser/>
</file:connector>
<flow name="incoming-orders" doc:name="incoming-orders">
<pop3s:inbound-endpoint host="pop.gmail.com" port="995" user="myuser%40mydomain" password="mypassword" responseTimeout="10000" doc:name="POP3" transformer-refs="returnAttachments" />
<collection-splitter doc:name="Collection Splitter"/>
<file:outbound-endpoint path="C:/popthreetest" outputPattern="#[function:datestamp].dat" doc:name="File">
<expression-transformer>
<return-argument expression="payload.inputStream" evaluator="groovy" />
</expression-transformer>
</file:outbound-endpoint>
</flow>
Thanks!
edit:
Here's the final flow based on #David Dossot's answer. I have an added complexity in that I'm reading in a JSON file that specifies attachment names and an arbitrary destinations for the attachment. I included the replaceAll because I was getting an error about an invalid character in the path file:///C:\.
<pop3:connector name="pop3Connector" checkFrequency="5000" doc:name="POP3"/>
<expression-transformer name="returnAttachments" doc:name="Expression">
<return-argument evaluator="attachments-list" expression="*" />
</expression-transformer>
<file:connector name="DestinationsFileConnector" doc:name="File" autoDelete="false" streaming="true" validateConnections="true">
<file:expression-filename-parser/>
</file:connector>
<file:endpoint path="C:/popthreetest/" name="DestinationsFileEndpoint" responseTimeout="10000" doc:name="File" connector-ref="DestinationsFileConnector">
<file:filename-regex-filter pattern="destinations\.json" caseSensitive="true"/>
</file:endpoint>
<mulerequester:config name="DestinationsMuleRequestorConnector" doc:name="Mule Requester"/>
<flow name="incoming-orders" doc:name="incoming-orders">
<pop3s:inbound-endpoint host="pop.gmail.com" port="995" user="myusername%40mydomain" password="mypassword" responseTimeout="10000" doc:name="POP3" transformer-refs="returnAttachments" />
<collection-splitter doc:name="Collection Splitter"/>
<set-variable variableName="MessagePart" value="#[message.payload]" doc:name="MessagePart"/>
<logger message="Got #[message.payload.dataSource.name]." level="INFO" doc:name="Logger"/>
<mulerequester:request config-ref="DestinationsMuleRequestorConnector" resource="DestinationsFileEndpoint" doc:name="GetDestinations"/>
<json:json-to-object-transformer returnClass="java.util.HashMap" doc:name="JSON to Object"/>
<choice doc:name="Choice">
<when expression="#[message.payload.get(MessagePart.dataSource.name) != null]">
<set-payload value="#[message.payload.get(MessagePart.dataSource.name)]" doc:name="Destination List"/>
<foreach doc:name="For Each">
<logger message="Saving to #[message.payload]." level="INFO" doc:name="Logger"/>
<set-variable variableName="DestinationPath" value="#[java.nio.file.Paths.get(message.payload).getParent().toString().replaceAll('\\\\', '/')]" doc:name="DestinationPath"/>
<set-variable variableName="DestinationPattern" value="#[java.nio.file.Paths.get(message.payload).getFileName()]" doc:name="DestinationPattern"/>
<logger message="Saving to #[DestinationPattern] in #[DestinationPath]." level="INFO" doc:name="Logger"/>
<set-payload value="#[MessagePart]" doc:name="MessagePart"/>
<file:outbound-endpoint path="#[DestinationPath]" outputPattern="#[DestinationPattern]" doc:name="File">
<expression-transformer>
<return-argument expression="payload.inputStream" evaluator="groovy"/>
</expression-transformer>
</file:outbound-endpoint>
</foreach>
</when>
<otherwise>
<logger message="Did not find destination(s) for #[MessagePart.dataSource.name]." level="INFO" doc:name="Logger"/>
</otherwise>
</choice>
</flow>
For completeness, here's the JSON file:
{
"attachment-name.txt": [
"C:/popthreetest/firstDestination.txt"
, "C:/path/to/secondDestination.txt"
, "C:\\popthreetest\\destination\\using-backslashes.txt"
]
}
An email with attachment is a multi-part email into which attachments are parts but also the body. Hence Mule can only download "the whole package" and give the different parts to you.
You should be able to filter the body part after the collection-splitter based on the name of the part.
Alternatively, you could use a MEL expression to drop the first element of the collection, which is typically the body (Mule uses this technique internally to set the message payload: https://github.com/mulesoft/mule/blob/mule-3.x/transports/email/src/main/java/org/mule/transport/email/transformers/EmailMessageToString.java#L50 )
I'm currently writing a rather simple mule-flow that uses basic authentication
<custom-transformer class="com.org.HttpRequestToMyRequest" name="HttpRequestToMyRequest" doc:name="Java"/>
<flow name="Input_Flow" doc:name="Input_Flow">
<http:inbound-endpoint exchange-pattern="request-response" host="http://12.23.34.45" port="1234" path="/foo">
<mule-ss:http-security-filter realm="mule-realm"/>
</http:inbound-endpoint>
<flow-ref name="Sorting_Flow" doc:name="Sorting flow"/>
</flow>
<sub-flow name="Sorting_Flow" doc:name="Sorting_Flow">
<transformer ref="HttpRequestToMyRequest" doc:name="Transform Http Request to POJO with interesting methods"/>
<enricher target="#[variable:authorized]">
<http:outbound-endpoint exchange-pattern="request-response" host="12.34.45.56" port="1234" path="foo/bar" method="GET" disableTransportTransformer="true"
responseTransformer-refs="ObjectToString" doc:name="Authorization Service"/>
</enricher>
<choice doc:name="Choice">
<when expression="header:INVOCATION:authorized=true">
<processor-chain>
<component class="com.org.MyClass"/>
<transformer ref="MyObjToString"/>
<http:response-builder status="200" contentType="text/xml" doc:name="HTTP Response Builder"/>
</processor-chain>
</when>
<when SOMETHING ELSE>
DO SOMETHING ELSE
</when>
</choice>
</sub-flow>
If I remove the http-security-filter then this flow works as expected and the payload in the MuleMessage is of the type MyRequest returned by the HttpRequestToMyRequest transformer. But once i add the security-filter the enricher changes my payload from the expected type to a byte[]. If i convert the byte[] to a String then it turns out to be the instancename of MyRequest (com.org.MyRequest#15ae9009).
Any help or ideas would be appreciated.
Edit
Just to be clear. Here are three flow xml fragments where two behave well and the last one has the strange behaviour i don't understand.
This flow, where I've commented out the security-filter on the inbound endpoint, works as expected and the ClassNameLogger statements prints out, in order, String, MyRequest, MyRequest.
<custom-transformer class="com.org.HttpRequestToMyRequest" name="HttpRequestToMyRequest" doc:name="Java"/>
<custom-transformer class="com.org.ClassNameLogger" name="ClassNameLogger" doc:name="Java"/>
<flow name="Input_Flow" doc:name="Input_Flow">
<http:inbound-endpoint exchange-pattern="request-response" host="http://12.23.34.45" port="1234" path="/foo">
<!-- <mule-ss:http-security-filter realm="mule-realm"/> -->
</http:inbound-endpoint>
<flow-ref name="Sorting_Flow" doc:name="Sorting flow"/>
</flow>
<sub-flow name="Sorting_Flow" doc:name="Sorting_Flow">
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<transformer ref="HttpRequestToMyRequest" doc:name="Transform Http Request to POJO with interesting methods"/>
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<enricher target="#[variable:authorized]">
<http:outbound-endpoint exchange-pattern="request-response" host="12.34.45.56" port="1234" path="foo/bar" method="GET" disableTransportTransformer="true"
responseTransformer-refs="ObjectToString" doc:name="Authorization Service"/>
</enricher>
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<choice doc:name="Choice">
<when expression="header:INVOCATION:authorized=true">
<processor-chain>
<component class="com.org.MyClass"/>
<transformer ref="MyObjToString"/>
<http:response-builder status="200" contentType="text/xml" doc:name="HTTP Response Builder"/>
</processor-chain>
</when>
<when SOMETHING ELSE>
DO SOMETHING ELSE
</when>
</choice>
</sub-flow>
This flow, where I've commented out the enricher, works as expected, aside from the fact that it doesn't enter the choise since the variable set by the enricher is not defined. The ClassNameLogger statements prints out, in order, String, MyRequest, MyRequest.
<custom-transformer class="com.org.HttpRequestToMyRequest" name="HttpRequestToMyRequest" doc:name="Java"/>
<custom-transformer class="com.org.ClassNameLogger" name="ClassNameLogger" doc:name="Java"/>
<flow name="Input_Flow" doc:name="Input_Flow">
<http:inbound-endpoint exchange-pattern="request-response" host="http://12.23.34.45" port="1234" path="/foo">
<mule-ss:http-security-filter realm="mule-realm"/>
</http:inbound-endpoint>
<flow-ref name="Sorting_Flow" doc:name="Sorting flow"/>
</flow>
<sub-flow name="Sorting_Flow" doc:name="Sorting_Flow">
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<transformer ref="HttpRequestToMyRequest" doc:name="Transform Http Request to POJO with interesting methods"/>
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<!-- <enricher target="#[variable:authorized]">
<http:outbound-endpoint exchange-pattern="request-response" host="12.34.45.56" port="1234" path="foo/bar" method="GET" disableTransportTransformer="true"
responseTransformer-refs="ObjectToString" doc:name="Authorization Service"/>
</enricher> -->
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<choice doc:name="Choice">
<when expression="header:INVOCATION:authorized=true">
<processor-chain>
<component class="com.org.MyClass"/>
<transformer ref="MyObjToString"/>
<http:response-builder status="200" contentType="text/xml" doc:name="HTTP Response Builder"/>
</processor-chain>
</when>
<when SOMETHING ELSE>
DO SOMETHING ELSE
</when>
</choice>
</sub-flow>
This flow, where I have both the enricher and the security-filter in place, doesn't work as expected and the ClassNameLogger statements prints out, in order, String, MyRequest, byte[].
<custom-transformer class="com.org.HttpRequestToMyRequest" name="HttpRequestToMyRequest" doc:name="Java"/>
<custom-transformer class="com.org.ClassNameLogger" name="ClassNameLogger" doc:name="Java"/>
<flow name="Input_Flow" doc:name="Input_Flow">
<http:inbound-endpoint exchange-pattern="request-response" host="http://12.23.34.45" port="1234" path="/foo">
<mule-ss:http-security-filter realm="mule-realm"/>
</http:inbound-endpoint>
<flow-ref name="Sorting_Flow" doc:name="Sorting flow"/>
</flow>
<sub-flow name="Sorting_Flow" doc:name="Sorting_Flow">
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<transformer ref="HttpRequestToMyRequest" doc:name="Transform Http Request to POJO with interesting methods"/>
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<enricher target="#[variable:authorized]">
<http:outbound-endpoint exchange-pattern="request-response" host="12.34.45.56" port="1234" path="foo/bar" method="GET" disableTransportTransformer="true"
responseTransformer-refs="ObjectToString" doc:name="Authorization Service"/>
</enricher>
<transformer ref="ClassNameLogger" doc:name="Logs The Class Name of MuleMessage payload"/>
<choice doc:name="Choice">
<when expression="header:INVOCATION:authorized=true">
<processor-chain>
<component class="com.org.MyClass"/>
<transformer ref="MyObjToString"/>
<http:response-builder status="200" contentType="text/xml" doc:name="HTTP Response Builder"/>
</processor-chain>
</when>
<when SOMETHING ELSE>
DO SOMETHING ELSE
</when>
</choice>
</sub-flow>
Edit 2
The code in the HttpRequestToMyRequest transformer
public class HttpRequestToMyRequest extends AbstractMessageTransformer{
#Override
public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException {
if("/foo".equals(message.getInboundProperty("http.request.path")){
return new MyFooRequest();
}
if("/bar".equals(message.getInboundParameter("http.request.path")){
return new MyBarRequest();
}
else return new MyEmptyRequest();
}
}
Edit 3
Apparently this was something Mule 3.3 specific. Once I upgraded to 3.4 everything started working as expected.
With Mule 3.4, I notice no difference of payload type with or without the http-security-filter element.
It is either a java.lang.String if the request is an HTTP GET or java.io.InputStream if it is a POST or PUT.
Since you are on 3.3, you are possibly hitting a bug that is gone in 3.4, since I don't have any issue. If that is the case, finding a workaround for the issue on 3.3 would be hairy: upgrading to 3.4 is the path forward.