Apache CXF: JAX-RS Restful web service + Integrating with Spring & Hibernate

In this article, we will extend previous article and integrate with Spring & Hibernate for database interaction. In the previous article, constructed a dummy objects for web service. But here we will use Hibernate’s rich API to interact with MySql database or in general any ORM compliant database

As we are using both XML & JSON for web service request/response, we will design XSD for our service. Then invoke the services of Maven’s JAXB plugin to generate java-sources, as this is considered standard

Note: It’s a good practice to design XSD, if one intended to use XML for web service request/response

Annotation Used

  • @Path (javax.ws.rs.Path)
  • @GET (javax.ws.rs.GET)
  • @POST (javax.ws.rs.POST)
  • @PUT (javax.ws.rs.PUT)
  • @DELETE (javax.ws.rs.DELETE)
  • @PathParam (javax.ws.rs.PathParam)
  • @Consumes (javax.ws.rs.Consumes)
  • @Produces (javax.ws.rs.Produces)
  • @Service (org.springframework.stereotype.Service)
  • MediaType (javax.ws.rs.core.MediaType)

Technology Used

  • Java 1.7
  • Eclipse Luna IDE
  • Spring-4.0.0-RELEASE
  • Hibernate-4.2.2.Final
  • Apache-CXF-3.0.0
  • Apache Maven 3.2.1
  • Apache Tomcat 7.0.54
  • MySql-Connector-Java-5.1.31

Mavenize or download required jars

Add Apache-CXF-3.0.0, Spring-4.0.0-RELEASE, Hibernate-4.2.2.Final & MySql-Connector-Jav-5.1.31 dependencies to pom.xml

	<dependencies>
			<!-- Apache CXF -->
			<dependency>
				<groupId>org.apache.cxf</groupId>
				<artifactId>cxf-rt-frontend-jaxrs</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-rt-transports-http-jetty</artifactId>
				<version>${cxf.version}</version>
			</dependency>

			<!-- for JSON support in Apache-CXF Restful web service -->
			<dependency>
				<groupId>org.codehaus.jackson</groupId>
				<artifactId>jackson-jaxrs</artifactId>
				<version>${jackson.version}</version>
			</dependency>

			<!-- Spring framework -->
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-core</artifactId>
				<version>${spring.version}</version>
			</dependency>
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-context</artifactId>
				<version>${spring.version}</version>
			</dependency>
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-web</artifactId>
				<version>${spring.version}</version>
			</dependency>
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-orm</artifactId>
				<version>${spring.version}</version>
			</dependency>

			<!-- Hibernate Core -->
			<dependency>
				<groupId>org.hibernate</groupId>
				<artifactId>hibernate-core</artifactId>
				<version>${hibernate.version}</version>
			</dependency>
			<dependency>
				<groupId>org.hibernate</groupId>
				<artifactId>hibernate-ehcache</artifactId>
				<version>4.3.0.Beta3</version>
			</dependency>

			<!-- MySql-Connector -->
			<dependency>
				<groupId>mysql</groupId>
				<artifactId>mysql-connector-java</artifactId>
				<version>5.1.31</version>
			</dependency>

			<!-- Apache HTTP components for writing test client -->
			<dependency>
				<groupId>commons-httpclient</groupId>
				<artifactId>commons-httpclient</artifactId>
				<version>3.1</version>
				<scope>compile</scope>
			</dependency>
			<dependency>
				<groupId>org.apache.httpcomponents</groupId>
				<artifactId>httpclient</artifactId>
				<version>${apache.httpcomponents.version}</version>
				<scope>compile</scope>
			</dependency>
			<dependency>
				<groupId>org.apache.httpcomponents</groupId>
				<artifactId>httpcore</artifactId>
				<version>${apache.httpcomponents.version}</version>
				<scope>compile</scope>
			</dependency>
			<dependency>
				<groupId>org.apache.httpcomponents</groupId>
				<artifactId>httpmime</artifactId>
				<version>${apache.httpcomponents.version}</version>
				<scope>compile</scope>
			</dependency>
	</dependencies>

Folks who aren’t familiar with Maven concepts or don’t require maven for their project, can download the below jars individually from the spring site or maven repository and include them in the classpath

JAXB – Generating java source files from XSD

Steps to generate java-sources from XML Schema Definition (XSD)

  • configure JAXB Maven plugin in pom.xml
  • write well-defined XSD for your service
  • use maven command “mvn generate-sources” to generate java source files

Configure JAXB Maven plugin

<!-- JAXB plugin to generate-sources from XSD -->
<plugin>
	<groupId>org.codehaus.mojo</groupId>
	<artifactId>jaxb2-maven-plugin</artifactId>
	<version>1.5</version>
	<executions>
		<execution>
			<goals>
				<goal>xjc</goal><!-- xjc/generate -->
			</goals>
			<configuration>
				<outputDirectory>${basedir}/generated/java/source</outputDirectory>
				<schemaDirectory>${basedir}/src/main/resources/com/apache/cxf/spring/hibernate/entities
				</schemaDirectory>
				<schemaFiles>*.xsd</schemaFiles>
				<schemaLanguage>XMLSCHEMA</schemaLanguage>
				<extension>true</extension>
				<args>
					<arg>-XtoString</arg>
				</args>
				<plugins>
					<plugin>
						<groupId>org.jvnet.jaxb2_commons</groupId>
						<artifactId>jaxb2-basics</artifactId>
						<version>0.6.4</version>
					</plugin>
				</plugins>
			</configuration>
		</execution>
	</executions>
</plugin>

Player.xsd

Below XSD contains two elements with name “PlayerType” and “PlayerListType”

  • PlayerType contains four attributes namely playerId, name, age and matches
  • PlayerListType which returns list of PlayerType
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	targetNamespace="http://benchresources.in/cdm/Player" xmlns:tns="http://benchresources.in/cdm/Player"
	elementFormDefault="qualified">

	<!-- player object with four attributes -->
	<xsd:element name="PlayerType">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="playerId" type="xsd:int" />
				<xsd:element name="name" type="xsd:string" />
				<xsd:element name="age" type="xsd:int" />
				<xsd:element name="matches" type="xsd:int" />
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>

	<!-- an object to contain lists of players referencing above player object -->
	<xsd:element name="PlayerListType">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element ref="tns:PlayerType" minOccurs="0"
					maxOccurs="unbounded" />
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>
</xsd:schema>

Run mvn generate-sources

Look at the generated java source files in the generated folder

PlayerType.java

package in.benchresources.cdm.player;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "playerId",
    "name",
    "age",
    "matches"
})
@XmlRootElement(name = "PlayerType")
public class PlayerType {

    protected int playerId;
    @XmlElement(required = true)
    protected String name;
    protected int age;
    protected int matches;

    public int getPlayerId() {
        return playerId;
    }
    public void setPlayerId(int value) {
        this.playerId = value;
    }
    public String getName() {
        return name;
    }
    public void setName(String value) {
        this.name = value;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int value) {
        this.age = value;
    }
    public int getMatches() {
        return matches;
    }
    public void setMatches(int value) {
        this.matches = value;
    }
}

PlayerListType.java

package in.benchresources.cdm.player;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "playerType"
})
@XmlRootElement(name = "PlayerListType")
public class PlayerListType {

    @XmlElement(name = "PlayerType")
    protected List<PlayerType> playerType;
    public List<PlayerType> getPlayerType() {
        if (playerType == null) {
            playerType = new ArrayList<PlayerType>();
        }
        return this.playerType;
    }
}

Directory Structure

Before moving on, let us understand the directory/package structure once you create project in Eclipse IDE
Maven has to follow certain directory structure

  • src/test/java –> test related files, mostly JUnit test cases
  • src/main/java –> create java source files under this folder
  • src/main/resources –> all configuration files placed here
  • generated/java/source –> generated java source files are placed here
  • Maven Dependencies or Referenced Libraries –> includes jars in the classpath
  • WEB-INF under webapp –> stores web.xml & other configuration files related to web application

Project Structure (Package Explorer view in Eclipse)

1_ApacheCXF-Spring-Hibernate_Project_Structure_In_Eclipse

 

Jars Libraries Used in the Project (Maven Dependencies)

It is quite long, just shown starting 60% of jars

2_ApacheCXF-Spring-Hibernate_Jars_Used_In_Classpath

Database: Creating table and inserting few records for this example
Create Table command

CREATE TABLE `PLAYER` (
  `PLAYER_ID` INT(6) NOT NULL AUTO_INCREMENT,
  `NAME` VARCHAR(50) NOT NULL,
  `AGE` INT(3) NOT NULL,
  `MATCHES` INT(3) NOT NULL,
  PRIMARY KEY (`PLAYER_ID`)
);

Insert command (examples)

INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Sachin Tendulkar",41,200);
INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Shane Warne",44,145);
INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Kevin Pietersen",34,104);
INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Shahid Afridi",35,27);
INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Brian Lara",45,131);
INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Graeme Smith",34,117);
INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Mahela Jayawardene",37,145);
INSERT INTO `PLAYER`(`NAME`, `AGE`, `MATCHES`) VALUES ("Steve Waugh",49,168);

Web application

For any web application, entry point is web.xml which describes how the incoming http requests are served / processed. Further, it describes about the global-context and local-context param (i.e.; <context-param> & <init-param>) for loading files particular to project requirements & contains respective listener

With this introduction, we will understand how we configured web.xml for Apache CXF JAX-RS Restful web service + integrating Spring/Hibernate

web.xml (the entry point –> under WEB-INF)

This web.xml file describes,

  • Like any JEE web framework register org.apache.cxf.transport.servlet.CXFServlet with servlet container
  • All http requests with URL pattern “/services/*” will be sent to the registered servlet called “CXFServlet” (org.apache.cxf.transport.servlet.CXFServlet)
  • <context-param> with its attributes describes the location of the “apache-cxf-service.xml” and ApplicationContext.xml files from where it has to be loaded. We will discuss briefly about these files
  • <welcome-file-list> files under this tag is the start-up page

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">

	<display-name>ApacheCXF-Spring-Hibernate</display-name>

	<!-- Apache CXF -->
	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/services/*</url-pattern>
	</servlet-mapping>

	<!-- web context param -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			WEB-INF/apache-cxf-services.xml,
			WEB-INF/ApplicationContext.xml
		</param-value>
	</context-param>

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

	<!-- welcome file list -->
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

Apache CXF services

Apache CXF comes with spring based configuration, so it is easy to register beans in the spring container much like we do any bean in spring application. CXFServlet receives the incoming http requests and invokes corresponding registered beans in according to http url path

NOTE: For this Restful JAX-RS application, we are using Spring annotations to define/register beans in the spring container thereby avoiding lot of boilerplate code to write

This apache-cxf-services.xml describes,

  • <jaxrs:server /> defines which service bean to be invoked for the incoming http requests. In this case, any wild card pattern “/” will invoke “playerService” which is registered as service bean using @Service(“playerService”) annotation (on top of the PlayerServiceImpl java class)
  • bean with id=”jsonProvider” defined to support JSON format
  • bean with id=”jaxbXmlProvider” defined to support XML format
  • <jaxrs:extensionMappings> with this element you can use dot notation to get result in the required format, instead of supplying the accept header parameter
  • NOTE: For two different beans we can have two different url-pattern(address) like
		<jaxrs:server id="restContainer" address="/">
			<jaxrs:serviceBeans>
				<ref bean="playerService" />
			</jaxrs:serviceBeans>
		</jaxrs:server>
		
		<jaxrs:server id="twotest" address="/two">
			<jaxrs:serviceBeans>
				<ref bean="testService" />
			</jaxrs:serviceBeans>
		</jaxrs:server>

apache-cxf-services.xml

<?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:jaxrs="http://cxf.apache.org/jaxrs" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">

	<!-- to support Java-to-JSON and vice-versa conversion -->
	<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />

	<!-- to support Java-to-XML and vice-versa conversion -->
	<bean id="jaxbXmlProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider" />

	<!-- CXFServlet configured in web.xml sends requests here -->
	<jaxrs:server id="restContainer" address="/">
		<jaxrs:serviceBeans>
			<ref bean="playerService" />
		</jaxrs:serviceBeans>

		<jaxrs:extensionMappings>
			<entry key="json" value="application/json" /> <!-- use .json to get data in JSON format -->
			<entry key="xml" value="application/xml" />   <!-- use .xml to get data in XML format -->
		</jaxrs:extensionMappings>

		<jaxrs:providers>
			<ref bean="jsonProvider" />
			<ref bean="jaxbXmlProvider" />
		</jaxrs:providers>
	</jaxrs:server>
</beans>

Spring Application Context

Segregated XML configuration for Apache CXF JAX-RS service and Spring context for modular approach. All spring & hibernate related configuration are placed inside this context file

This ApplicationContext.xml describes,

  • <context:annotation-config /> to activate annotation on the registered beans with application context
  • <context:component-scan base-package=”” /> tag scans all classes & sub-classes under the value of base-package attribute and register them with the Spring container
  • bean with id=”transactionManager” to inform spring to take care of the database transaction. All methods annotated with @Transactional
  • <tx:annotation-driven /> to turn ON transaction annotation on all DAO methods
  • bean with id=”sessionFactory” defines hibernate properties to let it take care of database operations using hibernate’s rich API
  • bean with id=”dataSource” defines values for driverClassName, url, username and password for MySql database
  • Note: injection series between transactionManager, sessionFactory and dataSource

ApplicationContext.xml

<?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:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

	<!-- to activate annotations in beans already registered in the application
		context -->
	<context:annotation-config />

	<!-- scans packages to find and register beans within the application context -->
	<context:component-scan base-package="com.apache.cxf.spring.hibernate.service" />

	<!-- turn on spring transaction annotation -->
	<tx:annotation-driven transaction-manager="transactionManager" />

	<!-- Transaction Manager -->
	<bean id="transactionManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<!-- Session Factory -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="annotatedClasses">
			<list>
				<value>com.apache.cxf.spring.hibernate.model.Player</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>
	</bean>

	<!-- dataSource configuration -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/benchresources" />
		<property name="username" value="root" />
		<property name="password" value="" />
	</bean>
</beans>

Let’s see coding in action

 

URL Pattern

Http URL for any common web application is http://<server>:<port>/<context-root>/<from_here_application_specific_path>

In our example, we are going to deploy war into Tomcat 7.0 server so our server & port are localhost and 8080 respectively. Context root is the project name i.e.; ApacheCXF-Spring-Hibernate. Initial path for this application is http://localhost:8080/ApacheCXF-Spring-Hibernate

We have configured “/services/*” as url-pattern for CXFServlet in web.xml and at interface-level (or say class-level) path configured is “/player” using @Path annotation. Next, respective path for each method annotated with @Path (method-level)

Model Class

Model class Player with four primitive attributes with their getter/setter && no-arg constructor, 3-arg constructor and 4-arg constructor

Also the Hibernate POJO class is annotated describing the mapping between java property and database columns

@Entity:  represents an object that can be persisted in the database and for this class should have no-arg constructor
@Table: describes which table in the database to map with this class properties
@Id: defines this is unique which means it represents primary key in the database table
@GeneratedValue: this will be taken care by hibernate to define generator sequence
@Column: tells to map this particular property to table column in the database

For example, “age” property used to map “AGE” column in the “PLAYER” table in the database
@Column(name= “AGE”)
private int age;

Note: we can add attributes to the @Column annotation like name, length, nullable and unique

Player.java

package com.apache.cxf.spring.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "PLAYER")
public class Player {

	// member variables
	@Id
	@GeneratedValue
	@Column(name = "PLAYER_ID")
	private int playerId;

	@Column(name= "NAME")
	private String name;

	@Column(name= "AGE")
	private int age;

	@Column(name= "MATCHES")
	private int matches;

	// default constructor
	public Player() {
		super();
	}

	// 3-arg parameterized-constructor
	public Player(String name, int age, int matches) {
		super();
		this.name = name;
		this.age = age;
		this.matches = matches;
	}

	// 4-arg parameterized-constructor
	public Player(int playerId, String name, int age, int matches) {
		super();
		this.playerId = playerId;
		this.name = name;
		this.age = age;
		this.matches = matches;
	}

	// getter and setter
	public int getPlayerId() {
		return playerId;
	}
	public void setPlayerId(int playerId) {
		this.playerId = playerId;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getMatches() {
		return matches;
	}
	public void setMatches(int matches) {
		this.matches = matches;
	}
}

Player Service interface

Defines simple CURD operations

  • @POST          – create/inserts a new resource (new player)
  • @GET           – read/selects internal resource representation based on the playerId
  • @PUT            – update/modifies an internal resource representation (modify player)
  • @DELETE       – delete/removes a resource (delete player)
  • @GET           – retrieves all players (get all players)

Let’s discuss @Produces, @Consumes and MediaType

@Consumes

Define which MIME type is consumed by this method. For this example, exposed methods supports both XML & JSON formats i.e.; methods are annotated with @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

Note: When Content-Type is not specified in the header, then by default it expects request body in “application/x-www-form-urlencoded”. So, Content-Type needs to be set/sent in the header

@Produces

Define which MIME type it will produce. For this example, exposed methods produce response in both XML & JSON formats i.e.; methods are annotated with @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

Note: By default, when invoked it returns the response in XML as it is the first string in the array. So, to get the response in the JSON format then set accept=”application/json” in the header

Most widely used Media Types are

  • MediaType.APPLICATION_XML,
  • MediaType.APPLICATION_JSON,
  • MediaType.TEXT_PLAIN,
  • MediaType.TEXT_XML,
  • MediaType.APPLICATION_FORM_URLENCODED,
  • etc

NOTE: It’s always a good programming practice to do code-to-interface and have its implementation separately

IPlayerService.java

package com.apache.cxf.spring.hibernate.service;

import in.benchresources.cdm.player.PlayerListType;
import in.benchresources.cdm.player.PlayerType;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/playerservice")
public interface IPlayerService {

	// Basic CRUD operations for Player Service

	// http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/addplayer
	@POST
	@Path("addplayer")
	@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	@Produces({MediaType.APPLICATION_FORM_URLENCODED})
	public String createOrSaveNewPLayerInfo(PlayerType playerType);

	// http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/getplayer/10
	@GET
	@Path("getplayer/{id}")
	@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public PlayerType getPlayerInfo(@PathParam("id") int playerId);

	// http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/updateplayer
	@PUT
	@Path("updateplayer")
	@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	@Produces({MediaType.APPLICATION_FORM_URLENCODED})
	public String updatePlayerInfo(PlayerType playerType);

	// http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/deleteplayer
	@DELETE
	@Path("deleteplayer")
	@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON,})
	@Produces({MediaType.APPLICATION_FORM_URLENCODED})
	public String deletePlayerInfo(PlayerType playerType);

	// http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/getallplayer
	@GET
	@Path("getallplayer")
	@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public PlayerListType getAllPlayerInfo();
}

Player Service implementation

Implements above interface. Every method uses hibernate’s rich API to interact with MySql database to insert/update/delete/select (DML) operations

Note: Ideally, there should be one more layer called DAO layer. For simplicity, performed database operations inside service layer

PlayerServiceImpl.java

package com.apache.cxf.spring.hibernate.service;

import java.util.List;

import in.benchresources.cdm.player.PlayerListType;
import in.benchresources.cdm.player.PlayerType;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.apache.cxf.spring.hibernate.model.Player;

@Service("playerService")
public class PlayerServiceImpl implements IPlayerService {

	@Autowired
	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 * returns a String value with SUCCESS message after adding a player
	 */
	@Transactional
	@Override
	public String createOrSaveNewPLayerInfo(PlayerType playerType) {

		// get player information from formal arguments
		Player newPlayer = new Player();
		newPlayer.setName(playerType.getName());
		newPlayer.setAge(playerType.getAge());
		newPlayer.setMatches(playerType.getMatches());

		// inserts into database & return playerId (primary_key)
		int playerId = (Integer) sessionFactory.getCurrentSession().save(newPlayer);
		return "Player information saved successfully with PLAYER_ID " + playerId;
	}

	/**
	 * retrieves a player object based on the playerId supplied in the formal argument using @PathParam
	 */
	@Transactional
	@Override
	public PlayerType getPlayerInfo(int playerId) {

		// retrieve player based on the id supplied in the formal argument
		Player player = (Player) sessionFactory.getCurrentSession().get(Player.class, playerId);

		// set values and return
		PlayerType getplayer = new PlayerType	();
		getplayer.setPlayerId(player.getPlayerId());
		getplayer.setName(player.getName());
		getplayer.setAge(player.getAge());
		getplayer.setMatches(player.getMatches());
		return getplayer;
	}

	/**
	 * returns a String value with SUCCESS message after updating a player
	 */
	@Transactional
	@Override
	public String updatePlayerInfo(PlayerType playerType) {

		// update player info & return SUCCESS message
		Player updatePlayer = new Player();
		updatePlayer.setPlayerId(playerType.getPlayerId());
		updatePlayer.setName(playerType.getName());
		updatePlayer.setAge(playerType.getAge());
		updatePlayer.setMatches(playerType.getMatches());

		// update database with player information and return success msg
		sessionFactory.getCurrentSession().update(updatePlayer);
		return "Player information updated successfully";
	}

	/**
	 * returns a String value with SUCCESS message after deleting a player
	 */
	@Transactional
	@Override
	public String deletePlayerInfo(PlayerType playerType) {

		// delete player info & return SUCCESS message
		Player deletePlayer = new Player();
		deletePlayer.setPlayerId(playerType.getPlayerId());
		deletePlayer.setName(playerType.getName());
		deletePlayer.setAge(playerType.getAge());
		deletePlayer.setMatches(playerType.getMatches());

		// delete player information and return success msg
		sessionFactory.getCurrentSession().delete(deletePlayer);
		return "Player information deleted successfully";
	}

	/**
	 * retrieves all players stored
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	@Override
	public PlayerListType getAllPlayerInfo() {

		// get all player info from database
		List<Player> lstPlayer = sessionFactory.getCurrentSession().createCriteria(Player.class).list();

		// create a object of type PlayerType which takes player objects in its list
		PlayerListType playerListType = new PlayerListType();

		// iterate and set the values and return list
		for(Player player : lstPlayer) {
			// add player info
			PlayerType playerType = new PlayerType();
			playerType.setPlayerId(player.getPlayerId());
			playerType.setName(player.getName());
			playerType.setAge(player.getAge());
			playerType.setMatches(player.getMatches());
			playerListType.getPlayerType().add(playerType); // add to playerListType
		}
		return playerListType;
	}
}

 

Deployment

  • Run maven command to build the war : mvn clean install (use command prompt or integrated maven in eclipse IDE)
  • Copy the war file from the target folder
  • Paste it into apache tomcat (webapps folder)
  • Start the tomcat server

Test the service !!

 

Testing

There are many ways to do testing

  • Copy URL of GET service into web browser
  • Advanced REST client from Google Chrome
  • Rest client from Mozilla Firefox Add On
  • Write your own client for example, Java client using httpcomponents from Apache
  • JDK’s in-built classes like HttpURLConnection (Java client)

 

1. Using RestClient from Mozilla Firefox Add-On

 

Every service tested setting up header parameters “accept” & “Content-Type” in the request. Mixed both XML/JSON while testing below services

1.1

First service: @POST (createOrSaveNewPLayerInfo())
URL: http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/addplayer
Request:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PlayerType xmlns="http://benchresources.in/cdm/Player">
	<playerId>10</playerId>
	<name>Steve Waugh</name>
	<age>49</age>
	<matches>168</matches>
</PlayerType>

Content-Type: application/xml
accept: application/x-www-form-urlencoded
Response: Player information saved successfully with PLAYER_ID 13
3_ApacheCXF-Spring-Hibernate_PlayerService_addPlayer_xml

1.2

Second service: @GET (getPlayerInfo())
URL:http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/getplayer/10
Request: None
Content-Type: application/x-www-form-urlencoded
accept: application/json
Response:

{
  "playerId": 10,
  "name": "Steve Waugh",
  "age": 49,
  "matches": 168
}

4_ApacheCXF-Spring-Hibernate_PlayerService_getPlayer_json

1.3

Third service: @PUT (updatePlayerInfo())
URL:http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/updateplayer
Request:

{
  "playerId": 10,
  "name": "Steve Waugh",
  "age": 49,
  "matches": 168
}

Content-Type: application/json
accept: application/x-www-form-urlencoded
Response: Player information updated successfully
5_ApacheCXF-Spring-Hibernate_PlayerService_updatePlayer_json

1.4

Fourth service: @DELETE (deletePlayerInfo())
URL:http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/deleteplayer
Request:

{
  "playerId": 10,
  "name": "Steve Waugh",
  "age": 49,
  "matches": 168
}

Content-Type: application/json
accept: application/x-www-form-urlencoded
Response: Player information deleted successfully
6_ApacheCXF-Spring-Hibernate_PlayerService_deletePlayer_json

1.5

Fifth service: @GET (getAllPlayerInfo())
URL:http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/getallplayer
Request: None
Content-Type: application/x-www-form-urlencoded
accept: application/json
Response:

{
  "playerType": [
    {
      "playerId": 1,
      "name": "Sachin Tendulkar",
      "age": 41,
      "matches": 200
    },
    {
      "playerId": 2,
      "name": "Shane Warne",
      "age": 44,
      "matches": 145
    },
    {
      "playerId": 3,
      "name": "Kevin Pietersen",
      "age": 35,
      "matches": 104
    },
    {
      "playerId": 4,
      "name": "Shahid Afridi",
      "age": 35,
      "matches": 27
    },
    {
      "playerId": 5,
      "name": "Brian Lara",
      "age": 45,
      "matches": 131
    },
    {
      "playerId": 7,
      "name": "Mahela Jayawardene",
      "age": 37,
      "matches": 145
    },
    {
      "playerId": 10,
      "name": "Steve Waugh",
      "age": 49,
      "matches": 168
    }
  ]
}

7_ApacheCXF-Spring-Hibernate_PlayerService_getAllPlayer_json

 

2. Java Client

Uses HttpURLConnection and its supporting classes which comes shipped with JDK

Tested one service i.e.; /getplayer/{id} using HttpURLConnection. Do test other services using this Java client making necessary changes to the requestParams[] array like requestURL, httpMethod, contentType, accept and include correct requestString

NOTE: Intentionally, commented out few lines of code. Make sure to un-comment while invoking services for POST, PUT requests

TestPlayerService.java

package test.apache.cxf.spring.hibernate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MediaType;

public class TestPlayerService {

	/**
	 * main method to invoke test method
	 * @param args
	 */
	public static void main(String[] args) {

		/*String requestURL = "http://localhost:8080/ApacheCXF-XML-JSON-IO/services/playerservice/addplayer";
		String httpMethod = HttpMethod.POST;
		String contentType = MediaType.APPLICATION_XML;
		String accept = MediaType.APPLICATION_FORM_URLENCODED;
		String requestString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
				+				"<PlayerType xmlns=\"http://benchresources.in/cdm/Player\">"
				+					"<playerId>183</playerId>"
				+					"<name>Courtney Walsh</name>"
				+					"<age>52</age>"
				+					"<matches>132</matches>"
				+ 				"</PlayerType>";

		String[] requestParams = {requestURL, httpMethod, contentType, accept, requestString};*/

		String requestURL = "http://localhost:8080/ApacheCXF-Spring-Hibernate/services/playerservice/getplayer/10";
		String httpMethod = HttpMethod.GET;
		String contentType = MediaType.APPLICATION_FORM_URLENCODED;
		String accept = MediaType.APPLICATION_JSON;
		String requestString =  "{"
				+  		"\"playerId\": 222,"
				+  		"\"name\": \"Ridley Jacobs\","
				+  		"\"age\": 47,"
				+  		"\"matches\": 65 "
				+  	"}";

		String[] requestParams = {requestURL, httpMethod, contentType, accept, requestString};

		String responseFromService = testPlayerService(requestParams);
		System.out.println("Response String: " + responseFromService);
	}

	/**
	 * This method uses HttpURLConnection to invoke exposed Restful web service and returns the response string to the calling client
	 * @param requestParams
	 * @return
	 */
	public static String testPlayerService(String[] requestParams) {

		// local variables
		URL url = null;
		HttpURLConnection httpURLConnection = null;
		OutputStreamWriter outputStreamWriter = null;
		String responseMessageFromServer = null;
		String responseXML = null; 

		try {
			url = new URL(requestParams[0]);
			httpURLConnection = (HttpURLConnection) url.openConnection();
			httpURLConnection.setRequestMethod(requestParams[1]);
			httpURLConnection.setRequestProperty("Content-Type", requestParams[2]);
			httpURLConnection.setRequestProperty("Accept", requestParams[3]);
			httpURLConnection.setDoOutput(true);

			/*outputStreamWriter = new OutputStreamWriter(httpURLConnection.getOutputStream());
			outputStreamWriter.write(requestParams[4]);
			outputStreamWriter.flush();*/

			System.out.println("Response code: " + httpURLConnection.getResponseCode());

			if (httpURLConnection.getResponseCode() == 200) {

				responseMessageFromServer = httpURLConnection.getResponseMessage();
				System.out.println("ResponseMessageFromServer: " + responseMessageFromServer);
				responseXML = getResponseXML(httpURLConnection);
			}
		}
		catch(Exception  ex){
			ex.printStackTrace();
		}
		finally{
			httpURLConnection.disconnect();
		}
		return responseXML;
	}

	/**
	 * This method is used to get response XML from the HTTP GET Request created for Authorization WireKey
	 * @param httpURLConnection
	 * @return stringBuffer.toString()
	 * @throws IOException
	 */
	private static String getResponseXML(HttpURLConnection httpURLConnection) throws IOException{

		StringBuffer stringBuffer = new StringBuffer();
		BufferedReader bufferedReader = null;
		InputStreamReader inputStreamReader = null;
		String readSingleLine = null;

		try{
			// read the response stream AND buffer the result into a StringBuffer
			inputStreamReader = new InputStreamReader(httpURLConnection.getInputStream());
			bufferedReader = new BufferedReader(inputStreamReader);

			// reading the XML response content line BY line
			while ((readSingleLine = bufferedReader.readLine()) != null) {
				stringBuffer.append(readSingleLine);
			}
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
		finally{
			// finally close all operations
			bufferedReader.close();
			httpURLConnection.disconnect();
		}
		return stringBuffer.toString();
	}
}

Output in Console

Response code: 200
ResponseMessageFromServer: OK
Response String: {"playerId":10,"name":"Steve Waugh","age":49,"matches":168}

Study java client and do necessary changes to test other exposed services

Conclusion: Example went through in this article is almost an enterprise application with services exposed using Apache CXF JAX-RS web service and later integrating service/DAO layer with Hibernate (ORM) for database interaction

Download project

ApacheCXF-Spring-Hibernate (11kB)

Happy Coding !!
Happy Learning !!

Apache CXF: JAX-RS Restful web service + Integrating with Spring Security
Apache CXF: JAX-RS Restful web service using both (JSON + XML) example
  • Alessandro Palumbo

    Hi,
    thank you for the guide, but i have a problem.
    I receive this error in deploy:

    Caused by: java.lang.NoSuchMethodError: org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.addAnnotatedClass(Ljava/lang/Class;)Lorg/hibernate/cfg/Configuration;

    searching throw the internet I have find that can be a version error or version incompatibility. I use all the version that you said.

    but.. for what?

    • SJ

      Palumbo,
      You definitely missing some important jars to import into classpath.
      Try to import project from Download section and Deploy & Test it.