JAX-RS 2.0 and OData

Post on 27-Jan-2015

196 views 1 download

Tags:

description

The presentation provides overview of JAX-RS 2.0 and the cool new things that come with it. It also provides an introduction to OData which is a protocol proposed by Microsoft for data interchange.

Transcript of JAX-RS 2.0 and OData

JAX-RS 2.0 AND ODATAAnil Allewar ( anil.allewar@synerzip.com )

About Me

Graduated (finally) as BE in Computers - 2002

Worked with Infosys, Opus Software Solutions and Synerzip

Core expertise in Java Enterprise and Middle-Ware stack

EAI, EII, Rule Engines, ESB, Spring, ORM, Data Virtualization

Currently working as Solutions Architect with Synerzip

Agenda

REST methodology introduction JAX-RS 2.0

History Use cases Features

Odata History Use cases Features

SOA

Service Oriented Architecture is an architecture paradigm where the software components are provided as services accessed through a network accessible endpoint

There is no object broker required as there is no remote object reference held by the clientRegistry

ContractProtocolDefines Format & Operations

Registers

What is REST?

REST stands for REpresentational State Transfer. The largest example of system conforming to REST

architecture is the World Wide Web. In the REST architectural style, data and functionality

are considered resources and are accessed using Uniform Resource Identifiers (URIs), typically links on the Web.

This architecture style is called REST since the state of the client changes with each URI (link in layman’s term) accessed. The URI provides the representation that is returned by the resource.

Resources are manipulated using a fixed set of four CRUD (create, read, update, delete) operations: PUT, GET, POST and DELETE

Current version of JAX-RS in production is JAX-RS 1.1 and JAX-RS 2.0 is currently being drafted.

JAX-RS

JAX-RS is Java API for RESTful Web Services

REST principals Assign URL to everything Link things together for state

transition(called HyperMedia as the engine of application state)

Use common methods Multiple representations of state Stateless communication

Introduced in JEE 5

Pros/Cons: JAX-WS Vs JAX-RS

JAX-WSJAX-WS JAX-RSJAX-RS

• Formal contract that describes the

web service (in the form of WSDL) is

available• Technologies defined by the

WorldWide Web Consortium (W3C):

HTTP (forTransport protocol), SOAP and

WSDL• Implemented either using an

EJB 3endpoint or using servlet

endpoint.• Can provide transactions,

security,Reliability and asynchronous

messages

• Formal contract that describes the

web service (in the form of WSDL) is

available• Technologies defined by the

WorldWide Web Consortium (W3C):

HTTP (forTransport protocol), SOAP and

WSDL• Implemented either using an

EJB 3endpoint or using servlet

endpoint.• Can provide transactions,

security,Reliability and asynchronous

messages

• Does not require creation of

client/server proxies• Requires only HTTP forapplication protocol - data interchange using XML, HTML,JSON,URL encoded forms etc• Implemented as a servletendpoint only • Possible to discover

resourceswithout centralized repository

• Does not require creation of

client/server proxies• Requires only HTTP forapplication protocol - data interchange using XML, HTML,JSON,URL encoded forms etc• Implemented as a servletendpoint only • Possible to discover

resourceswithout centralized repository• Requires client/server

proxies to make the SOAP based web

service call

• Requires client/server proxies to

make the SOAP based web service call

• Service provider and client need

to have understanding of thecontext and content provided

byservice (Not now as WSDL 2.0 /WADL / XML schema can beused to describe REST web

service) butthey don’t have standard

client support

• Service provider and client need

to have understanding of thecontext and content provided

byservice (Not now as WSDL 2.0 /WADL / XML schema can beused to describe REST web

service) butthey don’t have standard

client support

JAX-RS annotations

Annotation Description

@Path The @Path annotation defines the relative path where the web service is hosted. You could also embed variables in path like “/employee/{id}. A @Path value isn’t required to have leading or trailing slashes (/).The path can be applied to a root resource or to a sub-resource.

@GET, @POST@DELETE, @PUT

These annotations are request method designators and correspond to HTTP request method that will be serviced by this class method

@PathParam The @PathParam annotation is a type of parameter that you can extract from the URI path and use in the class/method

@QueryParam The @QueryParam annotation is a type of parameter that you can extract from the request URI query parameters and use in the class/method

@Consumes The @Consumes annotation is used to specify the MIME types of representations sent by the client that a resource can consume.

@Produces The @Produces annotation is used to specify the MIME types of representations sent by the resource to the client e.g. “application/xml”

Basic REST Web Service@Path("rest")public class RootResource {

/* * The @Context makes the HTTP context related objects available to the * resource class */@Contextprivate transient SecurityContext secContext;

@Contextprivate transient HttpServletRequest request;

@Injectprivate Provider<BaseProducer> producer;

@Path("employee")public EmployeeResource getEmployeeResource() {

this.request.setAttribute("test", "Memory");EmployeeResource returnResource = new EmployeeResource();returnResource.setContext(this.secContext);producer.get();return returnResource;

}@GET@Produces({ "application/xml; qs=0.9", MediaType.APPLICATION_JSON })public String sayXMLHello() {

return "<?xml version=\"1.0\"?>" + "<hello> Hello Jersey" + "</hello>";}

}

Resources

Dependency

Injection

HTTP Method Binding

Web Deployment Descriptor

<web-app id="WebApp_ID" version="2.4"xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-

app_2_4.xsd"><display-name>JAXRS_REST_WebServices</display-name>

<servlet><servlet-name>Jersey REST Service</servlet-name><servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class><init-param>

<param-name>javax.ws.rs.Application</param-name><param-value>com.anil.jaxb.resource.config.ApplicationConfigSetter</

param-value></init-param><load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping><servlet-name>Jersey REST Service</servlet-name><url-pattern>/</url-pattern>

</servlet-mapping>

</web-app>

There are various ways in which we can have Jersey discover the resources to be deloyed.

For simple deployments, no web.xml is needed at all. Instead, an @ApplicationPath annotation can be used to annotate the user defined application class and specify the the base resource URI of all application resources.

Basic Jersey Client

/** * This method is used to test get/delete request for an employee to the * REST API API * * @throws IOException */@Testpublic void testDeleteEmployeeRequest() throws IOException {

WebTarget webResource = this.client.target(getBaseURI()).path("rest/employee/3");

// Register the authentication to be used for logging in the web// applicationwebResource.configuration().register(

new HttpBasicAuthFilter("tomcat", "tomcat"));

String getResponse = webResource.request(MediaType.APPLICATION_XML).get(String.class);

String deleteResponse = webResource.request(MediaType.APPLICATION_XML).delete(String.class);

System.out.println("The response received from get is: " + getResponse);System.out.println("The response received from delete is: "

+ deleteResponse);}

Web Service Security

Since REST web services are essentially HTTP resources, they can be protected using the Java EE web authentication and authorization mechanism using elements in web.xml file.

<!-- The web resources with root /rest are protected --><security-constraint>

<web-resource-collection><web-resource-name>REST</web-resource-name><url-pattern>/rest/employee/*</url-pattern>

</web-resource-collection><auth-constraint>

<!-- Use the tomcat-users.xml for authentication --><role-name>tomcat</role-name>

</auth-constraint></security-constraint>

<!-- Define the Login Configuration for this Application --><login-config>

<auth-method>BASIC</auth-method><realm-name>UserDatabaseRealm</realm-name>

</login-config>

<!-- Security roles referenced by this web application --><security-role>

<description>The unlimited role allowed to login to the application.</description>

<role-name>tomcat</role-name></security-role>

Sub-resource Locators

We can delegate the handling of request to sub resources by using methods annotated with @Path BUT not annotated with resource method designators such as @GET or @POST.

Note that the runtime will not manage the life-cycle or perform any field injection onto instances returned from sub-resource locator methods.

However, sub-resources support method injection.@Path("rest")public class RootResource {

.............@Path("employee")public EmployeeResource getEmployeeResource() {

this.request.setAttribute("test", "Memory");EmployeeResource returnResource = new EmployeeResource();returnResource.setContext(this.secContext);return returnResource;

}}

Exceptions, URIBuilder, JAXB Based JSON support

JAX-RS provides exceptions to be thrown which match to HTTP error codes; examples include javax.ws.rs.NotFoundException, javax.ws.rs.ServerErrorException

The JAX-RS runtime provides URIBuilder class which takes care of specifics like encoding etc which you would need to otherwise do manually for java.net.URI class.

JAXB/JSON support is provided by using custom Message readers and writers.

@Produces(MediaType.APPLICATION_XML)

public Response insertEmployee(final Employee employee,

@Context UriInfo uriInfo) {...........}

Custom Serialization for JAXB

Injecting Types

We can define fields/method params etc to be injected with object of the described type by the JAX-RS runtime.

Injection can be performed on fields, constructor parameters, resource/sub-resource/sub-resource locator method parameters and bean setter methods.

For JAX-RS 1.1, @Context annotation was used to indicate injectables to JAX-RS runtime.

JAX-RS 2.0 has integrated with JSR-330 (CDI) and Jersey supports CDI using HK2. You can use @Context or @Inject though @Context is still preferred.

The proposal for this area is not finalized yet in JAX-RS 2.0 specifications

JAX-RS 2.0 features

Client API Filters & handlers

Asynchronous

Processing

Validation (JSR-349)

Hypermedia

CDI (JSR-330)

Server Side

Connection

Negotiation

Client API

The JAX-RS 1.1 specifications was focussed on server side and there was no client API.

Need for a standard to also share server side API(readers/writer) and encapsulate boiler plate codeClient

FactoryClient

Target

Request Builder

Invocation

Response

Configuration

Client API

Create a new client (This is heavy!!)this.client = ClientFactory.newClient();

Use builder pattern to invoke server resources – supports adding query params, target entities etc.

Close the client after use.

WebTarget webResource = this.client.target(getBaseURI()).path("rest/employee");// Register the authentication to be used for logging in the web// applicationwebResource.configuration().register(new HttpBasicAuthFilter("tomcat", "tomcat"));

Response response = webResource.request(MediaType.APPLICATION_XML).put(Entity.entity(employeeToAdd, MediaType.APPLICATION_XML),Response.class);

Client API

Support for generic invocation using command pattern for batch processing

Invocation invGoodRequest = this.client.target(getBaseURI()).path("rest/employee").request(MediaType.APPLICATION_XML).buildPut(Entity.entity(inputXMLBuilder.toString(),

MediaType.APPLICATION_XML));

invGoodRequest.configuration().register(new HttpBasicAuthFilter("tomcat", "tomcat"));

Collection<Invocation> invCollection = Arrays.asList(invGoodRequest,invBadRequest);

// Execute the invocation as a batch processfor (Invocation currentInvocation : invCollection) {

// By default invoke() returns a response but we also have an// overloaded method that takes the target class as parameterResponse response = currentInvocation.invoke();

}

Filters and Handlers

Message request pre-processing and response post-processing via well-defined extension points on the client and server side.

Use cases – logging, stream marshalling(Gzip etc), security Filters

Each filter can decide to continue or break the chain Filters in the filter chain are ordered according to their binding

priority The binding priority is an annotation that specifies an integer

based priority You can match what filters are applied to what resources –

default is global Do not directly invoke the next filter in the chain – hence is

called Non wrapping filter chain Filters implementing this interface must be annotated with

@Provider to be discovered by the JAX-RS runtime

Filters and Handlers@Provider@Logging@BindingPriority(1)public class LoggingFilter implements ContainerRequestFilter,

ContainerResponseFilter {private static Logger logger = LoggerFactory.getLogger(LoggingFilter.class);

/* * @see * javax.ws.rs.container.ContainerResponseFilter#filter(javax.ws.rs.container * .ContainerRequestContext, javax.ws.rs.container.ContainerResponseContext) */@Overridepublic void filter(ContainerRequestContext reqContext,

ContainerResponseContext responseContext) throws IOException {logger.info("POST request filter for server and the response entity class

is: "+ responseContext.getEntity().getClass().getCanonicalName());

}/** @see * javax.ws.rs.container.ContainerRequestFilter#filter(javax.ws.rs.container * .ContainerRequestContext) */@Overridepublic void filter(ContainerRequestContext reqContext) throws IOException {

logger.info("PRE request filter for server: "+ reqContext.getRequest().toString());

}}

Filters and Handlers

@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(value = RetentionPolicy.RUNTIME)@NameBindingpublic @interface Logging {

}

@GET@Produces({ "application/xml; qs=0.9", MediaType.APPLICATION_JSON })@Loggingpublic String sayXMLHello() {

return "<?xml version=\"1.0\"?>" + "<hello> Hello Jersey" + "</hello>";

}

Filters and Handlers

Handlers are wrapping i.e. It directly invokes the next handler in the handler chain

Typically used for message data marshalling and un-marshalling

By default handlers are global but mechanism is provided for static(@NameBinding) as well as dynamic binding(DynamicBinding interface) of a filter or handler to a particular resource or resource method

Typically used to say compress streams API still work in progress

Asynchronous Messaging

Server API support Execute long running server processes Frees up container threads to service other

requests By default remain suspended till the server

event completes Resume operation once the server event is

generated Client API support

Asynchronous request invocation API

Asynchronous Messaging - Serverpublic static final String NOTIFICATION_RESPONSE = "Hello async world!";private static final Logger LOGGER = LoggerFactory.getLogger(HelloResource.class.getName());private static final int SLEEP_TIME_IN_MILLIS = 1000;private static final ExecutorService TASK_EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("long-running-resource-executor-%d").build());private static int count = 0;

/** * Asyc method server support * * @param asyncResponse */@Path("async")@GETpublic void longGet(@Suspended final AsyncResponse asyncResponse) {

TASK_EXECUTOR.submit(new Runnable() {

@Overridepublic void run() {

try {Thread.sleep(SLEEP_TIME_IN_MILLIS);LOGGER.debug("Received request for async processing: "

+ HelloResource.count++);} catch (InterruptedException ex) {

LOGGER.error("Response processing interrupted", ex);}asyncResponse.resume(NOTIFICATION_RESPONSE);

}});

}

Asynchronous Messaging - Client@Testpublic void testAsyncClientRequests() throws InterruptedException {

Client client = ClientFactory.newClient();WebTarget webResource = client.target(getBaseURI()).path("hello/async");final int REQUESTS = 10;final CountDownLatch latch = new CountDownLatch(REQUESTS);for (int i = 0; i < REQUESTS; i++) {

webResource.request(MediaType.TEXT_PLAIN).async().get(new InvocationCallback<Response>() {

@Overridepublic void completed(Response response) {

try {final String result = response

.readEntity(String.class);Assert.assertEquals("Hello Jersey", result);

} finally {latch.countDown();

}}@Overridepublic void failed(ClientException exception) {

System.out.println("Exception while invocation: "+ exception.getMessage());

latch.countDown();}

});}

}

Validation

Services need to validate data before processing Bean Validation is a Java specification which:

provides a unified way of declaring and defining constraints on an object model.

defines a runtime engine to validate objects Already have JSR 349: Bean Validation 1.1 which

needs to be integrated to JAX-RS Support for constraint annotations in

Methods (response entities) Fields & properties Parameters (in request entity) Resource classes

Validation

@POST@Consumes(MediaType.APPLICATION_FORM_URLENCODED)@Produces(MediaType.APPLICATION_XML)public String searchEmployee(@NotNull @FormParam("name") String name,

@OrderNumber @FormParam(“orderNumber") String orderNumber,@FormParam(“department") String department) {

@Constraint(validatedBy = OrderNumberValidator.class) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) public @interface OrderNumber {

String message() default "{custom message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};

}

public class OrderNumberValidator implements ConstraintValidator<OrderNumber, String> {/** Configure the constraint validator based on the elements specified at the

time it was defined. */ public void initialize(OrderNumber constraint) { ... } /** Validate a specified value. returns false if the specified value does not conform to the definition */ public boolean isValid(String value, ConstraintValidatorContext context) {

//validate value }

}

Default

Custom

HyperMedia

Use HATEOAS (Hypermedia As The Engine Of App State) REST principle

Structural links are used to avoid sending a complete representation of a resource and enable lazy loading. The clients can follow these type of links to retrieve the "pieces" they need.

Transitional link is used to update the state of a resource and is typically identified by a "rel" attribute.

Structural links are normally in the entity; transitional links could be in link headers or the entity.

Support transitional links only

Improved Connection Negotiation This would allow a server to specify a

preferred MIME type if the client does not care about it.

An unspecified value takes the default value of 1

The rule says "application/json" will be served as the preferred type if there is no Accept header from the client.

@GET@Produces({ "application/xml; qs=0.9", MediaType.APPLICATION_JSON })@Loggingpublic String sayXMLHello() {return "<?xml version=\"1.0\"?>" + "<hello> Hello Jersey" + "</hello>";}

WADL/WSDL 2.0

The Web Application Description Language (WADL) is a machine-readable XML description of HTTP-based web applications (typically REST web services).

REST doesn’t really require description; follow links

Additionally because the WADL generates machine readable code, it would break when the interface changes – tight coupling

WSDL 2.0 adds supports for REST services Sample WADL available at

http://odata4j-sample.appspot.com/datastore.svc/application.wadl

Odata - Introduction

Open Data Protocol (OData) is a Web protocol for querying and updating data

Based on Web technologies such as HTTP, Atom Publishing Protocol (AtomPub) and JSON

The OData Protocol is different from other REST-based web service approaches in that it provides a uniform way to describe both the data and the data model

The current version of Odata specifications is version 3; however the java implementation for OData (called OData4j) supports version 2

OData4j will move to version 3 after release 1.0; currently at 0.7

ATOM

Atom is an XML-based document format that describes lists of related information known as "feeds“

Feeds are composed of a number of items, known as "entries", each with an extensible set of attached metadata

The "atom:entry" element represents an individual entry, acting as a container for metadata and data associated with the entry.

It can be child of an atom:feed element OR it can be the top level element

ATOM Example

<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom">

<title>Example Feed</title> <link href="http://example.org/"/> <updated>2003-12-13T18:30:02Z</updated> <author>

<name>John Doe</name> </author> <id>urn:uuid:60a76c80-d399-11d9-b93C-

0003939e0af6</id> <entry> <title>Atom-Powered Robots Run Amok</title> <link

href="http://example.org/2003/12/13/atom03"/> <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-

80da344efa6a</id> <updated>2003-12-13T18:30:02Z</updated>

<summary>Some text.</summary> </entry>

</feed>

XML Namespace

ATOM feedMetadat

a

Entry

Odata Structure

Feed Metadata (title, id, updated etc) Collection of entries

Entry Properties (primitives & complex) Links (related entries and related feeds)

Service document that lists all the top level feeds – usually serviced by the service root URI endpoint

OData services can also expose Service Operations, which are simple, service-specific functions that accept input parameters and return entries or complex/primitive values.

The data is usually serviced by a producer who can expose data from relational databases, web services etc

The producer takes care of transformation between EDM and the underlying data access layer

EDM

Entity Data Model (EDM) - is the underlying abstract data model used by OData services to formalize the description of the resources it exposes

All data services may also expose a Service Metadata Document that describes the data model (i.e. structure and organization of all the resources) exposed as HTTP endpoints by the service.

Metadata is available at the endpoint using convention $metadata

The central concepts in the EDM are entities and associations.

EDM is defined using an XML language called conceptual schema definition language (CSDL)

EDM

EntityTypes – data model definitions(for example customer, product etc)

Entities – instances of Entity types with a key and structured type consisting of list of properties

Complex Types - structured types also consisting of a list of properties but with no key

Entity Key – primary key either single or composite Associations - define the relationship between two

or more Entity Types Navigation Properties - special properties on Entity

Types which are bound to a specific association and can be used to refer to associations of an entity

OData Producers

Create a factory that OData4j can use to initialize the producer

OData4j currently supports JPAProducer & InMemoryProducer. It also plans to support JDBCProducer in the near future.

You can write your own producer and it has to implement the org.odata4j.producer.ODataProducer interface.

Registering Producer

public class ODataJPAProducerFactory implements ODataProducerFactory {

private String persistenceUnitName = "odataJPAService";private Strig namespace = "odataJPA";private int maxResults = 50;

@Overridepublic ODataProducer create(Properties arg0) {

EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName);

JPAProducer producer = new JPAProducer(emf, namespace, maxResults);return producer;}

}<servlet>

<servlet-name>OData</servlet-name><servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-

class><init-param>

<param-name>javax.ws.rs.Application</param-name><param-value>org.odata4j.jersey.producer.resources.ODataApplication</

param-value></init-param><init-param>

<param-name>odata4j.producerfactory</param-name><param-value>com.synerzip.odata.producer.ODataJPAProducerFactory</

param-value></init-param><load-on-startup>1</load-on-startup>

</servlet>

Inject Producer

Register Producer

URI Convention

URI Used for Example

$links Get all associations between entries

http://localhost:8080/odata_example/odatajpa.svc/SupplierParts(1L)/$links/part

$orderby

Order data based on desc or asc (default) options

http://localhost:8080/odata_example/odatajpa.svc/SupplierParts?$orderby=quantity desc

$top Return first N items of the set

http://localhost:8080/odata_example/odatajpa.svc/SupplierParts?$orderby=quantity desc&$top=4

$skip Skip the first N records of the entity set and get next

http://localhost:8080/odata_example/odatajpa.svc/SupplierParts?$skip=2&$top=2

$filter Select only subset of entities that match the predicates provided – rich set of operators and functions

http://localhost:8080/odata_example/odatajpa.svc/SupplierParts?$filter=(quantity gt 200 and quantity lt 300) or shipperId eq 10

URI Convention

URI Used for Example

$expand

Eagerly load associations inline

http://localhost:8080/odata_example/odatajpa.svc/Supplier?$expand=supplierParts

$format

Specify what media type to be used for response(Atom/Xml/Json)

http://localhost:8080/odata_example/odatajpa.svc/Supplier?$expand=supplierParts&$format=Json

$select Return only subset of properties

http://localhost:8080/odata_example/odatajpa.svc/Supplier?$select=supplierCity, supplierId

$inlinecount

Response should include the count on entities after applying $filter. Valid values are allpages/none

http://localhost:8080/odata_example/odatajpa.svc/Supplier?$inlinecount=allpages<m:count>16</m:count>

CUD Operations using Consumer// create the new productOEntity newSupplier = consumer.createEntity("Supplier")

.properties(OProperties.string("supplierId", "S200"))

.properties(OProperties.string("supplierName", "Smith A"))

.properties(OProperties.string("supplierCity", "Boston"))

.properties(OProperties.string("supplierState", "MA"))

.properties(OProperties.decimal("supplierStatus", 20L))

.execute();

// update the newly created productconsumer.updateEntity(newSupplier)

.properties(OProperties.string("supplierName", "Carl A"))

.execute();

report("newSupplier name after update: "+ consumer.getEntity("Supplier", "S200").execute().getProperty("supplierName").getValue());

// update the newly create product using mergeconsumer.mergeEntity("Supplier", "S101")

.properties(OProperties.string("supplierName", "Zack A"))

.execute();

report("newPart rating after merge: "+ consumer.getEntity("Supplier", "S101").execute().getProperty("supplierName").getValue());

// clean up, delete the new productconsumer.deleteEntity("Supplier", "S200").execute();

Batch Operations

Odata supports executing multiple operations sent in a single HTTP request through the use of Batching

The batch requests can be sent as POST with the path containing $batch

OData Batch Request is represented as a Multipart MIME v1.0 message

The OData4j implementation is inefficient in the sense that it executes the individual batch items in a separate transaction

Questions?