REST Service with MultivaluedMap using Jersey

Introduction

Here I am going to give an example on how MultivaluedMap in REST web service works. A MultivaluedMap<K, V> is a map of key-values pairs. Each key can have zero or more values, where K – the type of keys maintained by this map and V – the type of mapped values.

The most important concept in REST is resources, which are identified by global IDs — typically using URIs. Client applications use HTTP methods (GET/ POST/ PUT/ DELETE) to manipulate the resource or collection of resources.

Related Post:

A RESTful Web service is implemented using HTTP and the principles of REST. Typically, a RESTful Web service should define the following aspects:

URI

The base/root URI for the Web service such as http://<host>/<appcontext/contextpath>/<url pattern>/<resources>.

MIME Types

The MIME type of the response data supported, which are JSON/XML/TEXT/HTML etc.

HTTP Methods

HTTP methods are mapped to CRUD (create, read, update and delete) actions for a resource. Although you can make slight modifications such as making the PUT method to create or update, the basic patterns are listed as follows.

HTTP GET: Get/List/Retrieve an individual resource or a collection of resources.
HTTP POST: Create a new resource or resources.
HTTP PUT: Update an existing resource or collection of resources.
HTTP DELETE: Delete a resource or collection of resources.

Prerequisites

Java 8+, Jersey API 2.x – 3.x, Gradle 5.6, Maven 3.6.1 – 3.8.5

Project Setup

Create maven or gradle based project in your favorite IDE. The name of the project is rest-jersey-multivalued-map.

For maven based project you can use the following pom.xml file. This file uses Jersey 3.x version:

<?xml version="1.0" encoding="UTF-8"?>

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>rest-jersey-multivalued-map</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>jakarta.ws.rs</groupId>
			<artifactId>jakarta.ws.rs-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.grizzly</groupId>
			<artifactId>grizzly-http-server</artifactId>
			<version>3.0.1</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jersey.containers</groupId>
			<artifactId>jersey-container-servlet</artifactId>
			<version>3.1.0-M2</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jersey.bundles.repackaged</groupId>
			<artifactId>jersey-guava</artifactId>
			<version>2.26-b03</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jersey.containers</groupId>
			<artifactId>jersey-container-grizzly2-http</artifactId>
			<version>3.1.0-M2</version>
		</dependency>
		<!-- jersey-hk2 is required for jersey 3.1.0-M2 -->
		<dependency>
			<groupId>org.glassfish.jersey.inject</groupId>
			<artifactId>jersey-hk2</artifactId>
			<version>3.1.0-M2</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jersey.test-framework.providers</groupId>
			<artifactId>jersey-test-framework-provider-inmemory</artifactId>
			<version>3.1.0-M2</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.13.2</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<finalName>rest-service-authentication</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.2</version>
			</plugin>
		</plugins>
	</build>
</project>

For gradle based project I will include required dependencies.

Note you only need jaxb-api if you are using Java 9 or above. If you do not include jaxb-api then you will get an exception: java.lang.NoClassDefFoundError: javax/xml/bind/PropertyException

Make sure you have the following dependencies in your build.gradle file:

plugins {
    id 'java-library'
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    mavenCentral()
}

dependencies {
    implementation 'javax.ws.rs:javax.ws.rs-api:2.1.1'
    
    implementation 'javax.servlet:javax.servlet-api:4.0.1'
    
    implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359'
    
    implementation 'org.glassfish.jersey.inject:jersey-hk2:2.29.1'
    implementation 'org.glassfish.grizzly:grizzly-http-server:2.4.4'
    implementation 'org.glassfish.jersey.containers:jersey-container-servlet:2.29.1'
    implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.29.1'
    
    testImplementation 'junit:junit:4.12'
}

For maven based project with Jersey 2.x, you can use the below pom.xml file.

Note you only need jaxb-api if you are using Java 9 or above. If you do not include jaxb-api then you will get an exception: java.lang.NoClassDefFoundError: javax/xml/bind/PropertyException

<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.roytuts</groupId>
	<artifactId>rest-jersey-multivalued-map</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	
	<properties>
		<jersey.version>2.29.1</jersey.version>
	</properties>
	
	<dependencies>
		<dependency>
			<groupId>javax.ws.rs</groupId>
			<artifactId>javax.ws.rs-api</artifactId>
			<version>2.1.1</version>
		</dependency>
		
		<dependency>
			<groupId>org.glassfish.jersey.inject</groupId>
			<artifactId>jersey-hk2</artifactId>
			<version>${jersey.version}</version>
		</dependency>
		
		<dependency>
			<groupId>org.glassfish.grizzly</groupId>
			<artifactId>grizzly-http-server</artifactId>
			<version>2.4.4</version>
		</dependency>
		
		<dependency>
			<groupId>org.glassfish.jersey.containers</groupId>
			<artifactId>jersey-container-servlet</artifactId>
			<version>${jersey.version}</version>
		</dependency>
		
		<dependency>
			<groupId>org.glassfish.jersey.containers</groupId>
			<artifactId>jersey-container-grizzly2-http</artifactId>
			<version>${jersey.version}</version>
		</dependency>
		
		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.4.0-b180830.0359</version>
		</dependency>
		
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>rest</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>12</source>
					<target>12</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Deployment Descriptor

Though I am going to test this application using Junit test case but ideally your application will be deployed into an application server, at least servlet container based server. Therefore you need to have such deployment descriptor – web.xml file.

Modify web.xml file to use the jersey servlet.

<?xml version="1.0" encoding="UTF-8"?>
<!-- <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0"> -->

<web-app version="4.0"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
   http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">

	<display-name>REST MultivaluedMap using Jersey</display-name>

	<servlet>
		<servlet-name>REST</servlet-name>
		<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
		<init-param>
			<param-name>jersey.config.server.provider.packages</param-name>
			<param-value>com.roytuts.rest.jersey.multivalued.map.resources</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map /rest/* to Jersey -->
	<servlet-mapping>
		<servlet-name>REST</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

REST Resource Class

Now I will create REST resource class. This is a simple class that takes MultivaluedMap as a parameter and sends those as a response in string format.

package com.roytuts.rest.jersey.multivalued.map.resources;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MultivaluedMap;

@Path("/resource")
public class RestResource {

	@POST
	@Path("/multivalued")
	public String postMultivalued(MultivaluedMap<String, String> multivaluedMap) {
		
		if (multivaluedMap.get("fname") != null && multivaluedMap.get("lname") != null) {
			String fname = multivaluedMap.get("fname").get(0);
			String lname = multivaluedMap.get("lname").get(0);
			return "Your name : " + fname + " " + lname;
		}
		
		return "I have not received anything from you";
	}

}

Junit Class

As I said that I am not going to deploy the application into external server. Therefore, I will create a Junit class and using Junit class I will deploy the application into Glassfish Grizzly server and test our REST service.

package com.roytuts.rest.jersey.multivalued.map.resources;

import java.net.URI;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class RestResourceTest {

	private HttpServer httpServer;
	private WebTarget webTarget;
	private static final URI baseUri = URI.create("http://localhost:9090/rest/");

	@Before
	public void setup() throws Exception {
		// create ResourceConfig from Resource class
		ResourceConfig rc = new ResourceConfig(RestResource.class);
		// create the Grizzly server instance
		httpServer = GrizzlyHttpServerFactory.createHttpServer(baseUri, rc);
		// start the server
		httpServer.start();
		// configure client with the base URI path
		Client client = ClientBuilder.newClient();
		webTarget = client.target(baseUri);
	}

	@After
	public void tearDown() throws Exception {
		// if you want to stop the server from the input through keyboard then uncomment
		// below lines

		// System.out.println(String
		// .format("Application started.%nHit enter to stop it..."));
		// System.in.read();
		// stop the server
		httpServer.shutdown();
	}

	@Test
	public void testPostMultivalued() {
		MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
		map.add("fname", "Soumitra");
		map.add("lname", "Roy");
		String response = webTarget.path("resource/multivalued").request().post(Entity.form(map), String.class);
		System.out.println("Response: " + response);
	}

}

Testing MultivaluedMap in REST API

Running the above Junit class will give you the below output:

Response: Your name : Soumitra Roy

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *