Apache CXF: JAX-RS Restful web service using @QueryParam annotation

In this article, we will learn and implement @QueryParam annotation in JAX-RS Restful web service. Using QueryParam, you can map/bind URL query parameter into Java method

Apache CXF reference implementation comes handy to develop JAX-RS based Restful web service as this has good integration with Spring framework. So developer can define beans using spring annotation like @Service and later this can be invoked for the incoming http requests based on the CXFServlet configured

Difference between @PathParam & @QueryParam

In QueryParam you can map the URL query parameter and not their values whereas PathParam directly maps the actual values in the formal arguments of the method invocation

Sample URL for @QueryParam
http://<server>:<port>/<context-root>/<application-path>/<cxf-path>/<class-level-path>/<method-level-path>?key1=value1&key2=value2&key3=value3

After question mark (?), there are number of key-value pairs with ampersand sign (&) between them. Now, in @QueryParam these keys are mapped to the method invocation

Annotation Used

  • @QueryParam (javax.ws.rs.QueryParam)
  • @Path (javax.ws.rs.Path)
  • @GET (javax.ws.rs.GET)
  • @Service (org.springframework.stereotype.Service)

Technology Used

  • Java 1.7
  • Eclipse Luna IDE
  • Spring-4.0.0-RELEASE
  • Apache-CXF-3.0.0
  • Apache Maven 3.2.1
  • Apache Tomcat 7.0.54

Mavenize or download required jars

Add Apache-CXF-3.0.0 and Spring-4.0.0-RELEASE 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>

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

		<!-- 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>
	</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.

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
  • 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_QueryParam_Project_Structure_In_Eclipse

Jars Libraries Used in the Project (Maven Dependencies)

2_ApacheCXF_QueryParam_Jars_In_Classpath

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

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
  • 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” file from where it has to be loaded. We will discuss briefly about this file
  • <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-QueryParam</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</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,

  • <context:annotation-config /> to activate annotation on the registered beans with the application context
  • <context:component-scan base-package=”” /> this tag scans all classes & sub-classes under the value of base-package attribute and register them with the Spring container
  • <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)
  • 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"
	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">

	<!-- to turn on annotation wiring == turns on only the registered beans
		through ApplicationContext -->
	<context:annotation-config />

	<!-- scans and register beans using annotation-config (metadata) -->
	<context:component-scan base-package="com.apache.cxf.queryparam.service" />

	<!-- register restful web service endpoints -->
	<jaxrs:server id="restContainer" address="/">
		<jaxrs:serviceBeans>
			<ref bean="playerService" />
		</jaxrs:serviceBeans>
	</jaxrs:server>
</beans>

Let’s see coding in action

 

URL Pattern

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

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

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)

Player Service interface

Using QueryParam, URL query parameter are mapped into Java method
Defines two simple methods

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

IPlayerService.java

package com.apache.cxf.queryparam.service;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

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

	// http://localhost:8080/ApacheCXF-QueryParam/services/player/welcome?name=Gilchrist
	@GET
	@Path("/welcome")
	public Response welcomePlayer(@QueryParam("name") String playerName);

	// http://localhost:8080/ApacheCXF-QueryParam/services/player/playerinfo?name=Pietersen&age=33&matches=104
	@GET
	@Path("/playerinfo")
	public Response getPlayerInfo(
			@QueryParam("name") String playerName,
			@QueryParam("age") int age,
			@QueryParam("matches") int matches);
}

Player Service implementation

Implements above interface returning Response object for both methods

PlayerServiceImpl.java

package com.apache.cxf.queryparam.service;

import javax.ws.rs.core.Response;

import org.springframework.stereotype.Service;

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

	/**
	 * this method takes one argument from QueryParam and returns a Response
	 */
	public Response welcomePlayer(String playerName) {

		String greetMessage = "Welcome " + playerName + " to Lords - the home of cricket";
		return Response.status(200).entity(greetMessage).build();
	}

	/**
	 * this method takes three argument from QueryParam and returns a Response
	 */
	public Response getPlayerInfo(String playerName, int age, int matches) {

		String playerInfo = "[name=" + playerName +  ", age=" + age + ", matches=" + matches + "]";
		return Response.status(200).entity(playerInfo).build();
	}
}

 

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 the URL of GET service into web browser
  • Advanced REST client from Google Chrome
  • Rest client in Mozilla Firefox Add On
  • Write your own client for example, Java client using httpcomponents from Apache
  • JDK’s in-built HttpUrlConnection & its supporting classes

 

1. Hit the web browser with GET request URL

First Method:
Enter URL http://localhost:8080/ApacheCXF-QueryParam/services/player/welcome?name=Gilchrist

3_ApacheCXF_QueryParam_first_method_browser

Second Method:
Enter URL http://localhost:8080/ApacheCXF-QueryParam/services/player/playerinfo?name=Pietersen&age=33&matches=104
4_ApacheCXF_QueryParam_second_method_browser

 

2. Using Advanced Rest client from Google Chrome

First Method:
Enter URL http://localhost:8080/ApacheCXF-QueryParam/services/player/welcome?name=Gilchrist

5_ApacheCXF_QueryParam_first_method_advanced_rest_client

Second Method:
Enter URL http://localhost:8080/ApacheCXF-QueryParam/services/player/playerinfo?name=Pietersen&age=33&matches=104

6_ApacheCXF_QueryParam_second_method_advanced_rest_client

 

3. Java client

Uses Apache’s HttpGet to invoke Restful web service

TestPlayerService.java

package com.apache.cxf.queryparam.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class TestPlayerService {

	public static void main(String[] args) {

		// invokes for first method with one argument
		String returnValue1 = testPlayerService("http://localhost:8080/ApacheCXF-QueryParam/services/player/welcome?name=Gilchrist");
		System.out.println("Response from first service : " + returnValue1);

		// invokes for first method with three argument
		String returnValue2 = testPlayerService("http://localhost:8080/ApacheCXF-QueryParam/services/player/playerinfo?name=Pietersen&age=33&matches=104");
		System.out.println("Response from second service : " + returnValue2);
	}

	@SuppressWarnings({ "deprecation", "resource" })
	public static String testPlayerService(String uri) {

		// local variables
		HttpGet httpGet = null;
		DefaultHttpClient defaultHttpClient = null;
		HttpResponse httpResponse = null;
		HttpEntity httpEntity = null;
		InputStream inputStream = null;
		BufferedReader bufferedReader = null;
		StringBuilder stringBuilder = null;

		try {
			// http post request headers
			httpGet = new HttpGet(uri);

			// actual execution of http post
			defaultHttpClient = new DefaultHttpClient();
			httpResponse = defaultHttpClient.execute(httpGet);
			httpEntity = httpResponse.getEntity();

			// get the response content
			inputStream = httpEntity.getContent();
			bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
			stringBuilder = new StringBuilder();
			String strReadLine = bufferedReader.readLine();

			// iterate to get the data and append in StringBuilder
			while (strReadLine != null) {
				stringBuilder.append(strReadLine);
				strReadLine = bufferedReader.readLine();
				if (strReadLine != null) {
					stringBuilder.append("\n");
				}
			}
		}
		catch (UnsupportedEncodingException usee) {
			usee.printStackTrace();
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
		finally {
			// shuts down, when work done
			defaultHttpClient.getConnectionManager().shutdown();
		}
		return stringBuilder.toString();
	}
}

Output in console

Response from first service : Welcome Gilchrist to Lords - the home of cricket
Response from second service : [name=Pietersen, age=33, matches=104]

Conclusion: With key=value pair combination, it increases the readability and maintenance of @GET requests

Download project

ApacheCXF-@QueryParam-annotation (5kB)

Happy Coding !!
Happy Learning !!

Apache CXF: JAX-RS Restful web service using @MatrixParam annotation
Apache CXF: JAX-RS Restful web service using @PathParam annotation