Seems to be a simple task but I cannot get my head around it.
I have a Spring integration chain which calls an external Webservice, which returns an XML. I would like to use that XML response in a downstream XpathRouter.
What expected-response-type should I use?
<int:chain input-channel="filesChannel">
<!-- ... some previous components in the chain -->
<int-http:outbound-gateway
http-method="POST"
url="http://web/service/url"
expected-response-type="java.lang.String" />
<int-xml:xpath-router default-output-channel="resultChannel">
<int-xml:xpath-expression expression="/order/status" />
<int-xml:mapping value="Error" channel="importErrorChannel" />
</int-xml:xpath-router>
</int:chain>
It doesn't seem like the xpath-router can consume the XML returned by the webservice. When I debug the router with a breakpoint on the following line:
Node node = this.converter.convertToNode(message.getPayload());
The node is null, although the message does contain valid XML.
Is it because I am not setting the right expected-response-type?
Here is the response XML I receive from the service:
<apiResponse version="1.0">
<orders>
<order>
<orderReference>test_2_3045342</orderReference>
<status>Error</status>
<errors>
<error>
<errorCode>1100</errorCode>
<errorMessage><![CDATA[ "Field is required: dropWindow" ]]></errorMessage>
</error>
</errors>
</order>
</orders>
</apiResponse>
Ok, I found my mistake - it was actually in the XPATH expression. It should have been //order/status to enable deep search.
The java.lang.String expected-response-type works just fine with XML XpathRouter.
Thanks
Related
I am trying to make a validation of XML requests on missing or empty XML tags. I used this code
<filter description="Validate material" regex=".+"
source="//E1MARAM[not(MATNR)] | //E1MARAM/MATNR[not(text())]">
<then>
<log category="WARN">
<property name="/material"
value="validation-empty tag MATNR send back to SAP" />
</log>
<property name="HTTP_SC" scope="axis2" type="STRING"
value="500" />
<makefault version="soap11">
<code value="soap11Env:VersionMismatch" xmlns:soap11Env="http://schemas.xmlsoap.org/soap/envelope/" />
<reason value="Missing SAP parameter" />
<detail>MATNR</detail>
</makefault>
<respond />
</then>
</filter>
which beatifically checks the XML tag //E1MARAM/MATNR
My problem is that this works only when the tag is empty.
It seems like the Filter mediator with reqex .+ and xpath //E1MARAM[not(MATNR)] does not meet the condition and thus the check does not work
Any idea why? or perhaps another idea how to better validate in WSO2 ESB on missing XML tags or values without XSD? a XSD schema cannot be used, as the XML request does not have a fixed structure - typical for SAP iDoc
The easiest way is to check for an not empty string ''. This will resolve to false both when empty and when missing. Also, you do not have to use the regex to check, you can just use xpath. If you give the filter mediator an xpath expression it will resolve it as if checking a boolean.
<filter description="Validate material" xpath="not(//E1MARAM/MATNR!=''") >
This will return true if the element is either empty or does not exist. It will return false if there is a text value.
Below is my code snippet for Camel Context in Spring DSL. I can retrieve
value from PAYLOAD message but not able to retrieve using RAW. Is there any way we can retrieve value from RAW message which is a SOAP request XML?
<cxf:cxfEndpoint address="/xxx/xxx/xxx/xxx/" endpointName="vi:nameService" id="SOAPInput"
serviceName="vi:nameService" wsdlURL="wsdl/name.wsdl" xmlns:vi="http://services.visa.com/xx/xx/xx/xxx">
<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
<from id="Listener" uri="cxf:bean:SOAPInput?dataFormat=RAW"/>
<setProperty id="setName" propertyName="name">
<xpath resultType="java.lang.String" trim="false">/*/*/*/v1:getname/name/text()</xpath>
</setProperty>
In the process of moving to Spring WS and using JAXB from wsdl to generate the objects used in the Soap message. I am having an issue trying to get the Custom Exception into the details field of the Soap Fault message. The following is how the existing fault message is returned (FooException embedded in detail).
<env:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header/>
<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>Service specific exception: com.test.FooException: Invalid authentication credentials. Please try again.</faultstring>
<detail>
<n1:FooException xsi:type="n1:FooException" xmlns:n1="java:com.test">
<errorCode xsi:type="xsd:int">101</errorCode>
<errorReason xsi:type="xsd:string">InvalidUserCredentials:Invalid authentication credentials. Please try again</errorReason>
</n1:FooException>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>
I have setup a customer exceptionResolver bean identifier in the spring config xml file.
<bean id="exceptionResolver" class="com.test.FacadeExceptionHandler">
<property name="order" value="1"></property>
<property name="defaultFault" value="SERVER"/>
<property name="exceptionMappings">
<value> com.test.FooException=SERVER,FaultMsg </value>
</property>
</bean>
I created the FacadeExceptionHandler and it is getting called, but I cannot figure out how to get the FooException into the details portion of the fault message.
Any help would be appreciated!!!
Thanks in advance for your help!
I think you almost get it all, in your FacadeExceptionHandler you must override the customizeFault method (doc) and then add the detail for the fault (you have your Exception as a parameter there).
All this custom detail handling for Soap Fault messages is explained in detail in this entry: http://www.stevideter.com/2009/02/18/of-exceptionresolvers-and-xmlbeans/
You cannot return to SOAP client fault with such depth of nested elements in <detail> element cause in Spring WS SoapFault has list of SoapFaultDetail which has list of SoapFaultDetailElement but SoapFaultDetailElement is just detail message in String. So in Spring WS max depth of SOAP Fault details is one.
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>Сообщение не соответствуют схеме сервиса СМЭВ.</faultstring>
<detail>
<InvalidContent xmlns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/faults/1.1">cvc-complex-type.2.4.a: Invalid content was found starting with element 'ns:SenderProvidedRequestData1'. One of '{"urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1":SenderProvidedRequestData}' is expected.</InvalidContent>
<InvalidContent xmlns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/faults/1.1">cvc-complex-type.2.3: Element 'ns:CallerInformationSystemSignature' cannot have character [children], because the type's content type is element-only.</InvalidContent>
<InvalidContent xmlns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/faults/1.1">cvc-complex-type.2.4.b: The content of element 'ns:CallerInformationSystemSignature' is not complete. One of '{WC["http://www.w3.org/2000/09/xmldsig#"]}' is expected.</InvalidContent>
</detail>
</SOAP-ENV:Fault>
I am using Spring Integration and have the following two kinds of xml inputs
First XML:
<businessArea>SomeValue</businessArea>
Second XML:
<?xml version="1.0" encoding="UTF-8"?>
<businessArea>
<Code>SomeValue</Code>
</businessArea>
Now, I want to store the value of business Area in the header based on the following conditions:
1) If the businessArea has no child nodes such as <Code> in the second xml, then the node value of businessArea should be stored in the header.
2) If the businessArea has any child node such as <Code> in the second xml, then null value should be stored in the header.
I am currently using the following expression:
<int-xml:header name="businessArea" xpath-expression="//businessArea" evaluation-type="STRING_RESULT" overwrite="true" />
But, the above expression will not store the null value in the header if the xml is similar to the second xml that I have shown above.
I think I need to use ternary operator in the above expression. Can any one please suggest an expression which addresses my issue?
That's true, the XPath doesn't such a operator, but we can overcome it with just SpEL. Thanks to Spring Integration we hava #xpath SpEL-function out of the box:
<int:header-enricher>
<int:header name="businessArea" expression="#xpath(payload, '//Code', 'boolean') ? null : #xpath(payload, '/businessArea')/>
</int:header-enricher>
My application can use one of two web services, lets call them WS A and WS B. Both contain the same interface.
I want to perform some logic on the HTTP headers and request channel. WS B should only used on certain request channels. To decide which channel is used I have created a Java class that takes the request channel as a String parameter.
<http:outbound-gateway request-channel="XXXXX"
url-expression="'${EL EXP}'" http-method="GET"
extract-request-payload="true" expected-response-type="java.lang.String"
charset="UTF-8" reply-timeout="3000" reply-channel="XXXXX">
</http:outbound-gateway>
I then read that the url-expression is evaluated when the context is initialised.
source : http://forum.springsource.org/showthread.php?113446-Usage-of-expressions-in-http-namespace-element-url
<int-http:outbound-gateway request-channel="requestChannel"
url="dummy"
http-method="GET" extract-request-payload="true" expected-response-type="java.lang.String"
charset="UTF-8" reply-timeout="3000" reply-channel="sysLoggerRequestChannel">
<int-http:uri-variable name="teststring" expression="test"/>
<int-http:uri-variable name="url" expression="evalClass.getDestinationForChannel(teststring)"/>
</int-http:outbound-gateway>
The problem with this approach is that the expressions in int-http:uri-variable do not seem to be evaluation.
All this makes me believe I am taking the wrong approach. Any help would be very appreciated.
If you have two separate web service end points and a way to determine which one to use per message then a Spring Integration router would be better suited to directing your messages around. This has the added advantage that you can do further processing on your messages specific to the endpoint prior to sending.
There are many many ways to configure a router, including writing an entirely custom one, so I suggest reading through that whole section to see what will work best for you.
A quick example based on message type:
<int:payload-type-router input-channel="requests">
<int:mapping type="my.business.WebServiceARequest" channel="wsA" />
<int:mapping type="my.business.WebServiceBRequest" channel="wsB" />
</int:payload-type-router>
<int-http:outbound-gateway request-channel="wsA" url="http://wsA.com/whatever"
... />
<int-http:outbound-gateway request-channel="wsB" url="http://wsB.com/foo"
... />
To refer to a Spring Bean you should use #. So instead of expression="evalClass.getDestinationForChannel(teststring)" it will be expression="#evalClass.getDestinationForChannel(teststring)".