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.

1 comment:

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

    ReplyDelete