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

In this article, we will learn and implement @MatrixParam annotation in JAX-RS Restful web service. Matrix parameters are set of “key=value” pair with semicolon (;) between them

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 @QueryParam &@MatrixParam

@QueryParam: arguments are supplied in the URL with ampersand sign (&) between each “key=value” pair after the question mark (?)

@MatrixParam: arguments supplied to the URL for the invocation are separated using semicolon (;)

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

Look at the above sample URL for @MatrixParam, each pair of “key=value” is separated by semicolon (;)

NOTE: You can skip any “key=value” pair from the request URL while hitting the request and in this case their default values are passed to the method invocation. For example, string – null & int – 0, etc

Annotation Used

  • @MatrixParam (javax.ws.rs.MatrixParam)
  • @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_MatrixParam_Project_Structure_In_Eclipse

Jars Libraries Used in the Project (Maven Dependencies)

2_ApacheCXF_MatrixParam_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-MatrixParam</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.matrixparam.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-MatrixParam. Initial path for this application is http://localhost:8080/ApacheCXF-MatrixParam

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
Defines two simple methods

  • First method takes one-argument using @MatrixParam annotation (single parameter)
    URL: http://localhost:8080/ApacheCXF-MatrixParam/services/player/welcome;name=McGrath
  • Second method takes three-arguments using @MatrixParam annotation (multiple parameters)
    URL: http://localhost:8080/ApacheCXF-MatrixParam/services/player/playerinfo;name=Langer;age=44;matches=105

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

IPlayerService.java

package com.apache.cxf.matrixparam.service;

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

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

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

	// http://localhost:8080/ApacheCXF-MatrixParam/services/player/playerinfo;name=Langer;age=44;matches=105
	@GET
	@Path("/playerinfo")
	public Response getPlayerInfo(
			@MatrixParam("name") String playerName,
			@MatrixParam("age") int age,
			@MatrixParam("matches") int matches);
}

Player Service implementation

Implements above interface returning Response object for both methods

PlayerServiceImpl.java

package com.apache.cxf.matrixparam.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 MatrixParam 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 MatrixParam 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-MatrixParam/services/player/welcome;name=McGrath

3_ApacheCXF_MatrixParam_first_method_browser

Second Method:
Enter URL http://localhost:8080/ApacheCXF-MatrixParam/services/player/playerinfo;name=Langer;age=44;matches=105

4_ApacheCXF_MatrixParam_second_method_browser

 

2. Using Advanced Rest client from Google Chrome

First Method:
Enter URL http://localhost:8080/ApacheCXF-MatrixParam/services/player/welcome;name=McGrath

5_ApacheCXF_MatrixParam_first_method_advanced_rest_client

Second Method:
Enter URL http://localhost:8080/ApacheCXF-MatrixParam/services/player/playerinfo;name=Langer;age=44;matches=105

6_ApacheCXF_MatrixParam_second_method_advanced_rest_client

 

3. Java client

Uses Apache’s HttpGet class to invoke Restful web service

TestPlayerService.java

package com.apache.cxf.matrixparam.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-MatrixParam/services/player/welcome;name=McGrath");
		System.out.println("Response from first service : " + returnValue1);

		// invokes for first method with three argument
		String returnValue2 = testPlayerService("http://localhost:8080/ApacheCXF-MatrixParam/services/player/playerinfo;name=Langer;age=44;matches=105");
		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 McGrath to Lords - the home of cricket
Response from second service : [name=Langer, age=44, matches=105]

Conclusion: The URL of the @MatrixParam annotation is much simpler compared to the @QueryParam as there are no question mark (?) and ampersand (&)and this way readability improves much better

Download project

ApacheCXF-@MatrixParam-annotation (5kB)

Happy Coding !!
Happy Learning !!

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