Search This Blog

Friday, 12 April 2013

The first CXF Service

In the previous post we saw how JAX-WS 's RI implementation could be used to build and interact with web services. I decided to work with one of the other implementations - Apache CXF. I decided to create and run a code first webservice using CXF implementation.
The CXF applications are deployed in a web context. So the first step was to create a web application.
The next step is the same as we did in the last application. We created the webservice interface and implementation:
package com.code.first.ws.server;

import javax.jws.WebService;

@WebService
public interface IEcho {
 String echoHi(String text);

 String echoHiToUser(User user);
}
All the annotations used are standard JAX-WS annotations. The WebService annotation indicates that the Java class is implementing a Web Service, or (as in this case) a Java interface is defining a Web Service interface.
The implementation is as below:
package com.code.first.ws.server.impl;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebParam.Mode;
import javax.jws.WebResult;
import javax.jws.WebService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.code.first.ws.server.IEcho;
import com.code.first.ws.server.User;

@WebService(endpointInterface = "com.code.first.ws.server.IEcho", name = "EchoWebService",  
   portName = "EchoImplPort", serviceName = "EchoImplService", 
   targetNamespace = "http://www.cfx.ws/")
public class EchoImpl implements IEcho {

 private static final Logger logger = LoggerFactory
   .getLogger(EchoImpl.class);

 @Override
 @WebMethod(action = "hiEchoAction", operationName = "sayHi")
 public @WebResult(partName = "result", name = "rslt") String 
  echoHi( @WebParam(mode = Mode.IN, partName = "message", name = "msg") String text) {
  logger.info("echoHi called with msg as {}", text);
  return "Hi  " + text;
 }

 @Override
 @WebMethod(/* exclude = true, */ action = "echoHiToUserAction", 
   operationName = "echoHiToUser")
 public @WebResult(partName = "result") String 
  echoHiToUser(@WebParam(mode = Mode.IN, partName = "user") User user) {
  logger.info("echoHiToUser called with user id {}", user.getId());
  return "Hi " + user.getName() + " - id " + user.getId() + ", weight "
    + user.getWeight();
 }
}
Here more detailed information has been provided. Most of it is optional. The endpointInterface attribute of WebService indicates the Service endpoint Interface . Rest of the details are for use in the generated wsdl. The WebMethod annotation indicates that this method is exposed as a Web Service operation. The action attribute indicates the value for SoapAction header. If we set exclude attribute to true, then the method will not be exposed as a operation. Remaining attribute is for use in wsdl.
The last annotations are the WebParam and WebResult. WebParam will map the method parameter to a Web Service message part and XML element. WebResult is used to set the name for the returned result. The User class referred in the above code is a simple POJO.
public class User {
 private Long id;
 private String name;
 private BigDecimal weight;

 // setter getters
}
This completes the code.
Now to configure CXF:
First the web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 <!-- This file includes information of the services - needed by Spring to 
  create the beans and add them to the container -->
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
   classpath:spring-cxf-services.xml
                </param-value>
 </context-param>

 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>

 <servlet>
  <servlet-name>cxf</servlet-name>
  <servlet-class>org.apache.cxf.transport.servlet.CXFServlet
      </servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>cxf</servlet-name>
  <url-pattern>/services/*</url-pattern>
 </servlet-mapping>
</web-app>
As seen a single servlet was added to the web.xml – The CXFServlet is the start up point for the CFX system.
The CXF-Servlet is like a front controller to which all the webservice calls are routed. It then accordingly redirects the calls to appropriate webservice endpoints.
This will seem similar to the implementation of spring's -mvc framework – though the important point is CXF is not using spring-mvc. The architecture while similar to spring-mvc at no point uses anything beyond spring's core jars.
Here we have configured all urls of the form /services/* to be routed to the cxf servlet. Our webservice endpoints are treated as Spring beans and are wired here through the spring-cxf-services.xml 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"
 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">

 <!-- name of all the services exposed as beans. The path is added from here 
  ahead -->
 <jaxws:endpoint id="echoEndpointImpl" implementor="com.code.first.ws.server.impl.EchoImpl"
  address="echo" />
</beans>
The endpoint element from the http://cxf.apache.org/jaxws namespace configures a JAX-WS Web service endpoint. Here we have configured our implementation to the address “echo”.
The URL for the webservice is now <application-context>/services/echo.
What of the libraries ?
 The application can now be deployed on an application server. I used Apache Tomcat 7.
On starting the server:
2125 [pool-2-thread-1] DEBUG org.springframework.beans.factory.xml.XmlBeanDefini
tionReader  - Loaded 1 bean definitions from location pattern [classpath:spring-
cxf-services.xml]
...
5531 [pool-2-thread-1] INFO  org.apache.cxf.service.factory.ReflectionServiceFac
toryBean  - Creating Service {http://www.cfx.ws/}EchoImplService from class com.
code.first.ws.server.IEcho
...
8406 [pool-2-thread-1] DEBUG org.apache.cxf.jaxb.JAXBDataBinding  - Created JAXB
Context "jar:file:/C:/Program%20Files/Java/jdk1.6.0/jre/lib/rt.jar!/com/sun/xml/
internal/bind/v2/runtime/JAXBContextImpl.class Build-Id: 1.6.0-rc
Classes known to this context:
  [B
  boolean
  byte
  char
  com.code.first.ws.server.User
  com.code.first.ws.server.jaxws_asm.EchoHi
  com.code.first.ws.server.jaxws_asm.EchoHiResponse
  com.code.first.ws.server.jaxws_asm.EchoHiToUser
  com.code.first.ws.server.jaxws_asm.EchoHiToUserResponse
  com.sun.xml.internal.bind.api.CompositeStructure
...
11000 [pool-2-thread-1] INFO  org.apache.cxf.endpoint.ServerImpl  - Setting the 
server's publish address to be echo
...
11125 [pool-2-thread-1] DEBUG org.apache.cxf.endpoint.ServerImpl  - Server is st
arting.
...
11266 [pool-2-thread-1] INFO  org.springframework.web.context.ContextLoader  - R
oot WebApplicationContext: initialization completed in 11250 ms
As seen, the request and response wrappers were automatically generated. The service was also created with QName  {http://www.cfx.ws/}EchoImplService
This completes the web service implementation. In the next post we shall create a client for the service and test it.

3 comments:

  1. thanks for share....

    ReplyDelete
  2. This is post very good, and useful for me, thank you very much..

    ReplyDelete
  3. All the techniques on CXF service application that you have posted in your blog are so important as it gives a lot of advantage to individual programmers to learn the advance coding structures.

    ReplyDelete