Skip navigation
1 2 3 Previous Next

RESTEasy

33 posts

I'm pleased to announce that, at the end of last week, RESTEasy 4.2.0.Final has been released! It's less than two months since the previous minor has been tagged, however the community has been quite active lately (thanks!!) and we had a few interesting contributions really deserving a release:

  • Great performance improvements: two parallel efforts on reducing memory footprint improved our internal benchmark numbers quite a lot; both JAX-RS client and server applications should be sensible faster as a consequence of a lower memory allocation rate. Moreover, a couple of nasty bugs resulting in performance penalties in some corner cases have been fixed.
  • Vert.x Client HTTP engine: we now have another implementation of the RESTEasy Client HTTP engine interface, based on Eclipse Vert.x and properly supporting async / non-blocking IO.
  • Spring Web REST integration (experimental): RESTEasy now provides the ability to process Spring Web REST annotations (i.e. Spring classes annotated with @RestController) and handles related REST requests without delegating to Spring MVC.

 

The latest artifacts are available on the JBoss Nexus repository as well as Maven Central repository. Quarkus is being upgraded as well to benefit from the latest additions mentioned above!

Enjoy!

RESTEasy Spring Boot Starter 4.0.0.Final released

 

 

RESTEasy Spring Boot Starter 4.0.0.Final is released and can be fetched from Github:





Here is the maven dependency section to include the new release in your project:

 

<dependency>
   <groupId>org.jboss.resteasy</groupId>
   <artifactId>resteasy-spring-boot-starter</artifactId>
   <version>4.0.0.Final</version>
   <scope>runtime</scope>
</dependency>

 

In this release, it uses RESTEasy 4.0.0.Final as dependency.

 

From this release, the master branch is upgraded to use RESTEasy 4.x as dependency.

 

The future 3.x releases development will be continued in branch 3.x:





Folks,

as previously anticipated, RESTEasy 4.1.0.Final has been released! It brings the new MicroProfile REST Client 1.3 implementation on 4.x series too, the same provided last week with 3.8.0.Final.

Besides that, few other new features and improvements are included in this release; something worth mentioning is that the community contributed two new modules for integration with the Project Reactor , namely a new implementation of the Async client engine interface based on Reactor Netty and initial support for Flux and Mono types in resources, similarly to what RESTEasy already does with RxJava 2 types.

 

While 3.8.0.Final is soon landing in WildFly and Thorntail, the upgrade to 4.1.0.Final has just been merged in Quarkus master, so the new version will most likely be part of next Quarkus release!

Stay tuned and give the new versions a try!

Thanks!

Last week the release of MicroProfile 3.0 was announced. It comes with few additions and updates in some of its specifications, one of them being the MP REST Client which has been upgraded to version 1.3.

RESTEasy used to implement an old version (1.0) of MP REST Client, while more recent versions have been provided as part of the SmallRye project. Starting from MP 3, we decided to join the efforts of the two teams and provide the most up to date version of that spec in RESTEasy.

So today I'm happy to announce the availability of RESTEasy 3.8.0.Final, whose main new feature is the implementation of MicroProfile REST Client 1.3. The artifacts have been built and released to both Central and JBoss Nexus Maven repositories. The new documentation as well as the binaries, are available on the website.

A new release from the 4.x stream is also coming very soon and will of course include the same MP REST Client 1.3 implementation too.

Stay tuned and try RESTEasy 3.8... feedback is welcome!

RESTEasy Spring Boot Starter 3.1.0.Final is released and can be fetched from Github:





Here is the maven dependency section to include the new release in your project:

 

 

The change in this release is that it adopts RESTEasy 3.7.0.Final as dependency.

At the end of March I was announcing RESTEasy 4 CR1 and telling the final was coming soon. Unfortunately it has taken more than I hoped for the transition; the nice thing, however, is that was because the community (especially Quarkus one) really tested the CRs and we got few bugs reported (and fixed ;-) ). In any case earlier today the 4.0.0.Final artifacts have been built and released to both Central and JBoss Nexus Maven repositories. The new documentation, as well as the binaries, are available on the website.

Previous blog posts already provided references to the new features coming with RESTEasy 4, I really encourage reading them.

Going beyond new features, I believe this release sets the ground for future exciting developments in RESTEasy; the project got rid of old / deprecated pieces and is in a better position for accomodating the implementation of new and modern features, especially for Quarkus cloud-related scenarios.

Stay tuned and try RESTEasy 4... feedback is welcome!

Earlier this week a release candidate version (4.0.0.CR1) of RESTEasy has been tagged. The artifacts are built and available on the Maven Central and JBoss Nexus repositories.

This is the result of many months of work on RESTEasy master branch in parallel with the continuous enhancements and maintenance on the 3.x stream ; below I'm highligthing some of the areas on which the team has been focussing.

 

Major cleanup

During last 2 years, we have spent a lot of energies in getting rid of not-needed anymore (or deprecated) classes, components, dependencies and such. As an example, RESTEasy 3 still included the old RESTEasy 2 client implementation pre-dating the JAX-RS 2 spec client; clearly, that's deprecated since years and possibly even confusing users; with RESTEasy 4 it's gone. Similarly, old and deprecated modules have been dropped, including:

  • the Jettison, Jackson 1 (org.codehaus one), Abdera and YAML providers
  • the Netty 3 server adapter
  • the RxJava (1) integration
  • the TJWS embedded server
  • few security modules duplicating Keycloak services.

 

Code restructure and optimizations

Together with the removal of old cruft, we've made some progress in restructuring the project in a way that:

  1. allows running with JDK9+ (JDK11 tested at the time of writing) and the JPMS module system (as an example, we had multiple packages split between different modules that had to be dealt with);
  2. allows clearly figuring out whether a given class is to be considered as part of RESTEasy public API or not.

This clearly resulted in a lot of refactoring, with some classes that had to be moved. We tried to cause as less disruption as possible with the changes, still few non-backward compatible changes had to be performed and are going to be documented in a migration guide to be prepared before releasing 4.0.0.Final. Regarding the second goal above, the big resteasy-jaxrs and resteasy-client modules have been split into resteasy-core-spi, resteasy-client-api, resteasy-core and resteasy-client, with the first and second ones to be considered as public modules, for which we're expected to retain backward compatibility till next major release.

The reason for the refactoring aimed at the public/private module separation is of course easier future maintenance; however, we also did some refactoring for optimizing few parts of the project that we knew as problematic performance-wise. That's the case of the ResteasyProviderFactory class, which has grown a lot year after year and had a lot of state info being copied over and over for most common usage scenarios, badly increasing memory pressure. In RESTeasy 4, when a ResteasyProviderFactory instance is known to be needed for client usage only, the server side specific data is not computed and stored; the opposite happening for server-side only scenarios, with a nice memory allocation reduction.

 

New features

RESTEasy 4 is coming with all the features, fixes and upgrades that went into the 3.x releases (350+ jira issues have been solved). To name some of the recent additions, that includes the JAX-RS 2.1 implementation, the Reactive programming support, the new HTTP client engines (including NIO), etc.

On top of that, there're few additions that could not be backported to 3.x branches and are new in RESTEasy 4:

 

Quarkus

While RESTEasy 3 main integration targets are WildFly and JBoss EAP 7.x, when the efforts for RESTEasy 4 were started we had no clear idea of which container would have been consuming the project, so we thought we would have targeted standalone usage of RESTEasy. The reason for that clearly were WildFly and JBoss EAP coming with strict rules around backward compatibility and RESTEasy 4 not being able to satisfy them (because of the changes explained above and considering his historical deficiencies in terms of public/private classes separation).

However, when the work on Protean (now Quarkus) started, it became evident that RESTEasy 4 was well positioned for being included in the new stack, especially considering our efforts on reducing the memory footprint and getting rid of useless / old classes while retaining modern functionalities like the reactive programming support. As a consequence, Quarkus has been launched including RESTEasy 4 beta and few days ago I've upgraded it to pull 4.0.0.CR1.

 

So, it's definitely time to give RESTEasy 4 CR1 a try! There're multiple ways you can play with it:

  • in Quarkus, either waiting for next week release or building the latest sources from github master; note, there's a nice quickstart pulling RESTEasy too in the Quarkus Getting Started Guide ;-)
  • standalone, by simply pulling the 4.0.0.CR1 artifacts as dependencies in your Maven project
  • on WildFly 16, by patching its modules (if you can cope with the few API changes we did) with the contents of our jboss-modules module, after having built it from the sources on github.

 

Hopefully, unless something critical is spotted or reported, we'll be releasing Final (including proper documentation) in few weeks from now.

Stay tuned!

The UndertowJaxrsSpringServer

 

#undertow #spring

 

Recently I’m working on migrating resteasy-spring tests to use the Undertow container. Before these tests were using the TJWS embedded container.

 

As resteasy-tjws container is deprecated in master, so there is some work to be done to make undertow container to load the resteasy-spring components correctly.

 

In order to achieve the goal, I created a new module called resteasy-undertow-spring, and it contains a single class called UndertowJaxrsSpringServer.

 

This server class will accept the spring xml configuration file, and load the configured spring context beans into spring provided org.springframework.web.servlet.DispatcherServlet. Here is the relative code in UndertowJaxrsSpringServer:

 

 

In addition, the resteasy-spring module contains a default xml configuration file called springmvc-resteasy.xml, and it can be used by default to setup the resteasy-spring component properly.

 

So users just need to prepare their own spring config file and include the springmvc-resteasy.xml to load the resteasy-spring module. Here is a minimal example of the config:

 

 

In above config, we can see it includes the springmvc-resteasy.xml provided by resteasy-spring module, and it will scan the org.jboss.resteasy.springmvc.test package to include the user-written jaxrs resources.

 

And here is the code to setup the UndertowJaxrsSpringServer and start it:

 

 

The above code is part of the tests in resteasy-undertow-spring, and the tests can be used as usage example:

 

Resteasy/BasicSpringTest.java at master · resteasy/Resteasy · GitHub

 

And the tests shows more advanced spring configurations that can be used as reference.

We have removed resteasy-jettison-provider from master branch, and here is the tracker bug on this change:

 

https://issues.jboss.org/browse/RESTEASY-1316

 

The major affect is that the resteasy-link module is now dependent on resteasy-jackson2-provider to replace resteasy-jettison-provider to support JAXB annotation -> JSON data marshaling.

 

In addition, Jackson2 has some subtle differences on supporting JAXB annotations comparing with the default JAXB or Jettison marshallers. So some resteasy-link test classes are adjusted to accommodate this change. Here is the relative PR that shows the difference after the migration:

 

https://github.com/resteasy/Resteasy/pull/1850/files#diff-a873f7dcbc6d4b04d29b4f0dd1015f76R17

 

The above change shows the changes in JAXB annotations after changing the JAXB -> JSON provider to resteasy-jackson2-provider for resteasy-link module.

 

Above is the summary of Jettison removal.

# RESTEasy WADL Grammar Support

RESTEasy has added WADL grammar support by this PR:

 

 

- [RESTEASY-1695 Add GRAMMARS into RESTEasy WADL by liweinan · Pull Request #1649 · resteasy/Resteasy · GitHub](https://github.com/resteasy/Resteasy/pull/1649)

 

 

The major change is that `ResteasyWadlGrammar` is added into `ResteasyWadlWriter`:

 

 

 

In addition, the `ResteasyWadlWriter` is rewritten now, and all the static methods are now instance methods. It means users need to create an instance of `ResteasyWadlWriter` and put it into per-deployment scope.

 

 

To avoid people to write the boilerplate code, the `ResteasyWadlDefaultResource` can be used for deployment, and it can be put into the `getSingleton()` method of `Application` class:

 

 

```java

@Provider

@ApplicationPath("/")

public class WadlTestApplication extends Application {

   public static Set<Class<?>> classes = new HashSet<Class<?>>();

   public static Set<Object> singletons = new HashSet<Object>();

...

   @Override

   public Set<Object> getSingletons() {

   ...

         ResteasyWadlDefaultResource defaultResource = new ResteasyWadlDefaultResource();

         singletons.add(defaultResource);

      }

      return singletons;

   }

}

```

 

 

And then the URL `/application.xml` is enabled by the methods inside `ResteasyWadlDefaultResource`.

 

 

To enable the WADL Grammar support, users need to create an instance of `ResteasyWadlGrammar` and put it into the instance of `ResteasyWadlWriter`.

 

 

The recommended way is to use it with `ResteasyWadlDefaultResource`. Here is an example:

 

 

```java

ResteasyWadlDefaultResource defaultResource = new ResteasyWadlDefaultResource();

 

 

ResteasyWadlWriter.ResteasyWadlGrammar wadlGrammar = new ResteasyWadlWriter.ResteasyWadlGrammar();

wadlGrammar.enableSchemaGeneration();

 

 

defaultResource.getWadlWriter().setWadlGrammar(wadlGrammar);

```

 

 

As the code shown above, we have created an instance of `ResteasyWadlGrammar`, and it’s injected into the `ResteasyWadlWriter` instance included by `ResteasyWadlDefaultResource` instance.

 

 

In addition, we have called the `wadlGrammar.enableSchemaGeneration()` method, and it will scan the resources classes, and generate grammar files for JAXB annotated classes. Suppose we have entity class like this:

 

 

```java

import javax.xml.bind.annotation.XmlElement;

import javax.xml.bind.annotation.XmlRootElement;

import java.util.List;

 

 

@XmlRootElement(name = "listType")

public class ListType {

 

 

    private List<String> values;

 

 

    @XmlElement

    public List<String> getValues() {

        return values;

    }

 

 

    public void setValues(List<String> values) {

        this.values = values;

    }

}

```

 

 

And it’s used in resource class:

 

 

```java

import javax.ws.rs.Consumes;

import javax.ws.rs.POST;

import javax.ws.rs.Path;

 

 

@Path("/extended")

public class ExtendedResource {

 

 

    @POST

    @Consumes({"application/xml"})

    public String post(ListType income) {

        return "foo";

    }

}

```

 

 

And if we enable the grammar generation as shown above, then we will get sample output from `/application.xml` like this:

 

 

```xml

$ http http://localhost:8081/application.xml

HTTP/1.1 200 OK

Content-type: application/xml;charset=UTF-8

Date: Wed, 31 Oct 2018 07:57:38 GMT

Transfer-encoding: chunked

 

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<application xmlns="http://wadl.dev.java.net/2009/02">

    <grammars>

        <include href="xsd0.xsd">

            <doc title="Generated" xml:lang="en"/>

        </include>

    </grammars>

</application>

```

 

 

The above output shows that a grammar file is genrated, and it is called `xsd0.xsd`. The instance of `ResteasyWadlDefaultResource` will prepare a URL link named `/wadl-extended`, and it will serve the generated grammar file. Here is the example:

 

 

```xml

$ http http://localhost:8081/wadl-extended/xsd0.xsd

HTTP/1.1 200 OK

Content-type: application/xml;charset=UTF-8

Date: Wed, 31 Oct 2018 07:58:53 GMT

Transfer-encoding: chunked

 

 

<?xml version="1.0" standalone="yes"?>

<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

 

 

  <xs:element name="listType" type="listType"/>

 

 

  <xs:complexType name="listType">

    <xs:sequence>

      <xs:element name="values" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>

    </xs:sequence>

  </xs:complexType>

</xs:schema>

```

 

 

As shown in the above example, the grammar is generated for `ListType` entity class. If you don’t want RESTEasy to generate the grammar file for you, you can use the `includeGrammars()` method provided by the instance of `ResteasyWadlGrammar`. Here is an example:

 

 

```java

ResteasyWadlWriter.ResteasyWadlGrammar wadlGrammar = new ResteasyWadlWriter.ResteasyWadlGrammar();

wadlGrammar.includeGrammars(“application-grammars.xml”);

...

```

 

 

The `application-grammars.xml` file is grammar descriptor file provided by yourself, and it should be put into your project’s classpath. Here is a sample of `application-grammars.xml`:

 

 

```xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<grammars xmlns="http://wadl.dev.java.net/2009/02"

    xmlns:xsd="http://www.w3.org/2001/XMLSchema"

    xmlns:xi="http://www.w3.org/1999/XML/xinclude">

    <include href="schema.xsd" />

</grammars>

```

 

 

From above file we can see `schema.xsd` is the included schema file, and it should also be provided by yourself. Here is a sample of `schema.xsd`:

 

 

```xml

<?xml version=“1.0” encoding=“UTF-8” standalone=“yes”?>

<xs:schema version=“1.0” xmlns:xs=“http://www.w3.org/2001/XMLSchema”>

 

 

    <xs:element name=“listType” type=“listType”/>

 

 

    <xs:complexType name=“listType”>

        <xs:sequence>

            <xs:element name=“values” type=“xs:string” minOccurs=“0” maxOccurs=“unbounded”/>

        </xs:sequence>

    </xs:complexType>

</xs:schema>

```

 

 

And if you have called `wadlGrammar.includeGrammars(“application-grammars.xml”)`, then you will get the included section in `/application.xml`:

 

 

```xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<application xmlns="http://wadl.dev.java.net/2009/02">

    <grammars>

        <include href="schema.xsd"/>

    </grammars>

...

</application>

```

 

 

As the example shown above, the `schema.xsd` is included, and it can be fetched by using `/wadl-extended/schema.xsd` if you have registered the instance of `ResteasyWadlDefaultResource` into your `Application` and setup `ResteasyWadlGrammar` properly:

 

 

```xml

$ http http://localhost:8081/wadl-extended/schema.xsd

HTTP/1.1 200 OK

Content-type: application/xml;charset=UTF-8

Date: Wed, 31 Oct 2018 08:12:56 GMT

Transfer-encoding: chunked

 

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

 

 

    <xs:element name="listType" type="listType"/>

 

 

    <xs:complexType name="listType">

        <xs:sequence>

            <xs:element name="values" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>

        </xs:sequence>

    </xs:complexType>

</xs:schema>

```

 

 

Above is the description about the RESTEasy WADL Grammar feature.

 

 

The other change is that `ResteasyWadlServlet` and `ResteasyWadlServletWriter` is now deprecated, because it doesn’t support the grammar feature, and these two classes will be removed from master branch in the future. Using `ResteasyWadlDefaultResource` is recommended to enable the WADL feature.

Previously, the RESTEasy tracing feature just support the pre-formatted, text-based information like this:

 

$ curl -i http://localhost:8081/foo | head
...
X-RESTEasy-Tracing-000: START       [ ---- /  ---- ms |  ---- %] baseUri=[http://localhost:8081/] requestUri=[http://localhost:8081/foo] method=[GET] authScheme=[n/a] accept=*/* accept-encoding=n/a accept-charset=n/a accept-language=n/a content-type=n/a content-length=n/a
X-RESTEasy-Tracing-001: START       [ ---- /  0.60 ms |  ---- %] Other request headers: Accept=[*/*] Host=[localhost:8081] User-Agent=[curl/7.55.1]
1X-RESTEasy-Tracing-002: PRE-MATCH   [ 0.01 /  2.54 ms |  0.00 %] PreMatchRequest summary: 0 filters
0X-RESTEasy-Tracing-003: REQ-FILTER  [ 0.02 /  5.35 ms |  0.00 %] Filter by [io.weli.tracing.HttpMethodOverride @4b8ca04a]
0X-RESTEasy-Tracing-004: REQ-FILTER  [ 0.03 /  5.66 ms |  0.00 %] Filter by [org.jboss.resteasy.core.AcceptHeaderByFileSuffixFilter @4d5e22c1]
X-RESTEasy-Tracing-005: REQ-FILTER  [ 0.73 /  5.88 ms |  0.00 %] Request summary: 2 filters
  X-RESTEasy-Tracing-006: MATCH       [ ---- /  6.44 ms |  ---- %] Matched resource: template=[[org.jboss.resteasy.core.registry.ClassExpression @7e528471]] regexp=[\Q\E(.*)] matches=[[org.jboss.resteasy.core.registry.SegmentNode @5072e501]] from=[]
  X-RESTEasy-Tracing-007: MATCH       [ ---- /  6.60 ms |  ---- %] Matching path [/foo]
...

 

This kind of tracing info is easy for the human to read, but it’s hard for program to process. Especially when it’s used in a distributed environemnt when the tracing info need to be passed across applications.

 

So now we add the feature to let tracing info to be returned in JSON format. To use this feature, you need to add the tracing-api as dependency firstly:

 

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-tracing-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

 

As shown above, now the tracing-api is a standalone project and it belongs to the resteasy-extensions project:

 


https://github.com/resteasy/resteasy-extensions

 

After including the above basic package, then we need to choose a JSON provider for tracing module to generate JSON formatted info.

 

There are two JSON providers you can choose from and they both support the tracing feature.

 

One is the jackson2 provider:

 

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson2-provider</artifactId>
    <version>4.0.0-SNAPSHOT</version>
</dependency>

 

The other option is to use the json-binding provider:

 

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-json-binding-provider</artifactId>
    <version>4.0.0-SNAPSHOT</version>
</dependency>

 

After including either of the above module, now we can request the resource server for JSON formatted data. The method is to send a format request header:

 

X-RESTEasy-Tracing-Accept-Format: JSON

 

Here is a request example:

 

$ curl -H "X-RESTEasy-Tracing-Accept-Format: JSON" -i http://localhost:8081/foo

 

And the response will be like this:

 

X-RESTEasy-Tracing-000:
[{"duration":0,"event":"START","text":"baseUri=[http://localhost:8081/]
requestUri=[http://localhost:8081/foo] method=[GET] authScheme=[n/a] accept=*/*
accept-encoding=n/a accept-charset=n/a accept-language=n/a content-type=n/a
content-length=n/a
","timestamp":46600869878437},{"duration":0,"event":"START_HEADERS","text":"Other
request headers: Accept=[*/*] Host=[localhost:8081] User-Agent=[curl/7.55.1]
X-RESTEasy-Tracing-Accept-Format=[JSON]
","timestamp":46600870751617},{"duration":18336,"event":"PRE_MATCH_SUMMARY","tex
t":"PreMatchRequest summary: 0
filters","timestamp":46600872781491},{"duration":20724,"event":"REQUEST_FILTER",
"text":"Filter by [io.weli.tracing.HttpMethodOverride
@585b0020]","timestamp":46600876716467},{"duration":19414,"event":"REQUEST_FILTE
R","text":"Filter by [org.jboss.resteasy.core.AcceptHeaderByFileSuffixFilter
@3779d352]","timestamp":46600877017341},{"duration":657192,"event":"REQUEST_FILT
ER_SUMMARY","text":"Request summary: 2 filters",
...

 

After reformat the above response, we can see the response structure is like this:

 

[
{
  “duration”: 0,
  “event”: “START”,
  “text”: “baseUri=[http://localhost:8081/] requestUri=[http://localhost:8081/foo] method=[GET] authScheme=[n/a] accept=*/* accept-encoding=n/a accept-charset=n/a accept-language=n/a content-type=n/a content-length=n/a “,
  “timestamp”: 46600869878437
},
{
  “duration”: 0,
  “event”: “START_HEADERS”,
  “text”: “Other request headers: Accept=[*/*] Host=[localhost:8081] User-Agent=[curl/7.55.1] X-RESTEasy-Tracing-Accept-Format=[JSON] “,
  “timestamp”: 46600870751617
},
{
  “duration”: 18336,
  “event”: “PRE_MATCH_SUMMARY”,
  “text”: “PreMatchRequest summary: 0 filters”,
  “timestamp”: 46600872781491
},
{
  “duration”: 20724,
  “event”: “REQUEST_FILTER”,
  “text”: “Filter by [io.weli.tracing.HttpMethodOverride @585b0020]”,
  “timestamp”: 46600876716467
},
...
]

 

The timestamp is the event start time, and the other fields are quite straightforward.

 

The JSON formatted data should be more suitable to be parsed by code.

 

This feature is currently just in 4.0.0-SNAPSHOT, and haven’t been officially released yet.

It's been a busy week... but before leaving for the week-end I owe you an update ;-) Three new releases have been completed in the last days:

 

As usual, give them a try while they're hot, feedback is welcome!

A couple of months ago, we announced the move of the RESTEasy Spring Boot starter to RESTEasy organization on GitHub. We also mentioned that soon a new version of the starter would have been available... and here we are today, presenting release 2.0.0.Final, which

  • integrates Spring Boot 2 (currently 2.0.3.RELEASE version)
  • relies on the latest RESTEasy 3.x series (currently 3.6.0.Final version).

The artifacts are available on Maven central repository, as well as the JBoss repository. Feel free to pull them and give them a try.

Enjoy!

Now that we've seen RxJava support in RESTEasy, we're ready to build on more reactive applications to illustrate common reactive use-cases.

 

Let's create an application with several types of requests, that we collect statistics on, such as the number of times each request has been called. And let's store those statistics in a Redis instance. For that we will use the Vert.x Redis client because it supports RxJava out of the box.

 

A sample application

 

So we will be importing the following Maven modules:

 

<!-- For RESTEasy -->
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxrs</artifactId>
    <version>4.0.0-SNAPSHOT</version>
</dependency>
<!-- For RESTEasy's support of RxJava 2 -->
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-rxjava2</artifactId>
    <version>4.0.0-SNAPSHOT</version>
</dependency>
<!-- For the Vert.x Redis client -->
<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-redis-client</artifactId>
    <version>3.5.3</version>
</dependency>
<!-- For the Vert.x RxJava 2 support -->
<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-rx-java2</artifactId>
    <version>3.5.3</version>
</dependency>

 

Now, in order to make sure I get a single Redis client for my application, I will have to make it injectable by RESTEasy with the @Context annotation, and there's no support for pluggable injection in JAX-RS so it's a little convoluted, but we can achieve that with the help of this custom Feature:

 

@Provider
public class RedisFeature implements Feature {

  private RedisClient redis;

  public RedisFeature(){
    // connect to the local Redis
    redis = RedisClient.create(Vertx.vertx());
  }

  public boolean configure(FeatureContext context) {
    // this is tied to the deployment, which is what we want for the redis client
    if(context.getConfiguration().getRuntimeType() == RuntimeType.CLIENT)
      return false;
    Dispatcher dispatcher = ResteasyProviderFactory.getContextData(Dispatcher.class);
    if(dispatcher == null) {
      // this can happen, but it means we're not able to find a deployment
      return false;
    }
    dispatcher.getDefaultContextObjects().put(RedisClient.class, redis);
    return true;
  }
}

 

We can now write our three requests that collect usage statistics (they inject the RedisClient):

 

@Path("/")
public class Resource {
  @Context
  private RedisClient redis;

  @Path("req1")
  @GET
  public Single<String> req1() {
    return redis.rxIncr("req1.count").map(count -> "Req1 count: "+count);
  }

  @Path("req2")
  @GET
  public Single<String> req2() {
    return redis.rxIncr("req2.count").map(count -> "Req2 count: "+count);
  }

  @Path("req3")
  @GET
  public Single<String> req3() {
    return redis.rxIncr("req3.count").map(count -> "Req3 count: "+count);
  }
}

 

As you can see we count usage in the Redis keys req1.count, req2.count and req3.count.

 

Now, if we want to display them, we have to get all three values, which either means a lot of nesting with RxJava, or (better) using the Single.zip operator:

 

@GET
public Single<String> info(){
  return Single.zip(redis.rxGet("req1.count"), redis.rxGet("req2.count"), redis.rxGet("req3.count"),
    (req1, req2, req3) -> "Request 1: "+req1+"\nRequest 2: "+req2+"\nRequest 2: "+req3);  
}

 

As you can see, with RxJava, getting several values is a little more verbose than if we were doing it in blocking style. In fact, in real applications it is very common to start most requests with actions that depend on resolving a few asynchronous values. They can be waiting for database results, querying caches, or even obtaining permission lists, but eventually, lots of your requests will start with a Single.zip call to get the values you need in your request. That's annoying, and when they are often the same values, that's just plain boilerplate.

 

The solution

 

What if RESTEasy could take all those async values that you need, and resolve them before it called your resource method? This is called asynchronous injection, and the latest RESTEasy does just that.

 

The async values we want to be resolved are originally of type Single<String>, so their resolved value is of type String. In order to get async injection, we annotate our injected resolved value with @Context, and RESTEasy will look up a ContextInjector that is declared to resolve values to String. In our case, we declare our ContextInjector to resolve values from type Single<String> to String, but any async type is supported, thanks to the existing support for pluggable async types.

 

Once we've declared our ContextInjector, RESTEasy will call it to get the Single<String> that provides the String we want asynchronously injected, and will wait for the async value to be resolved, and only then proceed to inject it in your resource method. This way, when you start your resource method, you already have all your async values resolved!

 

For our example, we're going to describe our redis queries with the @RedisQuery annotation:

 

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisQuery {
  String value();
}

 

Now we can write our new resource method that wants these redis queries injected:

 

@Path("inject")
@GET
public String infoInjection(@Context @RedisQuery("req1.count") String req1,
    @Context @RedisQuery("req2.count") String req2,
    @Context @RedisQuery("req3.count") String req3){
  return "Request 1: "+req1+"\nRequest 2: "+req2+"\nRequest 2: "+req3;
}

 

And all we have to do for that async injection to work is to declare our ContextInjector:

 

@Provider
public class RedisInjector implements ContextInjector<Single<String>, String> {

  @Override
  public Single<String> resolve(Class<?> rawType, Type genericType, Annotation[] annotations) {
    RedisClient redisClient = ResteasyProviderFactory.getContextData(RedisClient.class);
    for (Annotation annotation : annotations) {
      if(annotation.annotationType() == RedisQuery.class) {
        String query = ((RedisQuery) annotation).value();
         // let's inject !
        return redisClient.rxGet(query);
      }
    }
    // not for us: try other injectors
    return null;
  }
}

 

As you can see, we just have to declare that our ContextInjector can provide values of type String via an async type Single<String>, and check the annotations on the injection point to figure out what query to run.

 

As I mentioned previously, this is not limited to async values of type Single, because any async value type is supported via plugins, and in fact only CompletionStage is supported by default (Single being provided by the resteasy-rxjava2 module we're using).

 

Conclusion

We've removed yet another common cause of boilerplate: rejoice!

 

Async injection was added in the latest 4.0.0.Beta4 release (RESTEASY-1905). Go ahead and try it out while it's fresh!

You might have recently read about the very interesting new features being developed these days in RESTEasy... great, the time has come to deliver them in a couple of releases!

Last week, RESTEasy 3.6.0.Final and 4.0.0.Beta4 have been tagged, built and published; here is a list of the most relevant additions coming with them:

  • client and server side reactive extensions [1][2]
  • parameter annotations with default names [3][4]
  • JettyClientEngine to use jetty-client with JAX-RS Client [5]
  • SPI to modify resource metadata [6]

 

Moreover, 4.0.0.Beta4, also feature the first part of the changes for the tracing system [7] and asynchronous container filters [8] support.

Both releases are available on the usual Maven repository, feel free to try them out!

3.6.0.Final is also being pulled into WildFly master, targetting inclusion in WildFly 14 release.

Enjoy!

 

[1] Asynchronous, reactive, rxjava and beyond!

[2] Chapter 39. Reactive programming support

[3] New DRY-er annotations for parameters

[4] Chapter 13. Improved @…Param annotations

[5] Chapter 50. RESTEasy Client API

[6] Chapter 18. Resources metadata configuration

[7] A brief introduction to the RESTEasy Tracing Feature

[8] New: Asynchronous container filters