Trying to use the Mule XML streaming feature as have to process very large xml files. Followed the documentation, the document does not have concrete examples.
When I inspected the payload I get the XMLUtils class and not the XMLStreamReader class as stated in the documentation.
The flow is as follows have a file connector which passes payload to a custom transformer, the transformer passes the data to a spring bean which is going to have event based processing.
In the spring bean. At run time the spring bean gets the XMLUtils class and not the XMLStreamReader class.
Mule - Config:
<spring:beans>
<spring:bean id="OracleCDMMapper" class="oraclecdmstream.OracleCDMMapper">
</spring:bean>
<spring:bean id = "OraclePaySlip" class="com.nect.transform.OracleCDMPaySlip" ></spring:bean>
</spring:beans>
<flow name="mulefileconnectorexampleFlow1" >
<file:inbound-endpoint path="C:/c-OracleCloud/src/main/resources" pollingFrequency="600000" moveToDirectory="C:/c-OracleCloud/src/main/resources/back" doc:name="File Input" >
<!-- <file:filename-regex-filter pattern="(^*.xml$)" caseSensitive="false"/>
--> <file:filename-wildcard-filter pattern="*.xml"></file:filename-wildcard-filter>
</file:inbound-endpoint>
<logger message="Transferring file : #[message.inboundProperties['originalFilename']]" level="INFO" doc:name="Logger"/>
<logger message ="Logger 1 " level="INFO" doc:name ="Logger1" />
<!-- Call the XMLSTREAMER -->
**<custom-transformer name="XmlToXSR" class="org.mule.module.xml.transformer.XmlToXMLStreamReader" doc:name="XMLTOORACLE">**
</custom-transformer>
<component doc:name="Java">
<spring-object bean="OracleCDMMapper"/>
</component>
-->
<logger message ="I am Complete " level="INFO" doc:name ="LoggerMurali" />
</flow>
</mule>
Here is the Javacode:
Spring Bean
public class OracleCDMMapper implements Callable {
private final Logger logger = LoggerFactory.getLogger(OracleCDMMapper.class);
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
// TODO Auto-generated method stub
MuleMessage muleMessage = eventContext.getMessage();
logger.info("In the Spring Component");
logger.info(muleMessage.getPayload().getClass().toString());
**javax.xml.stream.XMLStreamReader xsr = (XMLStreamReader) muleMessage.getPayload(javax.xml.stream.XMLStreamReader.class);**
Any help will be much appreciated
I've verified and you are right, in the code the supposed returning class should be a DelegateXMLStreamReader class that implements XMLStreamReader, but the returned class is an anonymous inner class of XMLUtils that at runtime cannot be casted to any Stream like class. It seems to be a defect.
If you really need the control of the xml stream, you could use a custom java component:
<component class="com.foo.CustomJavaComponent" doc:name="Java"/>
.
public class CustomJavaComponent implements Callable{
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
MuleMessage muleMessage = eventContext.getMessage();
FileInputStream fis = (FileInputStream)muleMessage.getPayload();
//Do something with this stream
return "Hello world";
}
}
And get the input stream to do whatever you want.
Related
I am new to Anypoint Studio and I am trying to create a flow that will have an HTTP connector and an endpoint and when a user invokes the http endpoint then the application will load the contents of an XML file, will convert then to JSON and will return the JSON back in the HTTP Response.
My flow configuration looks like this
<flow name="test-flow">
<http:listener config-ref="HttpListenerConfiguration" path="/read" allowedMethods="POST" doc:name="HTTP">
<http:response-builder statusCode="200">
<http:header headerName="Content-Type" value="application/json"/>
</http:response-builder>
</http:listener>
<file:inbound-endpoint path="xml/test.xml" responseTimeout="10000" doc:name="File"/>
<mulexml:xml-to-object-transformer returnClass="org.mule.examples.Catalog" doc:name="XML to Object"/>
<json:object-to-json-transformer sourceClass="org.mule.examples.Catalog" doc:name="Object to JSON"/>
</flow>
Of course it doesn't work. I get a
SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'file:inbound-endpoint'. One of '{"http://www.mulesoft.org/schema/mule/core":abstract-message-processor, "http://www.mulesoft.org/schema/mule/core":abstract-outbound-endpoint, "http://www.mulesoft.org/schema/mule/core":abstract-mixed-content-message-processor, "http://www.mulesoft.org/schema/mule/core":response}' is expected.
which I assume is coming from the fact that <file:inbound-endpoint...> needs to be as a source in the flow.
Any suggestions on how can I read a file after an HTTP endpoint is invoked?
Thanks
As you correctly assumed, file:inbound-endpoint can only act as a message source. Thus to achieve what you want you can use the mule module requester. HTH.
Try the requester module. It will allow you to load the file at any point in the flow. You can read more about it in this blogpost.
Also you can use XML to Json inbuilt transformer in to convert it to a Json object.
<http:listener-config name="HTTP_Listener_Configuration" host="localhost" port="8081" doc:name="HTTP Listener Configuration"/>
<flow name="test-flow1">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<json:xml-to-json-transformer doc:name="XML to JSON"/>
<logger message="#[message.payload]" level="INFO" doc:name="Logger"/>
</flow>
If the XML file is on the classpath, you might try <parse-template location="xml/test.xml"> instead of your <file:inbound-endpoint />
The parse-template processor comes in handy surprisingly often. The User's Guide also has a nice reference for it.
You can also write a java component to read the content of the file and use that component in the flow.
Example:
public class FileReaderComponent implements Callable, Initialisable {
private String filePath;
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
#Override
public void initialise() throws InitialisationException {
if (filePath == null || filePath.equals("")) {
throw new InitialisationException(MessageFactory.createStaticMessage("FilePath property has to be set"), this);
}
}
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(new File(filePath)));
} catch (Exception e) {
//Handle Exception
}
StringBuilder content = new StringBuilder();
try {
String line;
while ((line = in.readLine()) != null) {
content.append(line);
}
}catch (Exception e) {
//Handle Exception
} finally {
in.close();
}
return content.toString();
}
}
As many suggested in above, you can use java component or some kind of parse template.
We also had same kind of requirement , we used the expression component to read the file from particular location
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>
In a soap based web service i want to access the Mule message properties. Is there a way of doing this i know one way of using RequestContext.getEvent().getMessage() but this i guess is deprecated. An other way of accessing the MuleMessage properties in the web service. Can someone please provide any pointers on this.
Code Snippet
<flow name="MyWebService" doc:name="MyWebService">
<http:inbound-endpoint exchange-pattern="request-response" address="${WEB_SERVICE_PROTOCOL}://${WEB_SERVICE_HOST}:${WEB_SERVICE_PORT}/MyWebService?wsdl" tracking:enable-default-events="true">
<cxf:jaxws-service serviceClass="com.XXX.XXX.service.MyWebService" doc:name="SOAP"/>
</http:inbound-endpoint>
<component doc:name="My Web Service">
<spring-object bean="WebServiceImpl"/>
</component>
</flow>
Depending on what is your purpose for obtaining the message properties, one option is to use a cxf interceptor to access the message. See the following example.
adding the interceptor:
<cxf:jaxws-service serviceClass="org.example.HelloWorld">
<cxf:inInterceptors>
<spring:bean class="org.example.MyInterceptor"/>
</cxf:inInterceptors>
</cxf:jaxws-service>
interceptor class:
package org.example;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.mule.api.MuleEvent;
import org.mule.api.MuleMessage;
public class MyInterceptor extends AbstractSoapInterceptor {
public MyInterceptor() {
super(Phase.USER_PROTOCOL);
}
#Override
public void handleMessage(SoapMessage message) throws Fault {
MuleEvent muleEvent = (MuleEvent)message.getContextualProperty("mule.event");
MuleMessage muleMessage = muleEvent.getMessage();
System.out.println(muleMessage.toString());
}
}
You can achieve this by not implementing the service interface at all and deal with the SOAP requests as Mule messages (where properties are accessible) instead of dealing with deserialized objects in service classes.
Here is an example fragment, assuming you've generated the necessary classes and interfaces from the WSDL with wsdl2java:
<flow name="WebServiceFlow">
<http:inbound-endpoint exchange-pattern="request-response"
address="http://localhost:8080/services/some" />
<cxf:jaxws-service
serviceClass="com.amce.SomePortType" />
<choice>
<when
expression="#[cxf_operation.localPart == 'SomeOperation']">
<flow-ref name="HandleSomeOperation" />
</when>
If you have access to MuleMessage then you get the required properties by using the method
Set<String> getPropertyNames(PropertyScope scope);
available in MuleMessage class. To get the MuleMessage you would need access to MuleClient; have you got access to MuleClient? if yes, then use:
muleClient = muleContext.getClient();
MuleMessage result = muleClient.send(webaddress, "", props);
Is this what you are trying to acheive?
I building Mule project who receive JSON by HTTPComponent and convet to object.
My problem is can't acess field in object converted.
My flux's XML is:
<flow name="RestJsonHelloWorldFlow1" doc:name="RestJsonHelloWorldFlow1"><http:inbound-endpointexchange-pattern="request-response"
host="localhost" port="8081"
path="credit/new" doc:name="HTTP"
mimeType="application/json"/> <response>
<logger message=" #[message.payload.transactionCode]" level="INFO" doc:name="Logger"/>
</response>
<json:json-to-object-transformer doc:name="JSON to Object" returnClass="com.creditmobile.domain.Request" name="request"/></flow>
My pojo is:
#JsonAutoDetect
public class Request {
private Integer transactionCode;
public Request() {
super();
}
public Integer getTransactionCode() {
return transactionCode;
}
My JSON is:
{
"transactionCode": 1
}
I got following exception:
org.mule.api.transformer.TransformerMessagingException: The object transformed is of type: "SimpleDataType{type=java.lang.String, mimeType='*/*'}", but the expected return type is "SimpleDataType{type=org.mule.api.transport.OutputHandler, mimeType='application/json'}". The current MuleMessage is null! Please report this to dev#mule.codehaus.org
at org.mule.transformer.AbstractMessageTransformer.checkReturnClass(AbstractMessageTransformer.java:183)
at org.mule.transformer.AbstractMessageTransformer.transform(AbstractMessageTransformer.java:162)
at org.mule.transformer.AbstractMessageTransformer.transform(AbstractMessageTransformer.java:73)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
I saw anothers topics with same problem, but they don't have resolution and yours suggesttion doesn't work for me.
I access field with:[message.payload.transactionCode]
Thanks.
I think your problem is in the http response phase, not in accessing the field. Mule gets confused when you try to return your Java object as an http response. Depending on your requirements, try setting the return payload to something more understandable in the response phase. For example
<set-payload value="#[payload.transactionCode.toString()]"/>
or
<json:object-to-json-transformer/>
What do you actually want to return to the user once the application exits RestJsonHelloWorldFlow1 flow?
I think as Anton Kupias said your problem is the tag. I managed to solve the exception by removing the tag, and convert the payload into an object exactly after the HTTP inbound endpoint.
Config XML
<flow name="RestJsonHelloWorldFlow1" doc:name="RestJsonHelloWorldFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="credit/new"
doc:name="HTTP" mimeType="application/json" />
<json:json-to-object-transformer
doc:name="JSON to Object" returnClass="com.creditmobile.domain.Request" />
<component class="com.creditmobile.domain.TransactionResponse" doc:name="Java"/>
<object-to-string-transformer doc:name="Object to String"/>
</flow>
basically you have
1) An HTTP inbound endpoint which is set to request-response
2) immediately convert the JSON to Object
3) Then I have created a java component to perform the required processing; by creating a class which implements org.mule.api.lifecycle.Callable. Got the transaction code from the payload and set the overridden method to return an object of type string
public class TransactionResponse implements Callable {
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
// Type casts payload to object Request
Request requestObject = (Request) eventContext.getMessage().getPayload();
int code = Integer.valueOf(requestObject.getTransactionCode());
String reply = "";
switch (code) {
case 1:
reply = "New";
break;
case 2:
reply = "Delete";
break;
}
return reply;
}
}
4) Then i terminated the flow by adding the Object to String component, Or place the Object to JSON component if you wish to return a JSON object to the user.
I use mule to process the files from sftp, but I need to process the file in order(the sequence is order by name of file).
For example:
there are 3 files in sftp:
1.txt,2.txt,3.txt
I need to process these three files one by one and have to in the order of file's name.(first to process 1.txt then 2.txt at last 3.txt)
I use service-overrides in connector and extend the SftpMessageReceiver and override the poll() method ,in the method I make these files's name by order. But it doesn't work.Who can tell me where is wrong.
the class I write
public class NewSftpMessageReceiver extends SftpMessageReceiver {
private SftpReceiverRequesterUtil sftpRRUtil = null;
public NewSftpMessageReceiver(SftpConnector connector,
FlowConstruct flow,
InboundEndpoint endpoint,
long frequency) throws
CreateException {
super(connector, flow, endpoint);
this.setFrequency(frequency);
sftpRRUtil = new SftpReceiverRequesterUtil(endpoint);
}
public NewSftpMessageReceiver(SftpConnector connector, FlowConstruct flow, InboundEndpoint endpoint)
throws
CreateException {
super(connector, flow, endpoint);
sftpRRUtil = new SftpReceiverRequesterUtil(endpoint);
}
#Override
public void poll() throws
Exception {
try {
String[] files = sftpRRUtil.getAvailableFiles(false);
//do something here.....
for (String file : files) {
if (getLifecycleState().isStopping()) {
break;
}
routeFile(file);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
the config file like below
<sftp:connector name="incoming-mandate-connector"
archiveDir="${esb.archive.path}/mandate/incoming"
useTempFileTimestampSuffix="true"
autoDelete="true"
identityFile="${esb.incoming.sftp.identityFile}"
passphrase="${esb.incoming.sftp.passphrase}"
sizeCheckWaitTime="${esb.incoming.sftp.sizeCheckWaitTime}">
<service-overrides messageReceiver="com.xxy.NewSftpMessageReceiver"/>
</sftp:connector>
<sftp:endpoint name="incoming-mandate-ep"
address="${esb.incoming.connector}"
connector-ref="incoming-mandate-connector"
keepFileOnError="true" encoding="${esb.encoding}">
<file:filename-wildcard-filter pattern="${esb.incoming.filePattern}"/>
</sftp:endpoint>
the flow like blew
<flow name="process">
<quartz:inbound-endpoint name="quartz-endpoint" cronExpression="${esb.cronExpression}" jobName="incoming-mandate-job">
<quartz:endpoint-polling-job>
<quartz:job-endpoint ref="incoming-mandate-ep"/>
</quartz:endpoint-polling-job>
</quartz:inbound-endpoint>
<processor ref="mandate-processor"/>
<transformer ref="mandate-transformer"/>
<mulexml:jaxb-object-to-xml-transformer jaxbContext-ref="jaxb" name="mandate-jaxb-transformer"/>
<byte-array-to-string-transformer mimeType="application/xml"/>
<message-properties-transformer scope="outbound">
<add-message-property key="${esb.username}" value="${esb.password}"/>
</message-properties-transformer>
<outbound-endpoint ref= "mandate-service-ep"/>
</flow>
The reason why your message receiver is not being called is because you have a Quartz endpoint that calls the SFTP transport, so in fact you are using a message requester, not a receiver.
You would need to create a custom message requester instead of a receiver and also a custom requester factory (that creates instances of the requester)
You can configure it as follows:
<sftp:connector name="sftpConnector">
<spring:property name="serviceOverrides">
<spring:map>
<spring:entry key="requester.factory" value="your.package.YourCustomSftpRequesterFactory"/>
</spring:map>
</spring:property>
</ftp:connector>