Here is my spring configuration.
Spring.xml
-------------
<!-- Outgoing SOAP client endpoint -->
<cxf:cxfEndpoint id="serviceEndpoint" address="${endpointAddress}"
wsdlURL="${wsdlAddress}" endpointName="${portName}" serviceName="${serviceName}">
<!-- The interceptors - needed to log the SOAP requests and responses -->
<!-- They can be removed, when no logging is needed -->
<cxf:inInterceptors>
<ref bean="loggingInInterceptor" />
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref bean="loggingOutInterceptor" />
</cxf:outInterceptors>
<cxf:outFaultInterceptors>
<ref bean="loggingOutInterceptor" />
</cxf:outFaultInterceptors>
<cxf:inFaultInterceptors>
<ref bean="loggingInInterceptor" />
</cxf:inFaultInterceptors>
<cxf:properties>
<entry key="dataFormat" value="PAYLOAD" />
</cxf:properties>
</cxf:cxfEndpoint>
<http:conduit name="*.http-conduit">
<http:tlsClientParameters disableCNCheck="${disableHostnameCheck}">
<sec:keyManagers keyPassword="${keystorePassword}">
<sec:keyStore type="JKS" password="${keystorePassword}"
file="${keystoreLocation}" />
</sec:keyManagers>
<sec:trustManagers>
<sec:keyStore type="JKS" password="${truststorePassword}"
file="${truststoreLocation}" />
</sec:trustManagers>
<sec:cipherSuitesFilter>
<!-- these filters ensure that a ciphersuite with export-suitable or
null encryption is used, but exclude anonymous Diffie-Hellman key change
as this is vulnerable to man-in-the-middle attacks -->
<sec:include>.*_EXPORT_.*</sec:include>
<sec:include>.*_EXPORT1024_.*</sec:include>
<sec:include>.*_WITH_DES_.*</sec:include>
<sec:include>.*_WITH_AES_.*</sec:include>
<sec:include>.*_WITH_NULL_.*</sec:include>
<sec:exclude>.*_DH_anon_.*</sec:exclude>
</sec:cipherSuitesFilter>
</http:tlsClientParameters>
<http:client AutoRedirect="true" Connection="Keep-Alive"
ReceiveTimeout="${connectionTimeout}" ConnectionTimeout="${connectionTimeout}" />
</http:conduit>
Here is the Camel route configuration
-------------
<from ...
<to uri="cxf:bean:serviceEndpoint" />
This works well and we can have the soap request/response logged into the log file. Here soap request with soap header is generated by cxf.
Do we have a way to capture the soap request and response into camel Exchange? I have to send an email attached with soap request and response if the service call is failed.
Have tried using thread local but it doesn't seems to work expected.
Suggestion:
You have it in CXF Interceptors - take a look at them.
I guess, you can send your e-mail out of it.
Start from org.apache.cxf.phase.AbstractPhaseInterceptor class - there are bunch of different ones for different phases.
P.S. From first glance org.apache.cxf.binding.soap.saaj.SAAJInInterceptor and
org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor could be good candidates...
Related
I would like to apply an Interceptor on the reply-channel of an http:inbound-gateway to save some event related data to a table. The flow continues in a chain which then goes to a header-value-router. As an example let's take a service-activator at the end of this flow, where the output-channel is not specified. In this case, the replyChannel header holds a TemporaryReplyChannel object (anonymous reply channel) instead of the gateway's reply-channel. This way the Interceptor is never called.
Is there a way to "force" the usage of the specified reply-channel? The Spring document states that
by defining a default-reply-channel you can point to a channel of your choosing, which in this case would be a publish-subscribe-channel. The Gateway would create a bridge from it to the temporary, anonymous reply channel that is stored in the header.
I've tried using a publish-subscribe-channel as reply-channel, but it didn't make any difference. Maybe I misunderstood the article...
Inside my chain I've also experimented with a header-enricher. I wanted to overwrite the value of the replyChannel with the id of the channel I want to intercept (submit.reply.channel). While debugging I was able to see "submit.reply.channel" in the header, but then I got an exception java.lang.NoClassDefFoundError: org/springframework/transaction/interceptor/NoRollbackRuleAttribute and stopped trying ;-)
Code snippets
<int-http:inbound-gateway id="submitHttpGateway"
request-channel="submit.request.channel" reply-channel="submit.reply.channel" path="/submit" supported-methods="GET">
<int-http:header name="requestAttributes" expression="#requestAttributes" />
<int-http:header name="requestParametersMap" expression="#requestParams" />
</int-http:inbound-gateway>
<int:channel id="submit.request.channel" />
<int:publish-subscribe-channel id="submit.reply.channel">
<int:interceptors>
<int:ref bean="replyChannelInterceptor" />
</int:interceptors>
</int:publish-subscribe-channel>
Thanks in advance for your help!
The only "easy" way is to explicitly send the reply via the output-channel on the last endpoint.
In fact, all that happens when you send to a declared channel is the reply channel is simply bridged to the replyChannel header.
You could do it by saving off the replyChannel header in another header, set the replyChannel header to some other channel (which you can intercept); then restore the replyChannel header to the saved-off channel before the reply is returned to the gateway.
EDIT:
Sample config...
<int:channel id="in" />
<int:header-enricher input-channel="in" output-channel="next">
<int:header name="origReplyChannel" expression="headers['replyChannel']"/>
<int:reply-channel ref="myReplies" overwrite="true" />
</int:header-enricher>
<int:router input-channel="next" expression="payload.equals('foo')">
<int:mapping value="true" channel="channel1" />
<int:mapping value="false" channel="channel2" />
</int:router>
<int:transformer input-channel="channel1" expression="payload.toUpperCase()" />
<int:transformer input-channel="channel2" expression="payload + payload" />
<int:channel id="myReplies" />
<!-- restore the reply channel -->
<int:header-enricher input-channel="myReplies" output-channel="tapped">
<int:reply-channel expression="headers['origReplyChannel']" overwrite="true" />
</int:header-enricher>
<int:channel id="tapped">
<int:interceptors>
<int:wire-tap channel="loggingChannel" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="loggingChannel" log-full-message="true" logger-name="tapInbound"
level="INFO" />
<!-- route reply -->
<int:bridge id="bridgeToNowhere" input-channel="tapped" />
Test:
MessageChannel channel = context.getBean("in", MessageChannel.class);
MessagingTemplate template = new MessagingTemplate(channel);
String reply = template.convertSendAndReceive("foo", String.class);
System.out.println(reply);
reply = template.convertSendAndReceive("bar", String.class);
System.out.println(reply); }
Result:
09:36:30.224 INFO [main][tapInbound] GenericMessage [payload=FOO, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#fba92d3, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#fba92d3, id=326a610f-80c6-5b74-0158-e3644b732aab, origReplyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#fba92d3, timestamp=1442496990223}]
FOO
09:36:30.227 INFO [main][tapInbound] GenericMessage [payload=barbar, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#662b4c69, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#662b4c69, id=d161917c-ca73-a5a9-d0f1-d7a4346a459e, origReplyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#662b4c69, timestamp=1442496990227}]
barbar
Can I send distinct messages (distinct serialize/deserialize) to the same tcp server (same host and port) and differentiate the tcp-inbound-gateway by some value of header or payload with a router???
(...)
I want to add a router for select the correct tcp-inbound-gateway depends on some field in header or payload (for example named typedata). In what order would enter the request (or message) between router, tcp-inbound-gateway, tcp-connection-factory, serialize/deserialize? Will I have problems with the serialization/deserialization for chose the tcp-inbound-gateway? What is the correct way to do this?
Thanks in advance.
EDIT
In server:
<!-- Common -->
<int:channel id="channelConnectionFactoryRequest" />
<int:channel id="channelConnectionFactoryResponse" />
<router method="getDestinationChannel"
input-channel="channelSConnectionFactoryRequest">
<beans:bean class="org.mbracero.integration.CustomRouter" />
</router>
<beans:bean id="customSerializerDeserializer"
class="org.mbracero.integration.serialization.CustomSerializerDeserializer" />
<int-ip:tcp-connection-factory id="customConnectionFactory"
type="server" port="${payment.port}" single-use="true"
serializer="customSerializerDeserializer"
deserializer="customSerializerDeserializer" />
<int-ip:tcp-inbound-gateway id="customInboundGateway"
connection-factory="customConnectionFactory"
request-channel="channelCustomConnectionFactoryRequest"
reply-channel="channelCustomConnectionFactoryResponse"
error-channel="errorChannel" />
<!-- Custom -->
<beans:bean id="operations"
class="org.mbracero.integration.applepay.impl.OperationsImpl" />
<!-- Operation One -->
<int:channel id="operationOneRequest" />
<int:service-activator input-channel="operationOneRequest"
output-channel="operationOneResponse" ref="operations" method="getOperationOne" />
<!-- Operation Two -->
<int:channel id="operationTwoRequest" />
<int:service-activator input-channel="operationTwoRequest"
output-channel="operationTwoResponse" ref="operations" method="getOperationTwo" />
OperationsImpl:
ResultOne getOperationOne(RequestOne request);
ResultTwo getOperationOne(RequestTwo request);
ResultOne & ResultTwo implements ResultBase. And in serialize of customSerializerDeserializer I have:
#Override
public void serialize(ResultBase arg0, OutputStream arg1) throws IOException {
byte[] xxx = XXX.getBytes();
arg1.write(xxx);
byte[] yyy = yyy.getBytes();
arg1.write(senderName);
// **Each custom object have a method for serialize their own data**
arg0.transformDataToByte(arg1);
arg1.flush();
}
In client:
<gateway id="tmGateway"
service-interface="org.mbracero.integration.CustomGateway" />
<beans:bean id="operationOneSerializerDeserializer"
class="org.mbracero.integration.serialization.OperationOneSerializerDeserializer" />
<int-ip:tcp-connection-factory id="operationOneFactory"
type="client" host="127.0.0.1" port="7878" single-use="true"
serializer="operationOneSerializerDeserializer" deserializer="operationOneSerializerDeserializer" />
<int-ip:tcp-outbound-gateway id="operationOneOutGateway"
request-channel="operationOneChannel" connection-factory="operationOneFactory"
request-timeout="5000" reply-timeout="5000" remote-timeout="5000" />
<beans:bean id="operationTwoSerializerDeserializer"
class="org.mbracero.integration.operationTwoRequestSerializerDeserializer"/>
<int-ip:tcp-connection-factory id="operationTwoFactory"
type="client" host="127.0.0.1" port="7878" single-use="true"
serializer="operationTwoSerializerDeserializer"
deserializer="operationTwoSerializerDeserializer" />
<int-ip:tcp-outbound-gateway id="operationTwoOutGateway"
request-channel="operationTwoChannel" connection-factory="operationTwoFactory"
request-timeout="50000" reply-timeout="50000" remote-timeout="50000" />
CustomGateway:
#Gateway(requestChannel="operationOneChannel")
OperationOneResponse sendOperationOne(OperationOneRequest request);
#Gateway(requestChannel="operationTwoChannel")
OperationTwoResponse sendOperationTwo(OperationTwo request);
You cannot have 2 server connection factories listening on the same port. TCP doesn't allow it - the network stack wouldn't know which server socket to route the request to.
There's no problem on the client side but, with a single socket, the server would have to understand how to deserialize both data types.
It's probably easier to combine the serializers/deserializers into one on both sides (add another header to the message so the deserializer knows what type of payload to decode).
I need to send HTTP Request to publish some data in a WS
E.g:
http://localhost:8081/hello/publishAMANSequence/filter/sequenceGenerationTime=1696-09-01T00:00:00Z&AMANId=B1&landingSequenceEntry=11234567890EST
I take this fault from server:
Parameter should be ordered in the following sequence: [sequenceGenerationTime, AMANId, landingSequenceEntry]
I'm doing something wrong in the order?
mule flow:
<jms:activemq-connector name="Active_MQ1" brokerURL="tcp://localhost:61616" validateConnections="true" doc:name="Active MQ"/>
<flow name="jmsFlow1" doc:name="jmsFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="hello" doc:name="HTTP"/>
<cxf:jaxws-service doc:name="SOAP" serviceClass="aero.itec.amansequenceservice.AMANSequenceInfo"/>
<component doc:name="Java" class="implementations.AMANSequenceImpl"/>
<mulexml:object-to-xml-transformer doc:name="Object to XML"/>
<jms:outbound-endpoint queue="StudioIN" connector-ref="Active_MQ1" doc:name="JMS"/>
<logger message="#[message.payload]" level="INFO" doc:name="Logger"/>
</flow>
The url which you have provided is wrong. There should be a questionmark(?) after "filter". Then only it will consider it as parameters
http://localhost:8081/hello/publishAMANSequence/filter?sequenceGenerationTime=1696-09-01T00:00:00Z&AMANId=B1&landingSequenceEntry=11234567890EST
Moreover if you are trying to access a webservice you can't do that with a HTTP GET. You need to send it as a SOAP request. You can use APIs like CXF, AXIS etc.
Following this tutorial ~
I create a flow for publish the WS , that have an inbound-endpoint where I do the request
publish.flow:
<jms:activemq-connector name="Active_MQ1" brokerURL="tcp://localhost:61616" validateConnections="true" doc:name="Active MQ"/>
<flow name="jmsFlow1" doc:name="jmsFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="hello" doc:name="HTTP"/>
<cxf:jaxws-service doc:name="SOAP" serviceClass="aero.itec.amansequenceservice.AMANSequenceInfo" >
<cxf:jaxb-databinding/>
<cxf:inInterceptors>
<spring:bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
</cxf:inInterceptors>
<cxf:outInterceptors>
<spring:bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
</cxf:outInterceptors>
</cxf:jaxws-service>
<component doc:name="Java" class="implementations.AMANSequenceImpl"/>
<object-to-string-transformer doc:name="Object to String"/>
<jms:outbound-endpoint queue="StudioIN" connector-ref="Active_MQ1" doc:name="JMS"/>
<logger message="#[message.payload]" level="INFO" doc:name="Logger"/>
</flow>
Then I create a client.class where I set the variable values:
client.java
public class AMANwsClient extends AbstractTransformer{
#Override
protected Object doTransform(Object src, String enc)
throws TransformerException {
AMANSequence sequence = new AMANSequence();
XMLGregorianCalendar fec;
sequence.setSequenceGenerationTime(fec);
sequence.setAMANId("AA");
System.out.println(sequence);
return sequence;
}
This class is used like a transformer, we don't need to pass parameters in the url, only need to connect to the endpoint URL
Finally, create a client.flow
<custom-transformer class="implementations.AMANwsClient" name="AMANwsClient" />
<flow name="csvPublisher">
<transformer ref="AMANwsClient" />
<object-to-string-transformer doc:name="Object to String"/>
<outbound-endpoint address="http://localhost:63081/hello" exchange-pattern="request-response">
<cxf:jaxws-client clientClass="aero.itec.amansequenceservice.AMANSequenceInfo_Service" port="AMANSequenceInfoService" operation="publishAMANSequence">
<cxf:inInterceptors>
<spring:bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
</cxf:inInterceptors>
<cxf:outInterceptors>
<spring:bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
</cxf:outInterceptors>
</cxf:jaxws-client>
</outbound-endpoint>
</flow>
Now I can save the payload into a JMS Queue and reproduce the payload from Browser, console and JMS.
If you have some suggestion to improve program performance, I'm open to listen to it.
Put http://localhost:8081/hello?wsdl in SOAPUI ... It will create the request and response there ... then you can pass the values in the request and invoke the webservice ...
Please check the following for your reference :-
http://developers-blog.org/blog/default/Webservice-testing-with-soapUI and http://quicksoftwaretesting.com/soapui-web-service-testing-tool/
I'm using camel and cxf component to get some data from web-service. In some case web-service returns standard soap:fault.
I have the next steps:
<camel:route id="someId">
<camel:onException useOriginalMessage="false">
<camel:exception>java.lang.Exception</camel:exception>
<camel:handled>
<camel:constant>false</camel:constant>
</camel:handled>
<camel:process ref="defaultNaoIntegrationErrorHandler" />
<camel:to uri="ref:naointegration.checkAvailability.jms.error.queue" />
</camel:onException>
<camel:from uri="direct:naoCheckAvailabilityOut" />
<camel:marshal ref="soapjaxbSAP" />
<camel:to id="naoCheckAvailabilityEndpoint" uri="cxf:bean:naoCheckAvailabilityEndpoint" />
<camel:unmarshal ref="soapjaxbSAP" />
</camel:route>
where naoCheckAvailabilityEndpoint is:
<cxf:cxfEndpoint id="naoCheckAvailabilityEndpoint"
address="${naointegration.I011.CheckAvailability.soap.address}"
endpointName="s:checkAvailabilityEndpoint" serviceName="s:SOAPService"
xmlns:s="http://www.example.com/test">
<cxf:properties>
<entry key="dataFormat" value="MESSAGE" />
<entry key="setDefaultBus" value="true" />
</cxf:properties>
<cxf:outInterceptors>
<ref bean="logOutbound" />
</cxf:outInterceptors>
<cxf:inFaultInterceptors>
<ref bean="logOutbound" />
</cxf:inFaultInterceptors>
</cxf:cxfEndpoint>
If I'm getting normal soap message everything is ok. And when I'm getting soap foalt/http 500 I have just string message which contains soap message (xml) with soap fault.
Reading cxf and camel mail lists on like problems I understood cxf endpoint should throw exception if there soap foalt, exception of type org.apache.cxf.binding.soap.SoapFault, but I can not get it.
The goal is onException clause do handling of soap fault exception.
Any suggestions?
You are using the MESSAGE data format, which means the camel-cxf endpoint just pass the stream from the transport, it will not read the under layer message, so the camel-cxf endpoint cannot tell which message is normal soap message or soap fault message.
If you want to let the camel route deal with the soap fault, you need to use the PAYLOAD or POJO data format.
I have crated a CXF JAXWS Service as below.
I have provided a custom UserNameToken validator and a callback handler.
But this is not working. It throws an error everytime.
<http:inbound-endpoint address="http://localhost:8080/HelloService" exchange-pattern="request-response">
<cxf:jaxws-service serviceClass="com.example.service.HelloServiceImpl" >
<cxf:ws-security>
<cxf:ws-config>
<cxf:property key="action" value="UsernameToken" />
</cxf:ws-config>
<cxf:ws-custom-validator >
<cxf:username-token-validator ref="customUsernameTokenValidator" />
</cxf:ws-custom-validator>
</cxf:ws-security>
<cxf:properties>
<spring:entry key="ws-security.ut.validator" value-ref="customUsernameTokenValidator" ></spring:entry>
<spring:entry key="ws-security.callback-handler" value-ref="myPasswordCallback" ></spring:entry>
</cxf:properties>
.....
.....
And a policy on my WSDL binding as below
<sp:SupportingTokens>
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy />
</sp:UsernameToken>
</wsp:Policy>
</sp:SupportingTokens>
I get the below error when I fire my request from SOAPUI with usernametoken and password.
2013-04-10 11:08:13,811 WARN [qtp1221956599-40 - /HelloService] logging.LogUtils (LogUtils.java:384) - Interceptor for {http://example.org/HelloService}ProductSOAPService#{http://example.org/HelloService}addCompany has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault
at org.mule.module.cxf.MuleInvoker.invoke(MuleInvoker.java:106)
at org.mule.module.cxf.MuleJAXWSInvoker.invoke(MuleJAXWSInvoker.java:47)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:75)
.....
.....
Caused by: java.lang.NullPointerException
at org.mule.module.cxf.MuleInvoker.invoke(MuleInvoker.java:60)
... 29 more
Please help me understand what I missing here.
Note: I have tried the Mule way of implementing the security on jaxws using cxf:ws-security and my service is working and giving proper response.
Your cxf jaxws-service should be as below. Then it should work.
<http:inbound-endpoint address="http://localhost:8080/HelloService" exchange-pattern="request-response">
<cxf:jaxws-service serviceClass="com.example.service.HelloServiceImpl" >
<cxf:properties>
<spring:entry key="ws-security.ut.validator" value-ref="customUsernameTokenValidator" ></spring:entry>
<spring:entry key="ws-security.callback-handler" value-ref="myPasswordCallback" ></spring:entry>
</cxf:properties>
</cxf:jaxws-service>