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

Difference between revisions of "Tutorial: Exposing a Jax REST service as an OSGi Remote Service"

Line 74: Line 74:
 
==Introduction==
 
==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], [https://jersey.java.net/ Jersey], and [http://cxf.apache.org/ 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.
+
Jax RESTful Web Services (Jax-RS) is a popular standard/specification for exposing services for web-based remote access, with a number of implementations (e.g. [http://resteasy.jboss.org/ Resteasy], [https://jersey.java.net/ Jersey], and [http://cxf.apache.org/ CXF] and more appearing all the time.
  
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 [https://github.com/ECF/JaxRSProviders found here].
+
This tutorial will show how to use ECF's implementation of OSGi Remote Services to expose an arbitrary Jax-RS service as an OSGi Service and thereby gain the advantages of using OSGi Services, such as superior handling of service dynamics in an unreliable networking environment, service versioning, and a clear separation of service contract from service implementation.
 +
 
 +
ECF's implementation of OSGi Remote Services is unique among the Remote Service/Remote Service Admin implementations because it provides open APIs for substituting '''distribution providers'''.  Distribution providers are responsible for the 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 a relatively new one for Jax-RS.  The git repo containing this provider and the examples from this tutorial may be [https://github.com/ECF/JaxRSProviders found here].  This provider is based upon the [https://jcp.org/aboutJava/communityprocess/final/jsr339/index.html Jax-RS specification], and uses currently uses the [https://jersey.java.net Jersey implementation] from the Eclipse Foundation [http://www.eclipse.org/orbit Orbit project].  This provider is modular and extensible, allowing the easy replacement with other Jax-RS implementations.
  
 
This tutorial will guide the reader through
 
This tutorial will guide the reader through
  
#Defining a Jax-RS remote service so that it may be used as an OSGi Remote Service
+
#Describing how Jax-RS services are defined
#Demonstrating how to use the OSGi Remote Service for accessing Jax-RS service
+
#Declaring an example Jax-RS remote service so that it may be used as an OSGi Remote Service
 +
#Demonstrating how to use the Remote Service for accessing the underlying Jax-RS service
  
==Define the Service==
+
==Jax-RS Services==
  
In Jax-RS, typically what is done is that a server-side implementation class is annotated with standard java annotations.  For example:
+
With Jax-RS, typically a server-side implementation 'resource' class is annotated with Jax-RS specified java annotations.  For example:
  
 
<source lang="java">
 
<source lang="java">
Line 101: Line 104:
 
</source>
 
</source>
  
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:   
+
The @Path and @Get annotations are used by the Jax-RS implementation on the server to expose the sayHello method for access by a client via using an URL such as:   
  
 
<pre>
 
<pre>
Line 107: Line 110:
 
</pre>
 
</pre>
  
Which would return "Hello There" when accessed.
+
This would return "Hello There" when accessed.
  
For this tutorial we are using a service for accessing a simple database of Students.  [https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.webapp.host/src/com/mycorp/examples/student/webapp/host/StudentResource.java 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.
+
For this tutorial we will use a service for accessing a simple database of Students.  [https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.webapp.host/src/com/mycorp/examples/student/webapp/host/StudentResource.java Here is a Jax-RS implementation class].  Notice the annotations on the public methods.  These annotations signal to the Jax-RS implementation that these methods are to be exposed as remote services.  When included in a webapp (war file) with the necessary Jax-RS implementation libraries and dependencies on (e.g.) a Tomcat server, this service would be exposed for web-based remote access.
  
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
+
In order to make this student database service available for clients/consumers as an OSGi Remote Service, it's necessary to abstract an interface with the appropriate methods and including the same Jax-RS annotations.  Here is such an interface for the StudentResouce
  
 
<source lang="java">
 
<source lang="java">
Line 158: Line 161:
 
</source>
 
</source>
  
Please note that the method signatures and annotations are '''exactly''' the same as those given on the actual [https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.webapp.host/src/com/mycorp/examples/student/webapp/host/StudentResource.java Jax-RS service resource class].  A bundle project with this service interface and dependencies is [https://github.com/ECF/JaxRSProviders/tree/master/examples/com.mycorp.examples.student provided here].
+
Please notethe method signatures and annotations are '''exactly''' the same as those given on the actual [https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.webapp.host/src/com/mycorp/examples/student/webapp/host/StudentResource.java Jax-RS service resource class].  A bundle project with this service interface and dependencies is [https://github.com/ECF/JaxRSProviders/tree/master/examples/com.mycorp.examples.student 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.   
+
Note also that the annotations 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 [https://github.com/ECF/JaxRSProviders/tree/master/bundles ECF Remote Service Jax-RS provider bundles] and their dependencies, we can create an application that uses this remote service.   
+
With this interface and the [https://github.com/ECF/JaxRSProviders/tree/master/bundles ECF Remote Service Jax-RS provider bundles] and their dependencies, we can create a client/consumer application that uses this remote service.  ECF's implementation will dynamically create a proxy implementation of the StudentService interface, and make it available to the consumer.
  
Here is the java code for a Declarative Services component that injects the StudentService when discovered
+
As an OSGi service, it can be discovered and made available in several ways, but the easiest is to have the proxy instance injected via Declarative Services.  Here is the java code for a Declarative Services component that dynamically injects the StudentService
  
 
<source lang="java">
 
<source lang="java">
Line 175: Line 178:
  
 
public class StudentServiceClient {
 
public class StudentServiceClient {
 +
  
 
void bindStudentService(StudentService service) {
 
void bindStudentService(StudentService service) {

Revision as of 23:46, 26 August 2015


Introduction

Jax RESTful Web Services (Jax-RS) is a popular standard/specification for exposing services for web-based remote access, with a number of implementations (e.g. Resteasy, Jersey, and CXF and more appearing all the time.

This tutorial will show how to use ECF's implementation of OSGi Remote Services to expose an arbitrary Jax-RS service as an OSGi Service and thereby gain the advantages of using OSGi Services, such as superior handling of service dynamics in an unreliable networking environment, service versioning, and a clear separation of service contract from service implementation.

ECF's implementation of OSGi Remote Services is unique among the Remote Service/Remote Service Admin implementations because it provides open APIs for substituting distribution providers. Distribution providers are responsible for the 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 a relatively new one for Jax-RS. The git repo containing this provider and the examples from this tutorial may be found here. This provider is based upon the Jax-RS specification, and uses currently uses the Jersey implementation from the Eclipse Foundation Orbit project. This provider is modular and extensible, allowing the easy replacement with other Jax-RS implementations.

This tutorial will guide the reader through

  1. Describing how Jax-RS services are defined
  2. Declaring an example Jax-RS remote service so that it may be used as an OSGi Remote Service
  3. Demonstrating how to use the Remote Service for accessing the underlying Jax-RS service

Jax-RS Services

With Jax-RS, typically a server-side implementation 'resource' class is annotated with Jax-RS specified 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 Jax-RS implementation on the server to expose the sayHello method for access by a client via using an URL such as:

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

This would return "Hello There" when accessed.

For this tutorial we will use a service for accessing a simple database of Students. Here is a Jax-RS implementation class. Notice the annotations on the public methods. These annotations signal to the Jax-RS implementation that these methods are to be exposed as remote services. When included in a webapp (war file) with the necessary Jax-RS implementation libraries and dependencies on (e.g.) a Tomcat server, this service would be exposed for web-based remote access.

In order to make this student database service available for clients/consumers as an OSGi Remote Service, it's necessary to abstract an interface with the appropriate methods and including the same Jax-RS annotations. Here is such an interface for the StudentResouce

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: 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 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 interface and the ECF Remote Service Jax-RS provider bundles and their dependencies, we can create a client/consumer application that uses this remote service. ECF's implementation will dynamically create a proxy implementation of the StudentService interface, and make it available to the consumer.

As an OSGi service, it can be discovered and made available in several ways, but the easiest is to have the proxy instance injected via Declarative Services. Here is the java code for a Declarative Services component that dynamically injects the StudentService

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