Reputation: 2827
I'm building an apllication that need to read and parse an XML file to start some jobs (each one based on the XML tag - below i show you an example of my XML, omitting the namespace).
Looking on SI project i've found this example that explain how to process XML file.
Before looking this i was trying to use a simple Unmarshalling process that create POJOS classes, this is good to parse it but many nested POJO will make my work very hard because for example some tags can be omitted (all are optional) and my program looks like an "infinite" if-else
.
What i need to do is:
Right now i dont want to think about the job (even using Spring i've seen Spring Batch and in the future i would like to integrate it with SI, but right now i just only need to parse the XML)
In the example i've seen that is used si-xml:xpath-splitter
, so looking my XML file i will need to create many splitter for each tag and then redirect to an appropriate channel ? For example process
tag will be redirected to appropriate service-activator
, activity
tag to another service-activator
and so on ?
The example (the link i've show above) is a nice good start, but on the net i havent found other examples where i can "understand" other aspect of SI-XML, for this i've asked here for a little help on how to get started.
Another little problem is all attriutes that i have in the tags, with Unmarshalling i can create an interface and then i know that all classes must implement it (and use it to run the job), but here (SI + XML) i dont know how to create something similar...
<?xml version="1.0" encoding="UTF-8"?>
<root>
<activity attr="1" />
<activity attr="2" />
</root>
UPDATE Right now i've write this applicationContext in order to split each tag and redirect to appropiate channel but i'm having the error "Cannot convert #STRING in NodeList"
<si:channel id="rootChannel" />
<si:channel id="activityChannel" />
<!-- Here i split each tag of type activity -->
<si-xml:xpath-splitter id="activitySplitter" input-channel="rootChannel" output-channel="router">
<si-xml:xpath-expression expression="//process|//activity" />
</si-xml:xpath-splitter>
<!-- Here i want route the messages to appropiate channel each tag must be processed separately because will need to create a thread for each one-->
<si-xml:xpath-router input-channel="router">
<si-xml:xpath-expression expression="concat(name(./node()), 'Channel')" />
</si-xml:xpath-router>
<si:outbound-channel-adapter channel="activityChannel" method="dispatch">
<bean class="it.example.Activity" />
</si:outbound-channel-adapter>
This is the main class:
public class Probe {
public static void main(String[] args) throws Exception {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/probeProcessing.xml", Probe.class);
MessageChannel messageChannel = (MessageChannel) context.getBean("rootChannel");
GenericMessage<Document> probeMessage = createXmlMessageFromResource("/META-INF/spring/probe.xml");
messageChannel.send(probeMessage);
context.close();
}
private static GenericMessage<Document> createXmlMessageFromResource(String path) throws Exception {
Resource probeRes = new ClassPathResource(path);
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(false);
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document probeDoc = builder.parse(probeRes.getInputStream());
return new GenericMessage<Document>(probeDoc);
}
}
This is a part of the stack:
Exception in thread "main" org.springframework.messaging.MessageHandlingException: error occurred in message handler [org.springframework.integration.config.RouterFactoryBean#0]; nested exception is org.springframework.xml.xpath.XPathException: Could not evaluate XPath expression:com.sun.org.apache.xpath.internal.XPathException: Impossibile convertire #STRING in NodeList.; nested exception is javax.xml.xpath.XPathExpressionException: com.sun.org.apache.xpath.internal.XPathException: Impossibile convertire #STRING in NodeList.
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:139)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:147)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:120)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:442)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:392)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:231)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:154)
at org.springframework.integration.splitter.AbstractMessageSplitter.produceOutput(AbstractMessageSplitter.java:156)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:102)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:105)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:147)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:120)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:442)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:392)
at it.dirimo.Probe.main(Probe.java:20)
Upvotes: 1
Views: 1434
Reputation: 121272
Not sure what is the problem you see with the Spring Integration XML support when there is no any surprise unless standard XML processing: XPath, Marhsalling, XSLT etc.
The Spring Integration just provides EIP abstraction over there and allow you to make the messaging process for the XML documents as payload
.
I think you go right way with the <int-xml:xpath-splitter>
. For example using //process|//activity
as a XPath expression.
After the xpath-spliter
you can use <int-xml:xpath-router>
to check the root element for the splitter document and route it to the appropriate channel.
UPDATE
, how i can do to redirect to a channel simply from the tag? For exmample process redirect to processChannel and activity redirect to activityChannel... ?
Something like this:
<int-xml:xpath-router>
<int-xml:xpath-expression expression="concat(name(./node()), 'Channel')"/>
</int-xml:xpath-router>
UPDATE 2
Well, I've found how it will work for your case. That concat()
xPath works somehow only for documents. So you should add create-documents="true"
to the xpath-spliter
. And another concat()
concern that we expect a String
anyway. But xpath-router
evaluates to the NODELIST
by default. So you should add evaluate-as-string="true"
to xpath-router
configuration.
Tested locally - works well.
Upvotes: 1