> For the complete documentation index, see [llms.txt](https://amartyushov.gitbook.io/tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://amartyushov.gitbook.io/tech/specifications/jax-rs.md).

# JAX-RS

JAX-RS is a specification, a set of interfaces and annotations offered by Java EE. Well-known Implementations are [RESTEasy](http://resteasy.jboss.org/) and [Jersey](https://jersey.java.net/).�

{% file src="/files/-Ly36ZlmPOQh3OgGMAH4" %}
JAX-RS 2.1 Specification
{% endfile %}

## Applications

JAX-RS application has \[1..] resources and \[0..] providers.

**Configuration:** via application-supplied subclass of `Application`.

### Publications

Applications are published in diff ways:&#x20;

1. if app runs in Java SE environment
2. within container (is packed as .war file)&#x20;
   1. app classes are packaged in `WEB-INF/classes` or `WEB-INF/lib` and required libs are packaged in `WEB-INF/lib`&#x20;

![](/files/-LxvJkhGbjatStQzxdt_)

## Resources

Is a POJO that has at least one method annotated with `@Path` or a request method designator.

By **default** a **new** **resource** class **instance** is **created** for **each request** to that resource.

![](http://www.plantuml.com/plantuml/svg/LSyxqi8m30JWtgVGfFaVCxa2auFGAFQI61pBo7BEZw40Kk-tUapLMArjoVsGeafJ3_AIgsdp5YMxt_y_6ZKkguqd6jwHA9rphEGv9OJ1VOZYtb0DWG8ASa3s4RLhLb3CLtWx5hWKbQAH3RJ1LWclqTtkSZjIcPzrwY7l71FF2TrUH6dnNwxVxAQ-kl_r0000)

### Resource methods

JAX-RS resource method designators: @GET, @POST, @PUT, @DELETE, @PATCH, @HEAD, @OPTIONS

Methods must be `public`.

Method parameters are mapped from the request according to the semantics of annotation:

* @MatrixParam - extracts the value of URI matrix parameter
  * Matrix parameters are name-value pairs embedded within the path of a URI string
  * e.g. `http://example.cars.com/mercedes/e55;color=black/2006`
  * `color=black` is matrix parameter
* @QueryParam - extracts the value of URI query parameter
* @PathParam - extracts the value of URI template parameter
* @CookieParam - extracts the value of a cookie
* @HeaderParam - extracts the value of header
* @FormParam
* @Context - injects an instance of supported resource. :warning: TODO investigate later
* `@DefaultValue` is used to supply a default value for parameter
* `@Encode` is used to disable auto URI decoding of param value

### Return type

* `void` - results in an empty entity body with **204** status code
  * **204 status code** means that the request was received and understood, but that there is no need to send any data back
* `Response`&#x20;
  * entity property of *Response* -mapped-> entity **body**
  * status property of *Response* -mapped-> status **code**
  * ```
    if (Response == null) {
        status_code = 204;
    } 

    if (Response.status is NOT set) {
        if (Response.entity != null) {
            status_code = 200;
        }                
        if (Response.entity == null) {
            status_code = 204;
        }                
    }
    ```
  * use `Response` to provide metadata; use `ResponseBuilder`;
* `GenericEntity`&#x20;
  * *Entity* property of *GenericEntity* -mapped-> entity **body**
  * ```
    if (GenericEntity == null) {
        status_code = 204;
    }
    if (GenericEntity != null) {
        status_code = 200;
    }
    }}}
    ```
* `Other`&#x20;
  * returned instance -mapped-> entity **body**
  * ```
    if (class is anonymous inner class) {
        use super class;
    }
    ```
  * ```
    if (return_value == null) {
        status_code = 204;
    } else {
        status_code = 200;
    }
    ```

![](/files/-Ly-6ZfNMWz4_ZFxU82Z)

To illustrate consider a method that always returns an instance of ArrayList\<String> (directly or wrapped).

![](/files/-Ly-763SyP72xzlqz8-J)

### Exceptions

```
// map WebApplicationException to response
exceptionProvider = exceptionMappingProvider<WebApplicationException>

if (!exception.response.contains(entity) && exceptionProvider.isAvailable()) {
    Response instance = exceptionProvider.create();        
} else {
    use exception.response directly
}
// finished
```

```
exceptionProvider = exceptionMappingProvider<? super CurrentException>

if (exceptionProvider.isAvailable()) {
    
    try {
        Response instance = exceptionProvider.create();        
    } catch () {
        return server_error_response(status code 500) to client
    }
}
```

```
if (unchecked_exception not_mapped) {
    re-throw and propagate to underlying container
} 

if (checked_exception not_mapped 
    && checked_exception cannot_be_thrown_directly) {
     
    wrappedException = container_specific_exception.wrap(checked_exception)
    throw to underlying container           
}    
```

* Servlet-based implementation wrapper `ServletException`&#x20;
* JAX-WS Provider-based implementation wrapper `WebServiceException`&#x20;

### HEAD and OPTIONS

```
// how HEAD requests are handled
if (method.annotatedWith(HEAD) is present) {
    call method
} else {
    call method.annotatedWith(GET)
    discard returned entity
    
    // Performance risk if entity creation is expensive
}
```

```
// how OPTIONS requests are handled
if (method.annotatedWith(OPTIONS) is present) {
    call method
} else {
    generate response using metadata provided by JAX-RS annotations 
        on matching class and its methods
}
```

## URI Templates

```
@Path("widgets/{id}")
public class Widget {
... }
```

:bulb: This is a relative path with a combination of deployment context and application path `@ApplicationPath` .

`@Path("widget list/{id}")` equivalent to `@Path("widget%20list/{id}")` (encoded automatically).

:bulb: Annotation value can be a regex, e.g.

```
@Path("widgets/{path:.+}")
public class Widget {
... }
```

any request starting with `widgets` and containing at least one more path segment will match. E.g. `widgets/small/a` => the value of path will be `small/a`

### Sub Resources

Methods of resource class annotated by `@Path` are either:

* sub-resource methods => handle HTTP request **directly**&#x20;
* sub-resource locators => return an object or class that will handle HTTP request
  * may have all the same parameters as a normal resource method, except they must not have entity parameter

```
@Path("widgets")
public class WidgetsResource {
 
    @GET
    @Path("offers")
    public WidgetList getDiscounted() {...} // sub-resource method
    
    @Path("{id}")
    public WidgetResource findWidget(@PathParam("id") String id) {
      return new WidgetResource(id);
    } 
}

public class WidgetResource {
    public WidgetResource(String id) {...}
    
    @GET
    public Widget getDetails() {...}
}
```

## Media type capabilities

* @Consumes
* @Produces

Can be applied to resource `method`(**overrides** specified on class and on provider), resource `class`, entity `provider`.

:bulb: Default is `"*/*"`

```
@Path("widgets")
@Produces("application/widgets+xml")
public class WidgetsResource {

    @GET
    public Widgets getAsXML() {...}
    // response media type is "application/widgets+xml", taken from WidgetsProvider
    
    @GET
    @Produces("text/html")
    public String getAsHtml() {...}
    // String containg "text/html" will be returned, written by default impl of
    // MessageBodyWriter<String>

    @POST
    @Consumes("application/widgets+xml")
    public void addWidget(Widget widget) {...}
    // Value of widget will be mapped from request entity using WidgetProvider
}

@Provider
@Produces("application/widgets+xml")
public class WidgetsProvider implements MessageBodyWriter<Widgets> {...}

@Provider
@Consumes("application/widgets+xml")
public class WidgetProvider implements MessageBodyReader<Widget> {...}
```

### Multiple media types

When accepting multiple media types **client** may indicate preference by using relative quality factor `q parameter` (`q-value` ). `Q-value` is \[0..1]&#x20;

* 0 - undesired
* 1 - highly desired (default value)

Example for above code: GET with header Accept: "text/html; q=1, application/widgets+xml; q=0.8" will match `getAsHtml` instead of `getAsXML` because of q.

A **server** can also indicate media type preference using the `qs` parameter.

```
@Path("widgets2")
public class WidgetsResource2 {

    @GET
    @Produces("application/xml; qs=1", "application/json; qs=0.75")
    public Widgets getWidget() {...}
}
```

With `GET` request with header Accept: "application/\*; q=0.5" => media type `application/xml` is selected due to higher `qs` value.

![](http://www.plantuml.com/plantuml/svg/VSunhi8m48JXtgUOxbMvG8gdH3I82W45hMMFYANOZdRNaRWz5WqLrF_gsvdKl5ZBg_j3PamUzjoes1KBVQIe2sjYCGp_KCfEmOZZEusOG-1chbGZ93qM6v6zXOKAgvZEKcCBL9pc-t2vkb_KeHRhRRZrzr_1VgGjVy299VRjBm00)

```
if (@Produces.value != request Accept header) {
    sub-resource method is not invoked
}

if (@Consumes.value != request Content-Type header) {
    sub-resource method is not invoked    
}
```

## Annotation inheritance

:warning: For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.

```
public interface ReadOnlyAtomFeed {
     @GET @Produces("application/atom+xml")
     Feed getFeed();
}
     
@Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
     public Feed getFeed() {...}
}
```

`ActivityLog.getFeed()` inherits `@GET` and `@Produces` from the interface.

:warning: But, conversely&#x20;

```
@Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
    @Produces("application/atom+xml")
    public Feed getFeed() {...}
}
```

In the above case @GET annotation from ReadOnlyAtomFeed.getFeed() is not inherited. Because

> If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored

## Providers

Annotation `@Provider` is used by JAX-RS runtime to automatically discover provider classes via mechanisms such as class scanning.

* Provider must have public constructor
* Constructor may include parameter annotated with `@Context`
* Providers may be annotated with `@Priority`. `@Priority(1)` > `@Priority(10)`
* Default priority for application-supplied providers is `javax.ws.rs.Priorities.USER`

```
/**
* User-level filter/interceptor priority.
*/
public static final int USER = 5000;
```

### MessageBodyReader

Message entity body -> Java type

* implement `MessageBodyReader`
* annotate with `@Provider` (for automatic discovery)

`MessageBodyReader` always operate on **decoded** HTTP entity **body** (decoded by *container* or *JAX-RS* runtime).

#### How entity body is mapped to Java method parameter:

```
var media_type = request.Content_Type
if (media_type == null) {
    media_type = application/octet_stream
}

-----------------

Identify Java type of sub-resource method parameter

-----------------

Select set of MessageBodyReader that support media_type

-----------------

for (MessageBodyReader reader : set) {
    if (reader.isReadable()) {
        if (more readers suitable) {
            choose reader with highest priority
            reader.readFrom(entity_body) 
            done
        }
    }
}
server throw NotSupportedException (415 status) + no entity
client generate ProcessingException
```

### MessageBodyWriter

`javax.ws.rs.ext.MessageBodyWriter`&#x20;

Contract for a provider that supports the conversion of a Java type to a stream.

A `MessageBodyWriter` implementation may be annotated with `@javax.ws.rs.Produces` to restrict the media types for which it will be considered suitable.

Providers implementing `MessageBodyWriter` contract must be either programmatically registered in a JAX-RS runtime or must be annotated with `@javax.ws.rs.ext.Provider` annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.

Steps for mapping return value to message entity body:

```
var object = what_will_be_mapped_to_message_entity_body()

if (object instanceof <? extends Response>) {
    object = Response.entity
} else {
    object
}

-----------------

var media_type = determine()

-----------------

Select set of MessageBodyWriter that support object and media type of message body

-----------------

sort set (TODO criteria)

-----------------

for (MessageBodyWriter writer : set) {
    if (writer.isWriteable()) {
        if (more writers suitable) {
            choose writer with highest priority
            writer.writeTo()
            done
        }
    }
}
server generates InternalServerErrorException (status 500) + no entity
client generate ProcessingException
```

### Media type capabilities

Readers and writers may restrict media types they support using `@Consumes` and `@Produces`. Absence of annotation means `"*/*"`.

### ExceptionMapper

must **implement** `javax.ws.rs.ext.ExceptionMapper`

Contract for a provider that maps Java exceptions (checker or runtime) to `javax.ws.rs.core.Response`. If **>1** exception providers are applicable => use with highest **priority**.

Providers implementing `ExceptionMapper` contract must be either programmatically registered in a JAX-RS runtime or must be annotated with `@javax.ws.rs.ext.Provider` annotation to be automatically **discovered** by the JAX-RS runtime during a provider scanning phase.

## Client API

`javax.ws.rs.client`&#x20;

## Filters

Filters execute code at an extension point but **without wrapping a method invocation**.

`@Provider`

```java
public interface ClientRequestFilter {    // for client 
    void filter(ClientRequestContext requestContext) throws IOException;
}
// before HTTP request is delivered to the network

public interface ClientResponseFilter {    // for client 
    void filter(ClientRequestContext requestContext,
        ClientResponseContext responseContext) throws IOException;
}
// is executed upon receiving a server response
                   
public interface ContainerRequestFilter {    // for server
    void filter(ContainerRequestContext requestContext) throws IOException;
}
// is executed upon receiving a request from a client

public interface ContainerResponseFilter {    // for server
    void filter(ContainerRequestContext requestContext,
        ContainerResponseContext responseContext) throws IOException;
}
// before HTTP response is delivered to the network
```

Filters are grouped in `Filter chain` . There is a separate filter chain for each extension point (e.g. `ClientRequest`, `ClientResponse`, `ContainerRequest`, `ContainerResponse`, `PreMatchContainerRequest`).&#x20;

![](http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuU8AJYqgIosABqejBiZFAqvLqBLJY7REoKpDAubLi5A0i8AWacwPEIMfHGeQoHc9cNcQN1uX8G7noOavEGeskZcfQIMws7GMbgOMbw1fv798pKi1sWu0)

`ContainerRequestFilter` annotated by **@PreMatching** executed upon receiving client request but **before a resource method is matched**.

## Interceptors

Entity interceptors **wrap** around a method **invocation** at a specific extension point.

`@Provider`

```java
public interface ReaderInterceptor {
    Object aroundReadFrom(ReaderInterceptorContext context)
        throws java.io.IOException, javax.ws.rs.WebApplicationException;
}       
```

Reader interceptor wraps around calls to `MessageBodyReader` readFrom().

```java
public interface WriterInterceptor {
    void aroundWriteTo(WriterInterceptorContext context)
        throws java.io.IOException, javax.ws.rs.WebApplicationException;
} 
```

Writer interceptor wraps around calls to `MessageBodyWriter` writeTo().

## Binding

### Global

A filter/interceptor that has no annotations is assumed to be bound globally => applies to all resource methods in application (well either `Provider` or register manually in Application)

### Name binding

```java
@Provider
@MyLogged
class LoggingFilter implements ContainerRequestFilter{...}

@Provider
@MyAuthenticated
class AuthenticationFilter implements ContainerRequestFilter{...}


@Path("*/*")
public class MyResource {
    @MyLogged @MyAuthenticated
    @GET
    public String hello() {...}
}

@NameBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyLogged { }

@NameBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAuthenticated { }
```

## JAX-RS request processing cycle

![](http://www.plantuml.com/plantuml/svg/TP5DQiCm48NtFiMGJN_0LoWfsHKAnLcq46UXoY-sG1wvet5BRjyXjMaBoavelk_fITfcTQBp6AiRUdfygviz2JvcPAL9aaVEWNloPnzHtTxVKIsX7xH-eCCk1h2kz94sBu9wTEe7CrhsYTK5XhIy1fv2LCYcMZDCILclENbAd56G6hWEGYq7jed7fEdV_DbAknxqdhgpKNB13VXnSvhDiVqHEgJE5h_tNA3TMeDAFI9EbsDU9MYXXerBBRwC_giVzf1Fqvib_qZskjKMtDbVV0C0)

## Open questions:

* How JAX-RS configuration is done?
*
