Tuesday, September 09, 2008

Build contract first web services using CXF+JAXWS+JAXB & Spring

Today at work, I had to port a spring web services based application into using CXF. I found that the documentation of apache CXF is scattered all over the place and so I decided to write this one place stop to get one running . We will develop a 'complex' webservice that takes in the first name and last name of a person and returns it in the format lastname, firstname

Assumptions

1) You are familiar with the basics of Maven.
2) The steps outlined will have a affinity to use Eclipse as the IDE.
3) You will need JDK 1.5 or higher coz we will be using annotations.

The first step in developing the web service is to get your build system up.

Task 1: Setup the project in Eclipse.

Issue the command


mvn archetype:create -DgroupId=com.cxftest -DartifactId=CXFTest



This creates the basic skeleton of the project. Next, cd in to the cxftest directory and issue the following commands

mkdir src/main/webapp

mkdir src/main/resources

Next run


mvn eclipse:clean eclipse:eclipse -Dwtpversion=1.5


This creates the basic structure of your eclipse project.

Now lets add the dependencies needed by the project

<properties>
<cxf.version>2.1</cxf.version>
<spring.version>2.5</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-core</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-common-utilities</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
</dependencies>



Task 2 :Define the contract

Define the schema / contract for communication.
a) Define the xsd schema for communication; lets call it NameFormat.xsd. This file will define both the input that the system expects as well as the expected output from the system. In other words this XML document defines the contract of the system.


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.cxftest.org" elementFormDefault="qualified">


<!-- Define the request -->
<xs:complexType name="FormatRequest">
<xs:sequence>
<xs:element name="firstName" type="xs:string" />
<xs:element name="lastName" type="xs:string" />
</xs:sequence>
</xs:complexType>

<!-- Define the response -->
<xs:complexType name="FormatResponse">
<xs:sequence>
<xs:element name="formattedName" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>



Save this file under src/main/resources


Task 3: Automate all the mundane stuff.

Maven pitches in here to save a lot of time here so that we need to only concentrate on building our business logic.

First we need to setup JAXB, to execute the xjc task so that it generates Java classes from the XSD that we defined earlier.

for that add the following code to your POM.xml under the plugins section


<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<packageName>com.cxftest.model</packageName> <!-- The name of your source package under which the source classes get generated -->
<schemaDirectory>src/main/resources</schemaDirectory> <!-- The path where you have placed the XSD -->
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>


These two plugins will read the XSD and generate the source code of the expected inputs and output objects.

This is how the final POM will look


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cxftest</groupId>
<artifactId>CXFTest</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>CXFTest</name>
<url>http://maven.apache.org</url>
<properties>
<cxf.version>2.1</cxf.version>
<spring.version>2.5</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-core</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-common-utilities</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<!--
The name of your source package under which the source classes get
generated
-->
<packageName>com.cxftest.model</packageName>
<!--
The path where you have placed the XSD from which the code needs
to generated
-->
<schemaDirectory>src/main/resources</schemaDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>

</plugins>
</build>
</project>



Task 4: Code the web service.

Now code the implementation of the web services. Following the grand tradition of coding to interfaces lets define the interface for the web service as shown below.



package com.cxftest.service;

import javax.jws.WebMethod;
import javax.jws.WebService;

import com.cxftest.model.FormatRequest;
import com.cxftest.model.FormatResponse;


@WebService(targetNamespace="http://www.cxftest.org")
public interface NameFormatterService {
@WebMethod
public abstract FormatResponse formatName(FormatRequest request);

}




After this is done, lets Code the implementation of what our web service is supposed to do.

/**
*
*/
package com.cxftest.service;

import javax.jws.WebMethod;
import javax.jws.WebService;

import com.cxftest.model.FormatRequest;
import com.cxftest.model.FormatResponse;

/**
* @author jvictor
*
*/


@WebService (targetNamespace="http://www.cxftest.org", endpointInterface="com.cxftest.service.NameFormatterService")
public class NameFormatterServiceImpl implements NameFormatterService {

/* (non-Javadoc)
* @see com.cxftest.service.NameFormatterService#formatName(com.cxftest.model.FormatRequest)
*/

@WebMethod
public FormatResponse formatName(FormatRequest request){
FormatResponse response = new FormatResponse();
response.setFormattedName(request.getLastName() "," request.getFirstName());
return response;
}

}



Task 5: Wire up everything

The first thing that we need to do is to declare a servlet in our web.xml and configure it to receive all incoming requests. My web .xml looks like this



<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:appContext.xml</param-value>
</context-param>
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>



Note the <context-param> and <listener> are being defined in the web.xml, this is how we bootstrap spring.
Also note how an instance of org.apache.cxf.transport.servlet.CXFServlet is used to service all requests.

The final part in the JIGSAW is the spring application context xml definition for spring.



<?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:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"
default-dependency-check="none" default-lazy-init="false">

<!-- Load the needed resources that are present in the cxf* jars -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<!-- Hook up the web service -->
<jaxws:endpoint
id="formatterService"
implementor="com.cxftest.service.NameFormatterServiceImpl"
address="/format" />

</beans>



Task 6:
Finally run


mvn eclipse:clean eclipse:eclipse -Dwtpversion=1.5
mvn clean compile


Refresh the project and fire up the webservice !

Labels:

12 Comments:

At 8:37 PM , Blogger ed said...

Hi Victor,
Thanks for writing this blog. this looks neat and understandable but still it looks like some holes.
I haven't understand from Task 5.
my questions are:
1. After creating web.xml file what is next? you given some xml code but what is that file name of xml?
2. where to keep that xml file? i mean in which folder
3. After excute final command then how to run example?
4. where is client code to access services?
5. After we did all from command prompt then do we need to import it into eclipse or not? if yes then as what type(java, maven or web) of projects?

In task 1 mkdir path is not working. change as mkdir \src\main\webapp then it's working.

please give me some clues on above quires.

Many Thanks
sduser

 
At 1:07 AM , Blogger Victor, Juby said...

1. After creating web.xml file what is next? you given some xml code but what is that file name of xml?

The file in Step 5 is the applicationContext.xml It is the spring config file. It can be placed anywhere in the classpath.

2. where to keep that xml file? i mean in which folder

I would place it under src/main/resources

3. After excute final command then how to run example?

You need a eclipse+wtp setup with a server such as tomcat. Rt Click Run as > Run on server

4. where is client code to access services?

You can use a tool called soapUI. First you need to run the application and get the wsdl url from the running application.

After we did all from command prompt then do we need to import it into eclipse or not? if yes then as what type(java, maven or web) of projects?

This tutorial assumes that you are using Eclipse + WTP bundle as the IDE. you execute all the commands from the workspace of your eclipse setup.

Its a WTP web project, You just need to import the project if your project is not in the workspace. The Eclipse+WTP setup will take care of converting the project into a web project based on the config files generated by maven.


In task 1 mkdir path is not working. change as mkdir \src\main\webapp then it's working.

Are you using windows ? if so / will not work. (I gave up on windows after they released vista.)

 
At 11:30 PM , Anonymous Joe Morrison said...

Hi Victor,
Thanks for this - very helpful for getting up and running with CXF. But isn't it still a code-first approach? It doesn't look like the NameFormat.xsd file is actually used in the code or enforced in any way. I tried changing the type names in NameFormat.xsd and the example still worked.

 
At 1:38 AM , Blogger Victor, Juby said...

Well its an open ended question, Upfront CXF is not as contract first-ish when compared to stuff like Spring-WS.

My perspective is that the data contract is defined in the nameformat.xsd file. The java classes needed to define the contract are generated at runtime. So you typically focus on what the WS has to do rather than writing boiler plate code to define the model and all the other nuances.

 
At 2:57 PM , Blogger Rares said...

As far as I know, spring-ws uses the same approach for contract-first (only one supported) by defining the contract as an .xsd schema, then generating the wsdl from that

 
At 3:17 PM , Anonymous Itsnavigator said...

Thanks for the tip, good work..

 
At 3:58 PM , Anonymous Anonymous said...

hi victor,
i recently started with web services.i first started with web service classes and generated the wsdl using java2wsdl. then i generated client using wsdl2java.but was unable to execute the client. i am using springs and cxf.it works fine with JaxWsProxyFactoryBean.but wanted to generate a client using wsdl.

 
At 4:08 AM , Anonymous swastikwebby said...

http://www.swastikwebby.com

hello, our company provide highly experienced and talented web designer,our capacity to build a powerful and attractive Finance comparison website.Specially For Traveling website / Real Estate / Matrimonial / Visa / eShopping / Education / School / College / Custom Websites.

contact us: +91 972 570 7728

 
At 5:16 PM , Blogger Simon said...

Hi, I have a WSDL for my webservice. You use an xsd. Is there any way to generate code from WSDL instead?

I tried the soapui nature, which has has a generate code method, but it always crashes eclipse when you fire it up.

 
At 5:47 PM , Blogger Rares Barbantan said...

This comment has been removed by the author.

 
At 11:21 AM , Blogger shiva said...

thanks victor your blog is very helpful

 
At 7:37 PM , Anonymous Anonymous said...

Thank you Victor - your tutorial helped me a lot!

Jarek

 

Post a Comment

Subscribe to Post Comments [Atom]

Links to this post:

Create a Link

<< Home