Working with Matrix URIs (@MatrixParam,PathSegment) in JAX-RS

This tutorial shows how to extract Matrix Parameters from http request in JAX-RS.

Example Scenario :

Our rest service is like a car inventory  which has cars of different models and years. For the sake of simplicity, let’s say it has only Honda cars. We want to get one or more cars from the inventory based on different matrix parameter values.


Project artifacts:

1. Technologies used –

a. Eclipse IDE for Java EE developer 4.5.0.20150621-1200 (Mars)
b. Preinstalled Maven with Eclipse Mars
c. Tomcat 7.0.64
d. RESTEasy 3.0.9.Final
e. Java 1.8

2. Eclipse project folder structure:

JAXRSRESTMatrixParameters

3. Web Project

Create a standard web project in Eclipse by following the below link.

Create a Maven Web Project

4. Jars used

In case you are not using maven, here are external jars needed for deploying this application in Tomcat.

activation-1.1.jar
commons-codec-1.6.jar
commons-io-2.1.jar
commons-logging-1.1.1.jar
httpclient-4.2.6.jar
httpcore-4.2.5.jar
javax.ws.rs-api-2.0.jar
jaxrs-api-3.0.9.Final.jar
jboss-annotations-api_1.1_spec-1.0.1.Final.jar
jcip-annotations-1.0.jar
resteasy-jaxrs-3.0.9.Final.jar

5. Maven Dependencies

Paste the below content to pom.xml in the newly created project. It has dependencies for JAX-RS and RESTEasy jars.

<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.javanbeyond</groupId>
	<artifactId>WorkingWithPathSegMatrixParam</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>Restful Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>javax.ws.rs</groupId>
			<artifactId>javax.ws.rs-api</artifactId>
			<version>2.0</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-jaxrs</artifactId>
			<version>3.0.9.Final</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>WorkingWithPathSegMatrixParam</finalName>
	</build>
</project>

6. POJO

file: Car.java. A POJO which the data is converted into and stored in inventory.

package com.javanbeyond.data;

public class Car {
  private int id;
  private String make;
  private String model;
  private String exteriorColor;
  private String interiorColor;
  private int year;

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getMake() {
    return make;
  }

  public void setMake(String make) {
    this.make = make;
  }

  public String getModel() {
    return model;
  }

  public void setModel(String model) {
    this.model = model;
  }

  public String getExteriorColor() {
    return exteriorColor;
  }

  public void setExteriorColor(String exteriorColor) {
    this.exteriorColor = exteriorColor;
  }

  public String getInteriorColor() {
    return interiorColor;
  }

  public void setInteriorColor(String interiorColor) {
    this.interiorColor = interiorColor;
  }

  public int getYear() {
    return year;
  }

  public void setYear(int year) {
    this.year = year;
  }

}

7. Application class

The class that registers the resource class for JAX-RS runtime to forward requests to methods of this class.

file: MyApplication.java.

package com.javanbeyond.service;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApplication extends Application {
  private Set<Object> singletons = new HashSet<Object>();
  private Set<Class<?>> empty = new HashSet<Class<?>>();

  // Registering the resource class object
  public MyApplication() {
    singletons.add(new OrderReceiver());
  }

  @Override
  public Set<Class<?>> getClasses() {
    return empty;
  }

  @Override
  public Set<Object> getSingletons() {
    return singletons;
  }
}

8. Resource class

file: OrderReceiver.java . In JAX-RS, we use @MatrixParam and PathSegment to capture information provided in matrix parameters of a URI and pass it to methods as parameters. The “/cars/{make}” @Path value  before the class brings the URI “http://localhost:8080/rest/cars/honda” to this class. The part after this URI decides which method will be called at runtime.

Case 1: Getting Matrix Parameters with PathSegment

The method getCarByPathSeg receives the URI  “/cars/honda/civic;color=gray/1991″ and “honda” is mapped to {make} from class level @Path annotation .{year} gets the value “1991” and “civic;color=gray” is stored in {model}. {model} is passed as PathSegment  to method parameter which encapsulates matrix parameter as well. We want to get a car with interior color as gray.

// Request match : /cars/honda/civic;color=gray/1991
  // To test it, please comment getCarByMatrix method
  // This method can be used as an alternative to getCarByMatrix
  @GET
  @Path("{model}/{year}")
  @Produces("application/xml")
  public StreamingOutput getCarByPathSeg(@PathParam("make"String make,
      @PathParam("model"PathSegment carSeg,
      @PathParam("year"int year) {
    System.out.println("in getCarByPathSeg");
    String color = carSeg.getMatrixParameters().getFirst("color");
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      final Car car = carEntry.getValue();
      if (car.getInteriorColor().equalsIgnoreCase(color)) {
        return new StreamingOutput() {
          public void write(OutputStream outputStream)
              throws IOException, WebApplicationException {
            outputRequestedItem(outputStream, car);
          }
        };
      }
    }
    return null;

  }

Case 2: Getting Matrix Parameters with @MatrixParam

The method getCarByMatrix receives the URI  “/cars/honda/civic;color=gray/1991″. The matrix parameter “color” is captured by @MatrixParam annotation and the same is injected to the color parameter of the method. This case is similar to the case above. Only this time we are capturing the value of color with MatrixParam. We want to get a car with interior color as gray.

// Request match : /cars/honda/civic;color=gray/1991
  // This is same as above. But we don't have to extract the color ourselves.
  // MatrixParam does it for us
  // This method can be used as an alternative to getCarByMatrix
  @GET
  @Path("{model}/{year}")
  @Produces("application/xml")
  public StreamingOutput getCarByMatrix(@PathParam("make"String make,
      @PathParam("model"String model,
      @MatrixParam("color"String color, @PathParam("year"int year) {
    System.out.println("in getCarByMatrix");
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      final Car car = carEntry.getValue();
      if (car.getInteriorColor().equalsIgnoreCase(color)) {
        return new StreamingOutput() {
          public void write(OutputStream outputStream)
              throws IOException, WebApplicationException {
            outputRequestedItem(outputStream, car);
          }
        };
      }
    }
    return null;

  }

Case 3: Capturing multiple Matrix Parameters

The method getCar receives the URI  “/cars/honda/crv;color=white/accord;color=black/1991″. This is the case of multiple matrix parameters. The parameter color is repeated two times and thus needs to be captured by a list of PathSegment. The getCar “carSegs” parameter does it beautifully. Here we are looking for cars with colors white or black.

// Request match : /cars/honda/crv;color=white/accord;color=black/1991
  // This method uses regex to match multiple matrix parameters
  @GET
  @Path("{model:.+/.+}/{year}")
  @Produces("application/xml")
  public StreamingOutput getCar(@PathParam("make"String make,
      @PathParam("model"List<PathSegment> carSegs,
      @PathParam("year"int year) {
    System.out.println("in getCar");
    String interiorColor1 = carSegs.get(0).getMatrixParameters()
        .getFirst("color");
    String interiorColor2 = carSegs.get(1).getMatrixParameters()
        .getFirst("color");
    final Map<Integer, Car> subInventory = new HashMap<Integer, Car>();
    for (Entry<Integer, Car> carEntry : carInventory.entrySet()) {
      Car car = carEntry.getValue();
      if (car.getInteriorColor().equalsIgnoreCase(interiorColor1|| car
          .getInteriorColor().equalsIgnoreCase(interiorColor2)) {
        subInventory.put(carEntry.getKey(), car);

      }
    }
    if (subInventory.isEmpty()) {
      return new StreamingOutput() {
        public void write(OutputStream outputStream)
            throws IOException, WebApplicationException {
          PrintStream writer = new PrintStream(outputStream);
          writer.println(
              "<item>" + Response.Status.NOT_FOUND + "</item>");
        }
      };
    else
      return new StreamingOutput() {
        public void write(OutputStream outputStream)
            throws IOException, WebApplicationException {
          outputRequestedItem(outputStream, subInventory);
        }
      };

  }

9. Deployment descriptor

file: web.xml . Application class is registered by setting as init param to the servlet. The URI must contain “/rest” for this REST servlet to be invoked.

<web-app>
	<display-name>Archetype Created Web Application</display-name>
	<context-param>
		<param-name>resteasy.servlet.mapping.prefix</param-name>
		<param-value>/rest</param-value>
	</context-param>
	<servlet>
		<servlet-name>rest</servlet-name>
		<servlet-class>
			org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
		</servlet-class>
		<init-param>
			<param-name>javax.ws.rs.Application</param-name>
			<param-value>com.javanbeyond.service.MyApplication
			</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>rest</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
</web-app>

 

Example Execution:

1. Start the application.

2. Open your favorite browser and type the url “http://localhost:8080/WorkingWithQueryParam/rest/cars/honda/civic;color=gray/1991” and press Enter. You will get an XML response of a car of Civic model and interior color as gray.

JAXRSPathSegmentExample

3. Comment the method getCarByPathSeg and redeploy the application. Type the previous URI “http://localhost:8080/WorkingWithQueryParam/rest/cars/honda/civic;color=gray/1991” in your browser address bar and press Enter. The response should be similar to the one before. But this time we used MatrixParam annotation to extract color parameter.

MatrixParamExample

4. Now type “http://localhost:8080/WorkingWithPathSegMatrixParam/rest/cars/honda/crv;color=white/accord;color=black/1991” and press Enter. You get the cars with interior color as white or black.

JAXRSMultipleMatrixParameters

 

Leave a Reply

Back to Top
%d bloggers like this: