Search This Blog

Wednesday, 17 July 2013

Conduits in CXF

If we observe any page request sent by a browser, it will be seen to be composed of multiple headers. Consider the below screenshot taken for a simple AJAX request.
As seen the browser has set several request headers. When using web services with HTTP transport layer we should also be able to take advantage of these settings. For e.g. the gzip encoding if supported by the header could reduce the size of the response while sent over the network. Chunking is also an option for improved performance.
But with web services how do we specify these headers ?
This is where CXF conduits come into the picture. Taken verbatim from the book Apache CXF Web Service Development:
Conduit simply means channel or pipe. HTTP conduits are channels allow us to apply 
certain HTTP related properties or attributes which can affect the way  messages are 
exchanged between endpoints. You typically specify HTTP connection attributes such as 
whether to allow chunking, connection timeout, and so on. The  conduit is always 
defined by the client or consumer of the service
I decided to create a conduit for my CXF client. The changes are all in the configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
 http://cxf.apache.org/transports/http/configuration 
 http://cxf.apache.org/schemas/configuration/http-conf.xsd">

 <jaxws:client id="client" serviceClass="com.code.first.ws.server.IEcho"
  address="http://localhost:8080/WithSpring/services/echo">
  <jaxws:inInterceptors>
   <bean id="loggingInInterceptor" 
            class="org.apache.cxf.interceptor.LoggingInInterceptor" >
    <property name="prettyLogging" value="true" />
   </bean>
  </jaxws:inInterceptors>
  <jaxws:outInterceptors>
   <bean id="loggingOutInterceptor" 
             class="org.apache.cxf.interceptor.LoggingOutInterceptor" >
    <property name="prettyLogging" value="true" />
   </bean>
  </jaxws:outInterceptors>
 </jaxws:client>


 <http-conf:conduit name="{http://server.ws.first.code.com/}IEchoPort.http-conduit">
  <http-conf:client Accept="text/xml" AcceptEncoding="gzip,deflate,sdch"
   AllowChunking="true" AutoRedirect="false" CacheControl="No-Cache"
   ContentType="text/xml" />
 </http-conf:conduit>  
  
</beans>
As seen we added a new element the conduit. This is from a different workspace. CXF requires that the name attribute's value be defined in a certain manner:
{WSDL_endpoint_target_namespace}PortName.http-conduit
You don't have to worry too much of getting it right. The CXF client logs automatically indicate the expected name if you provide the wrong one.
2171 [main] DEBUG org.apache.cxf.configuration.spring.ConfigurerImpl  - Could n
ot find a definition for bean with id {http://server.ws.first.code.com/}IEchoPor
t.http-conduit - no injection will be performed.
Within the XML file, we need to provide the remaining settings for the client. The client element sets the header values.
The above configuration indicates the
  • values for the accept header, content-type header and accept-encoding header. 
  • Chunking has also been allowed. 
  • Cached responses have been deemed unacceptable. 
The code in the application remains unchanged:
public static void main(String[] args) {
 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
   "client-http-conduit.xml");
 IEcho client = (IEcho) context.getBean("client");
 String response = client.echoHi("Joe");
 System.out.println("Response: " + response);
}
The client logs indicate that the conduit has been detected and the changes applied:
2421 [main] DEBUG org.apache.cxf.transport.http.HTTPConduit  - Conduit '{http:/
/server.ws.first.code.com/}IEchoPort.http-conduit' has been (re)configured for p
lain http.
...
 2687 [main] INFO  org.apache.cxf.services.IEchoService.IEchoPort.IEcho  - Outbo
und Message
---------------------------
ID: 1
Address: http://localhost:8080/WithSpring/services/echo
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[text/xml], Accept-Encoding=[gzip,deflate,sdch], Cache-Control=
[No-Cache], Connection=[Keep-Alive], SOAPAction=[""]}
Payload: <?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns2:echoHi xmlns:ns2="http://server.ws.first.code.com/">
      <msg>Joe</msg>
    </ns2:echoHi>
  </soap:Body>
</soap:Envelope>

--------------------------------------
...
 2718 [main] DEBUG org.apache.cxf.transport.http.HTTPConduit  - Sending POST Mes
sage with Headers to http://localhost:8080/WithSpring/services/echo Conduit :{ht
tp://server.ws.first.code.com/}IEchoPort.http-conduit
As seen below conduit settings have been applied in the request. In a similar manner HTTP destination can be used to apply settings on the server side. Conduits include several more settings such as SSL, Challenge Authentication, basic authentication etc.
These settings can also be applied through code:
IEcho echo = (IEcho) context.getBean("client");
  
Client client = ClientProxy.getClient(echo);
HTTPConduit http = (HTTPConduit) client.getConduit();

HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();

httpClientPolicy.setAllowChunking(false);
httpClientPolicy.setAccept("text/xml");
httpClientPolicy.setAcceptEncoding("gzip,deflate,sdch");
httpClientPolicy.setCacheControl("No-Cache");
httpClientPolicy.setContentType("text/xml");

http.setClient(httpClientPolicy);

String response = echo.echoHi("Joe");
System.out.println("Response: " + response);
HTTPClientPolicy instance is used to set the header values. If we run the code then the logs indicate that the values were set at the client.

4 comments:

  1. Very detailed explanation of the purpose of conduit. Thank you very much

    ReplyDelete
  2. I'm using soap endpoints.I was tried to set timeout for soap calls using conduits.But not working.Below my code




    Please help me out.

    ReplyDelete
  3. Ultimate explanation. Great thanks :)

    ReplyDelete