I would like to ask how to add file as attachment in Mule (3.4.0)?
I tried many solutions and googled a lot but haven't found anything good.
This is what I have now (last try before posting here):
<sub-flow name="sendBackMail" doc:name="sendBackMail">
<set-attachment attachmentName="changed.xml" value="#[payload]" contentType="text/xml" doc:name="Attachment"/>
<logger message="Attachment ok" level="INFO" doc:name="Logger"/>
<file:file-to-byte-array-transformer doc:name="File to Byte Array"/>
<logger message="Attachment ok. Message: #[message]" level="INFO" doc:name="Logger"/>
<smtps:outbound-endpoint host="${mailSMTP}" port="${mailSendPort}"
user="${mailUser}" password="${mailPass}" to="${receiver}"
from="${mailUser}" responseTimeout="60000" doc:name="SMTP"
connector-ref="SMTP" mimeType="text/xml" subject="msp2bass" >
</smtps:outbound-endpoint>
</sub-flow>
It sends mail but I get content of xml file as body of mail.
What should I do/change so that mail would be sent as attachment. I get file from another sevice, do XSLT on it and then I should send it to some mail.
Any more info I should provide?
Thank you!
EDIT
How I call bean:
<spring:bean id="SetAttachment" name="SetAttachment" class="si.irose.msp.cust.bass.SetAttachment">
</spring:bean>
<component doc:name="Java">
<spring-object bean="SetAttachment"/>
</component>
Java class:
package si.irose.msp.cust.bass;
import org.mule.api.MuleEventContext;
import org.mule.api.MuleMessage;
import org.mule.api.lifecycle.Callable;
public class SetAttachment implements Callable{
private MuleMessage mule;
private String name;
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
mule = eventContext.getMessage();
String tryit="routeid";
for (int i=0;i<mule.getInvocationPropertyNames().toArray().length;i++) {
if (mule.getInvocationPropertyNames().toArray()[i].equals(tryit)) {
name=mule.getInvocationProperty(mule.getInvocationPropertyNames().toArray()[i].toString()).toString();
break;
}
}
mule.addOutboundAttachment(name, mule.getInvocationProperty(name), "text/xml");
return null;
}
}
Ok issue solved.
When you read from file inbound or generate with quartz (string or file) you can add "Attachment" directly BUT if you do an XSLT transformation and then try to send that payload via smtp, then you need to add "Object to String" transformer before attachment.
Final working solution:
<sub-flow name="sendBackMail" doc:name="sendBackMail">
<object-to-string-transformer doc:name="Object to String"/>
<set-attachment attachmentName="changed.xml" value="#[payload]" contentType="text/xml" doc:name="Attachment"/>
<logger message="Attachment ok" level="INFO" doc:name="Logger"/>
<file:file-to-byte-array-transformer doc:name="File to Byte Array"/>
<logger message="Attachment ok. Message: #[message]" level="INFO" doc:name="Logger"/>
<smtps:outbound-endpoint host="${mailSMTP}" port="${mailSendPort}"
user="${mailUser}" password="${mailPass}" to="${receiver}"
from="${mailUser}" responseTimeout="60000" doc:name="SMTP"
connector-ref="SMTP" mimeType="text/xml" subject="msp2bass" >
</smtps:outbound-endpoint>
</sub-flow>
I haven't checked yet what transformer you need to apply if you do any other job instead of XSLT so if anyone wish to add some knowledge to this then please do so and add comment.
You need to use set-payload before the smtps:outbound-endpoint to set the body of the email to whatever you want, otherwise it will contain the same value attached with set-attachment.
Also remove the file:file-to-byte-array-transformer: it's unclear what it's there for.
Related
Details : I have created on flow in MuleESB which is calling a web-service without any parameter just sending it username, password and token in a property and it is working fine.
But the second API I want to post some parameters while calling soap request but I don't know how to use it I tried to pass through set payload but no response.
<http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
<http:request-config name="HTTP_Request_Configuration" host="webservicehostadd" port="443" doc:name="HTTP Request Configuration">
<http:basic-authentication username="username" password="pass"/>
</http:request-config>
<ws:consumer-config name="Web_Service_Consumer" wsdlLocation="https://xxxx/1.0?wsdl" service="xxx" port="xxxx" serviceAddress="https://xxxxx/1.0" connectorConfig="HTTP_Request_Configuration" doc:name="Web Service Consumer"/>
<flow name="mycustomflow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/TEST" doc:name="HTTP"/>
<set-property propertyName="APIKey" value="xxx-xxx-xxx-xxx-xxx" doc:name="Property"/>
<dw:transform-message doc:name="Transform Message" metadata:id="xxx-xxx-xxx-xxx-xxxxxxxx">
<dw:input-payload mimeType="application/xml"/>
<dw:set-payload><![CDATA[%dw 1.0
%output application/xml
%namespace ns0 http://localhost/getDetails:getDetailsWSD
---
{
ns0#getDetails: {
getDetailsOrder: {
ID: payload.ns0#getDetails.getDetailsOrder.ID,
AllData: payload.ns0#getDetails.getDetailsOrder.AllData
}
}
}]]></dw:set-payload>
</dw:transform-message>
<ws:consumer config-ref="Web_Service_Consumer" operation="employeeDetails" doc:name="Web Service Consumer"/>
</flow>
It showed the below error:
Exception while executing:
[row,col]: [1,1]
Unexpected character '{' (code 123) in prolog; expected '<'
at [row,col {unknown-source}]: [1,1].
Updated answer:
<dw:transform-message metadata:id="XXXXX" doc:name="Transform Message">
<dw:set-payload><![CDATA[
%output application/xml skipNullOn="everywhere"
%namespace ns0 localhost/getDetails:getDetailsWSD
---
{
ns0#getDetails: {
getDetailsOrder: {
ID: payload.ns0#getDetails.getDetailsOrder.ID,
AllData: payload.ns0#getDetails.getDetailsOrder.AllData
}
}
}]]>
</dw:set-payload>
</dw:transform-message>
In your scenario: you are passing a body in XML format and sometimes an empty body in your Postman requests.
Passing an empty body results your payload to be {NullPayload}. To handle this, we have to remove explicitly defining the input mime type: <dw:input-payload mimeType="application/xml"/>.
In your transformation: ID: payload.ns0#getDetails.getDetailsOrder.ID,. You are retrieving a value from an empty payload and this will fail. To avoid failing, we have added: skipNullOn="everywhere". You can read more about it here.
I have tried the transformation myself which results to this:
<?xml version='1.0' encoding='UTF-8'?>
<ns0:getDetails xmlns:ns0="http://localhost/getDetails:getDetailsWSD">
<getDetailsOrder/>
</ns0:getDetails>
I think we are done with your initial issue regarding transformation of your empty payload. Your concern now is consuming the web service.
Thank you.
I am trying to write configure a gateway, which should take a complete SOAP Message and then delegate it to another SOAP Provider (incl. all SOAP headers of the first request).
What I have done so far:
1) web.xml
MessageDispatcherServlet with Mapping:
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/appservices/*</url-pattern>
</servlet-mapping>
2) Configuration with an Endpoint-Mapping
<bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
<property name="defaultEndpoint" ref="ws-in-gw"/>
</bean>
3) Configuration of Spring Integration inbound-gateway and outbound-gateway
<int-ws:inbound-gateway id="ws-in-gw"
request-channel="in"
reply-channel="out"
mapped-request-headers="*" />
<int:channel id="in" />
<int:channel id="out" />
<int-ws:outbound-gateway
id="ws-out-gw-status"
request-channel="in-status"
reply-channel="out-status"
uri="http://${delegationServer}/${delegation.contextroot}/soap/AnotherService"
interceptor="soapEnricher"
</int-ws:outbound-gateway>
<bean id="soapEnricher" class="foo.bar.SoapHeaderEnricher" />
public class SoapHeaderEnricher implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
try {
SoapMessage soapMessage = (SoapMessage) messageContext.getRequest();
SoapHeader sh = soapMessage.getSoapHeader();
// can use sh.addHeaderElement(new QName(...)) now, but where are the original Headers???
} catch () {
}
}
My first Problem was, that the original SOAP Headers had been cut of, so I introduced the ' mapped-request-headers="*" ' attribute at the inbound gateway.
When I now configure a wire-tap, I see the Headers (myToken:MySecretToken) are received:
DEBUG 10:46:53 - [Payload DOMSource content=javax.xml.transform.dom.DOMSource#24a6ce98][Headers={errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#43456ff4, myToken:MySecretToken=org.springframework.ws.soap.saaj.SaajSoapHeaderElement#3b91ead, ...}]
This is the SOAP Message for my test:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:stat="http://status.service.promakler.provinzial.com/">
<soapenv:Header>
<myToken:MySecretToken xmlns=""
xmlns:myToken="http://foo.bar">12345</myToken:MySecretToken>
</soapenv:Header>
<soapenv:Body>
<stat:getStatus/>
</soapenv:Body>
</soapenv:Envelope>
So the Headers are now in my Message, but in the ClientInterceptor, there is no way to get the Headers (just the payload)?! I can add new Headers, but how can I get the original Header?
Can anybody give me a hint (or perhaps there is even a quiet simpler solution??)
Regards
Timo
Try to introduce a custom extension of DefaultSoapHeaderMapper and override populateUserDefinedHeader to extract those SaajSoapHeaderElement from the MessageHeaders and populate them to the SoapHeader. And finally inject your solution to the header-mapper of your <int-ws:outbound-gateway>.
I have a requirement where I need to expose a SOAP webservice .. So I have the following Mule flow :-
<jms:activemq-connector name="Active_MQ" brokerURL="tcp://localhost:61616" validateConnections="true" doc:name="Active MQ"/>
<flow name="Flow1" doc:name="Flow1" >
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8082" path="mainData" doc:name="HTTP"/>
<cxf:jaxws-service serviceClass="com.test.services.schema.maindata.v1.MainData" doc:name="SOAP"/>
<mulexml:object-to-xml-transformer doc:name="Object to XML"/>
<jms:outbound-endpoint queue="NewQueue" connector-ref="Active_MQ" doc:name="JMS" exchange-pattern="request-response"/>
<logger message="Response2 :- #[message.payload]" level="INFO" doc:name="Logger"/>
<mulexml:xml-to-object-transformer doc:name="XML to Object"/>
</flow>
<flow name="flow2" doc:name="flow2" >
<jms:inbound-endpoint connector-ref="Active_MQ" address="jms://tcp:NewQueue" doc:name="JMS" exchange-pattern="request-response" disableTemporaryReplyToDestinations="true" responseTimeout="90000"/>
<set-variable variableName="SOAPRequest" value="#[message.payload]" doc:name="Variable"/>
<mulexml:xml-to-object-transformer doc:name="XML to Object"/>
<component class="com.test.services.schema.maindata.v1.Impl.MainDataImpl" doc:name="JavaMain_ServiceImpl">
<method-entry-point-resolver>
<include-entry-point method="retrieveDataOperation"/>
<include-entry-point method="insertDataOperation"/>
<include-entry-point method="updateDataOperation"/>
<include-entry-point method="deleteDataOperation"/>
</method-entry-point-resolver>
</component>
<mulexml:object-to-xml-transformer doc:name="Object to XML"/>
</flow>
Here you can see the request is getting into JMS queue from the first flow and the second flow use JMS inbound takes it from the JMS queue and the webservice implemented class proccess it .. Now the service works fine without any issue .. the only issue is it get Mule Header in the SOAP in the response like the following :-
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<mule:header xmlns:mule="http://www.muleumo.org/providers/soap/1.0">
<mule:MULE_CORRELATION_ID>ID:ANIRBAN-PC-49483-1408719168461-1:1:10:1:1</mule:MULE_CORRELATION_ID>
<mule:MULE_CORRELATION_GROUP_SIZE>-1</mule:MULE_CORRELATION_GROUP_SIZE>
<mule:MULE_CORRELATION_SEQUENCE>-1</mule:MULE_CORRELATION_SEQUENCE>
</mule:header>
</soap:Header>
<soap:Body>
<deleteDataResponse xmlns="http://services.test.com/schema/MainData/V1">
<Response>Data not exists !!! Please provide correct ID</Response>
<Id>0</Id>
<Age>0</Age>
</deleteDataResponse>
</soap:Body>
</soap:Envelope>
Now please help me to remove the Mule headers from the SOAP response .. I tried with the following :- <cxf:jaxws-service serviceClass="com.test.services.schema.maindata.v1.MainData" enableMuleSoapHeaders="false" doc:name="SOAP"/> but no use .. it's still showing the Mule headers .. Please help ..
You can potentially get away with a temporary workaround until the enableMuleSoapHeaders="false" issue gets solved, by either:
Adding a between the HTTP inbound endpoint and the CXF service ; in order to remove the undue SOAP headers but XSL-Transformation
Using delete-message-property to remove the MULE_* props at the end of Flow1, right after the mulexml:xml-to-object-transformer
I also found an alternate solution off adding it to cxf:outInterceptors
<cxf:jaxws-service serviceClass="com.test.services.schema.maindata.v1.MainData" doc:name="SOAP">
<cxf:outInterceptors >
<spring:bean id="outfault" class="com.test.services.schema.SOAPOptionalData.SOAPInterceptorOutboundHeaderRemover"/>
</cxf:outInterceptors>
</cxf:jaxws-service>
And in the Java class :-
public class SOAPInterceptorOutboundHeaderRemover extends AbstractSoapInterceptor {
public SOAPInterceptorOutboundHeaderRemover() {
super(Phase.PRE_PROTOCOL);
}
#Override
public void handleMessage(SoapMessage arg0) throws Fault {
List<Header> headerList = arg0.getHeaders();
Header muleHeader = null;
boolean isMuleHeader = false;
for (Header header : headerList) {
ElementNSImpl element = (ElementNSImpl) header.getObject();
if ("mule:header".equals(element.getNodeName())) {
isMuleHeader = true;
muleHeader = header;
}
}
if (isMuleHeader) {
arg0.getHeaders().remove(muleHeader);
}
}
}
And this is also working fine
Im able to log some message but how to change it to access a database and send those messages and store there?
i know the database part is disconected from the pattern but tried somethings and nothing worked so far..
code:
<scripting:transformer name="noopLoggingTransformer">
<scripting:script engine="groovy">
log.info "insert into inbound_messages (payload, timestamp, agent, ip_from, endpoint, soap_operation) values ('',now(), ${message.getInboundProperty('user-agent')}, ${message.getInboundProperty('MULE_REMOTE_CLIENT_ADDRESS')}, ${message.getInboundProperty('http.request')}, '');"
message
</scripting:script>
</scripting:transformer>
<pattern:web-service-proxy name="service" transformer-refs="noopLoggingTransformer" inboundAddress="${serverName}/services/Logradouros/LogradouroServico" outboundAddress="${targetServer}/servicos/v2/LogradouroServico.svc" wsdlFile="LogradouroServicos.wsdl">
</pattern:web-service-proxy>
<jdbc-ee:postgresql-data-source name="dbconection" user="${database.user}" password="${database.pass}" url="${database.url}" transactionIsolation="UNSPECIFIED" doc:name="PostgreSQL Data Source">
</jdbc-ee:postgresql-data-source>
<jdbc-ee:connector name="jdbcConnector" dataSource-ref="dbconection" validateConnections="false" transactionPerMessage="true" queryTimeout="10" pollingFrequency="10000" doc:name="JDBC">
<jdbc-ee:query key="querymsg" value="insert into inbound_messages (payload, timestamp, agent, ip_from, endpoint, soap_operation) values ('', now(), '','','')"></jdbc-ee:query>
</jdbc-ee:connector>
What i did after was try a different aproach:
<flow name="logradouros-autenticacao" doc:name="logradouros-autenticacao">
<servlet:inbound-endpoint path="${webservice.logradouros.in.autenticacao.path}" responseTimeout="10000" doc:name="HTTP"></servlet:inbound-endpoint>
<async doc:name="Async">
<flow-ref name="log-request" doc:name="Flow Reference"></flow-ref>
</async>
<outbound-endpoint exchange-pattern="request-response" address="${webservice.logradouros.out.protocol}://${webservice.logradouros.out.host}/${webservice.logradouros.out.autenticacao.path}" doc:name="Generic"></outbound-endpoint>
</flow>
<flow name="log-request" doc:name="log-request">
<logger message="1-> #[groovy:payload.getClass()]" level="INFO" doc:name="Logger"></logger>
<jdbc-ee:outbound-endpoint exchange-pattern="one-way" queryKey="insertMsg" responseTimeout="10000" queryTimeout="-1" connector-ref="jdbcConnector" doc:name="Persist raw message"></jdbc-ee:outbound-endpoint>
<logger message="#[header:INBOUND:Host] #[header:INBOUND:http.method] #[message.inboundProperties.entrySet().toString()] #[groovy:payload.toString()] #[groovy: return message.getInboundPropertyNames().toString()] #[groovy: return message.getInboundProperty('request.parameters').toString()]" level="INFO" category="mule.http.accesslog" doc:name="Logger"></logger>
<logger message="#[message.outboundProperties.entrySet().toString()]" level="INFO" category="mule.http.accesslog" doc:name="Logger"></logger>
</flow>
its not 100% but it may workout, the approach through pattern wasnt working...
In your transformer, use muleContext.client to dispatch messages to VM queue consumed by another flow, which will take care of insert data in the DB.
So in your case, something similar to the following should work:
<scripting:transformer name="noopLoggingTransformer">
<scripting:script engine="groovy">
muleContext.client.dispatch('vm://log-request.in', message)
message
</scripting:script>
</scripting:transformer>
<pattern:web-service-proxy name="service" transformer-refs="noopLoggingTransformer" inboundAddress="${serverName}/services/Logradouros/LogradouroServico" outboundAddress="${targetServer}/servicos/v2/LogradouroServico.svc" wsdlFile="LogradouroServicos.wsdl" />
<flow name="log-request" doc:name="log-request">
<vm:inbound-endpoint path="log-request.in" />
<logger message="1-> #[groovy:payload.getClass()]" level="INFO" doc:name="Logger" />
<jdbc-ee:outbound-endpoint exchange-pattern="one-way" queryKey="insertMsg" responseTimeout="10000" queryTimeout="-1" connector-ref="jdbcConnector" doc:name="Persist raw message" />
<!-- TODO use MEL, not these old style expressions -->
<logger message="#[header:INBOUND:Host] #[header:INBOUND:http.method] #[message.inboundProperties.entrySet().toString()] #[groovy:payload.toString()] #[groovy: return message.getInboundPropertyNames().toString()] #[groovy: return message.getInboundProperty('request.parameters').toString()]" level="INFO" category="mule.http.accesslog" doc:name="Logger" />
<logger message="#[message.outboundProperties.entrySet().toString()]" level="INFO" category="mule.http.accesslog" doc:name="Logger" />
</flow>
I created a sample Mule flow by first generating client classes with CXF per http://www.mulesoft.org/documentation/display/current/Consuming+Web+Services+with+CXF guide.
The flow is started by going to localhost:8081/test. The parametersObjectArray will transform any message into a hardcoded object array required for the web service method call, like this:
package com.test.example.transformers;
import org.mule.api.transformer.TransformerException;
import org.mule.transformer.AbstractTransformer;
public class GetCustomersArrayTransformer extends AbstractTransformer {
#Override
protected Object doTransform(Object src, String enc)
throws TransformerException {
Object[] msg = new Object[3];
msg[0] = 10;
msg[1] = 0;
msg[2] = null;
return msg;
}
}
When this transformer is used in a flow to pass a message to a jaxws-client node, everything works as expected:
<custom-transformer name="parametersObjectArray" class="com.test.example.transformers.GetCustomersArrayTransformer" doc:name="Java"/>
<flow name="mulecartFlow" doc:name="mulecartFlow">
<http:inbound-endpoint exchange-pattern="one-way" host="localhost" port="8081" doc:name="HTTP" path="test"/>
<transformer ref="parametersObjectArray" doc:name="Java"></transformer>
<https:outbound-endpoint exchange-pattern="request-response" host="12.34.56.78" port="1234" path="services/SOAP/TestEndpoint" doc:name="HTTP" connector-ref="httpsConnector" method="POST">
<cxf:jaxws-client clientClass="com.test.TestEndpointService" enableMuleSoapHeaders="true" doc:name="SOAP" operation="getCustomers" port="TestEndpoint" />
</https:outbound-endpoint>
<transformer ref="customerInfoTypesToString" doc:name="Transformer Reference"/>
<logger level="INFO" doc:name="Logger" message="#[message:payload]"/>
</flow>
I would like to use a wrapper object, so that parameters are legible and type-safe:
package com.test.example.transformers;
import org.mule.api.transformer.TransformerException;
import org.mule.transformer.AbstractTransformer;
import com.test.GetCustomers;
public class GetCustomersObjectTransformer extends AbstractTransformer {
#Override
protected Object doTransform(Object src, String enc)
throws TransformerException {
GetCustomers soapRequest = new GetCustomers();
soapRequest.setStartIndex(0);
soapRequest.setMaxBatchSize(1);
return soapRequest;
}
}
However, that does not seem to work. I noticed that the manual page states:
Note: the CXF transport doesn't support wrapper-style web service
method calls. You may need to create a binding file or change the WSDL
directly
What does that mean? How can I send a wrapper object that wraps all method parameters to the web service method?
Add:
<jaxws:bindings xmlns:jaxws="http://java.sun.com/xml/ns/jaxws">
<jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
</jaxws:bindings>
inside wsdl:portType and CXF will generate the wrapper objects you're after.
Also, note that creating a Java transformer to set the payload is overkill: use set-payload with a simple MEL expression and you'll be good.