Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Tutorial: Exposing a Jax REST service as an OSGi Remote Service


Introduction

Jax REST Services (Jax-RS) is a popular standard for exposing services for remote access, with a number of implementations (e.g. [ http://resteasy.jboss.org/ Resteasy], Jersey, and CXF among others. This tutorial will show how to use ECF Remote Services to expose an arbitrary Jax-RS service as an OSGi Remote Service and thereby gain the advantages of using OSGi Remote Services, such as superior handling of services dynamics, service versioning, and a clear separation of the service contract from the implementation.

ECF's implementation of OSGi Remote Services is unique because it provides open APIs for creating new distribution providers. Distribution providers are responsible for the actual transmission of remote service calls: marshalling the method arguments, unmarshalling return values, and transporting the call to and from the service using some network protocol. ECF has a number of provider implementations, including one for Jax-RS clients. The git repo containing this provider and the examples from this tutorial may be found here.

This tutorial will guide the reader through

  1. Defining a Jax-RS remote service so that it may be used as an OSGi Remote Service
  2. Demonstrating how to use the OSGi Remote Service for accessing Jax-RS service

Define the Service

In Jax-RS, typically what is done is that a server-side implementation class is annotated with standard java annotations. For example:

import javax.ws.rs.*;
 
@Path("/helloservice")
public class HelloResource {
 
    @GET
    @Path("sayhello")
    public String sayHello() {
        return "Hello There";
    }
}

The @Path and @Get annotations are used by the server Jax-RS implementation to expose the sayHello method for access by a client via an URL such as:

curl http://localhost:8080/mycontext/helloservice/sayhello

Which would return "Hello There" when accessed.

For this tutorial we are using a service for accessing a simple database of Students. Here is the implementation class. Notice the annotations on the public methods. These annotations signal to the Jax-RS server implementation that these methods are to be exposed as remote services.

To make this service available as an OSGi Remote Service, it's necessary to abstract an interface with the appropriate public methods and including the same Jax-RS annotations

package com.mycorp.examples.student;
 
import java.util.List;
 
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
 
@Path("/studentservice")
public interface StudentService {
 
	@GET
	@Produces(MediaType.APPLICATION_XML)
	@Path("/students")
	List<Student> getStudents();
 
	@GET
	@Produces(MediaType.APPLICATION_XML)
	@Path("/students/{studentId}")
	Student getStudent(@PathParam("studentId") String id);
 
	@POST
	@Produces(MediaType.APPLICATION_XML)
	@Path("/students/add/{studentName}")
	Student addStudent(@PathParam("studentName") String studentName);
 
	@POST
	@Consumes(MediaType.APPLICATION_XML)
	@Produces(MediaType.APPLICATION_XML)
	@Path("/students/update")
	Student updateStudent(Student student);
 
	@DELETE
	@Path("/students/delete/{studentId}")
	@Produces(MediaType.APPLICATION_XML)
	Student deleteStudent(@PathParam("studentId") String studentId);
}

Please note that the method signatures and annotations are exactly the same as those given on the actual Jax-RS service resource class. A bundle project with this service interface and dependencies is provided here.

Note also that the annotations used are only those standardized by the Jax-RS specification (i.e. in javax.ws.rs.* packages), and so are not bound to any Jax-RS implementation.

With this annotated interface and the ECF Remote Service Jax-RS provider bundles and their dependencies, we can create an application that uses this remote service.

Here is the java code for a Declarative Services component that injects the StudentService when discovered

package com.mycorp.examples.student.client;
 
import java.util.List;
 
import com.mycorp.examples.student.Student;
import com.mycorp.examples.student.StudentService;
 
public class StudentServiceClient {
 
	void bindStudentService(StudentService service) {
		System.out.println("Discovered student service=" + service);
		// Get students
		List<Student> originalStudents = service.getStudents();
		// Print list
		System.out.println("students=" + originalStudents);
		// Get first student
		Student s = originalStudents.get(0);
		System.out.println("Student 0=" + s);
		if (s != null) {
			// Get this student via id
			s = service.getStudent(s.getId());
			System.out.println("Student with id=" + s.getId() + "=" + s);
		}
		// Add a new student
		Student newStudent = service.addStudent("April Snow");
		System.out.println("Created student=" + newStudent);
		// Update with grade
		newStudent.setGrade("First");
		newStudent = service.updateStudent(newStudent);
		System.out.println("Updated student=" + newStudent);
		// Delete student
		System.out.println("Deleted student=" + service.deleteStudent(newStudent.getId()));
	}
}

This example client code simply invokes all the StudentService proxy methods, which will result in invocation of the appropriate Jax-RS service.

To trigger the dynamic discovery of the StudentService is the starting of a bundle containing EDEF (Endpoint Description Extension Format), an OSGi-standardized xml file format for describing an OSGi Remote Service. See Static File-based Discovery of Remote Service Endpoints for background on EDEF. For this tutorial here is the EDEF for this service, and here is the project containing this EDEF.

Running the Jax-RS Server

For testing, this project was used to create a Tomcat 7-based server (no OSGi) and expose the StudentResource service as a Jax-RS service using JBoss' Resteasy implementation of Jax-RS. Notice that the server is not running OSGi.

Running the Jax-RS OSGi Service Client

Using the StudentServiceClient class as above, and triggering the RSA discovery via starting the bundle containing the EDEF file given above, results in the creation of a StudentService proxy, and it's injection via DS into the StudentServiceClient.bindStudentService method, resulting in the following OSGi console output:

osgi> Discovered student service=com.mycorp.examples.student.StudentService.proxy@org.eclipse.ecf.remoteservice.RemoteServiceID[containerID=URIID [uri=http://localhost:8080/studentresource/rs];containerRelativeID=0]
students=[Student [id=94381a53-b2b9-4cb4-ab36-1e8db4643f2f, name=Joe Senior, grade=First, address=Address [street=111 Park Ave, city=New York, state=NY, postalCode=11111]]]
Student 0=Student [id=94381a53-b2b9-4cb4-ab36-1e8db4643f2f, name=Joe Senior, grade=First, address=Address [street=111 Park Ave, city=New York, state=NY, postalCode=11111]]
Student with id=94381a53-b2b9-4cb4-ab36-1e8db4643f2f=Student [id=94381a53-b2b9-4cb4-ab36-1e8db4643f2f, name=Joe Senior, grade=First, address=Address [street=111 Park Ave, city=New York, state=NY, postalCode=11111]]
Created student=Student [id=391d2e3e-de8a-4dea-a9d6-26a408737eee, name=April Snow, grade=null, address=null]
Updated student=Student [id=391d2e3e-de8a-4dea-a9d6-26a408737eee, name=April Snow, grade=First, address=null]
Deleted student=Student [id=391d2e3e-de8a-4dea-a9d6-26a408737eee, name=April Snow, grade=First, address=null]

One major advantage of this approach is that

Background and Related Articles

Getting Started with ECF's OSGi Remote Services Implementation

OSGi Remote Services and ECF

Asynchronous Proxies for Remote Services

Static File-based Discovery of Remote Service Endpoints

Download ECF Remote Services/RSA Implementation

How to Add Remote Services/RSA to Your Target Platform

Back to the top