RESTEasy Implementation of JAX-RS SPEC 2.0 Section 3.7

RESTEasy Implementation of JAX-RS SPEC 2.0 Section 3.7

In this article I’d like to share with you my study on JAX-RS SPEC 2.0 Section 3.7 and relative implementations in RESTEasy.

JAX-RS SPEC 2.01 shows us how to match a URI path to actual Java method in Section 3.7:

  1. Identify a set of candidate root resource classes matching the request
  2. Obtain a set of candidate resource methods for the request
  3. Identify the method that will handle the request

To implement the above logic, RESTEasy has provided several classes in package org.jboss.resteasy.registry, and here are the classes:

RESTEasy classes

From the above class diagram, we can see some relationships about these classes. Firstly, there are four Node classes, which are RootClassNode, ClassNode, RootNode and SegmentNode. Secondly, there one Expression interface and its two implementations, which are ClassExpression and MethodExpression.

From the class name, we can deduce that the Expression classes must contain information for matching the URI path. The ClassExpression class should be used to match classes, and the MethodExpression should be used to match methods.

For the Node classes, we can see RootClassNode is only connected with ClassNode, as its root attribute. From the name of the class, we can deduce that RootClassNode is the top data structure in matching process. We can verify this deduction later.

Next we should check ClassNode. We can see ClassNode is connected with ClassExpression bidirectionally. ClassNode contains targets field, which has the type ArrayList<ClassExpression>. In another direction, ClassExpression has a parent field, and the type of the field is ClassNode. So this is a One-To-Many relationship: one ClassNode instance contains many ClassExpression instances.

We need to examine ClassExpression now. ClassExpression also contains a root field, which has a type of RootNode. RootNode contains a root field too, which type is SegmentNode.

Now we can check SegmentNode. This class has a bidirectional relationship with MethodExpression, which is simliar to the relationship between ClassNode and ClassExpression.

From the above analyze, we can deduce that RootNode and SegmentNode are two abstract concepts that connects the class matching and method matching processes. The relationship between these classes is like this:

RootClassNode -> ClassNode <-> ClassExpression -> RootNode -> SegmentNode <-> MethodExpression

So the whole matching process should start from RootClassNode. We can verify our deduction by analyzing the real codes in RESTEasy. I have done this work, and I can say that the entry point of the matching process is ResourceMethodRegistry.getResourceInvoker method call. Here is the sequence diagram of the method call:

org.jboss.resteasy.core.ResourceMethodRegistry.getResourceInvoker.png

The above diagram reflects the following codes in ResourceMethodRegistry.getResourceInvoker method call:

public ResourceInvoker getResourceInvoker(HttpRequest request)
{
   try
   {
      if (widerMatching) return rootNode.match(request, 0);
      else return root.match(request, 0);
   }
   catch (RuntimeException e)
   {
      throw e;
   }
}

We can see that rootNode : RootNode and root : RootClassNode are used in two different conditions, and the widerMatching variable controls the above logic.

The widerMatching variable is defined by ResteasyDeployment class. In ResteasyDeployment class it contains a variable called widerRequestMatching:

public class ResteasyDeployment
{
   protected boolean widerRequestMatching;
}

And it is set by user controlled Configuration Switches2:

2017-03-10-switch.png

From the above screenshot of RESTEasy document, we can see the meaning of ` resteasy.wider.request.matching` switch:

Turns off the JAX-RS spec defined class-level expression filtering and instead tries to match version every method’s full path.

From the above descrption, we can see the switch is to override some SPEC defined behaviors, and the default value is false. In this article I’ll focus on analyzing the SPEC defined behavior, so I will ignore the logic:

if (widerMatching) return rootNode.match(request, 0);

And treat this as the matching logic entry point:

else return root.match(request, 0);

Now let’s check the Node classes. For all the Node classes, there are match() method inside. Let’s check these methods one by one. First is the match method of RootClassNode:

org.jboss.resteasy.core.registry.RootClassNode.match.png

From above diagram, we can see the call chain like this: RootClassNode.match() -> ClassNode.match() -> RootNode.match(). Now let’s check the match method of ClassNode:

org.jboss.resteasy.core.registry.ClassNode.match.png

From above digram, we can see the ClassNode -> RootNode matching process is complex, and it uses the ClassExpression in matching process. We’ll check the detail later. Now we should check RootNode.match():

org.jboss.resteasy.core.registry.RootNode.match.png

We can see RootNode -> SegmentNode is easy, because RootNode.match() will just call SegmentNode.match(). Let’s check the sequence diagram of SegmentNode.match():

org.jboss.resteasy.core.registry.SegmentNode.match.png

From the above diagram, we can see SegmentNode matching process is simliar to the ClassNode matching process, however the MethodExpression class is used instead of the ClassExpression.

In conclusion, the matching processes are mainly in ClassNode and SegmentNode. ClassNode deals with class matching process, and SegmentNode processes the method matching process.

Now let’s go back to SPEC document and learn the terminology defined in Section 1.5:

2017-03-10-terminology.png

From the above definitions, we need to understand the definitions of Resource class, Root resource class, Sub-resource locator and Sub-resource method.

Now let’s check Section 3.7.2, Request Matching. If you read through this section, regardless of the detail algorithms, you should catch some basic requirements: Firstly we need to have some candidate resources classes for the matching processes, secondly we need a relative regular expression to each class. This regular expression is actually the pattern of the resource classes that can be checked against the URL requests.

Besides the candidate classes, we also need to have candidate methods inside the classes, and we need to store the relative regular expressions of these methods.

To sum up the above requirements, RESTEasy has provided a Registry interface and its implementation, the ResourceMethodRegistry class, to support the storage of candidate resource classes.

In addition, we have seen there is an abstract class Expression and its two extended classes, ClassExpression and MethodExpression classes to store the relative regular expressions of the resource classes and methods.

The Expression and Registry implementations provides basic data unit for multiple Node classes to implement their match() methods. The finally goal is to get a ResourceInvoker, which contains the matched “class.method” and other useful information.

Now let’s check the Registry firstly, and then let’s see ResourceInvoker. Here is the class diagram with Registry and ResourceMethodRegistry included:

2017-03-10-registry.png

From the above diagram, we can see Registry is mainly designed to store different kinds of resource classes. The name of ResourceMethodRegistry is a little bit confusing, be cause we can see in this class are actually stored resource classes.

We can see ResourceMethodRegistry connects with RootNode and RootClassNode.

We know that RootClassNode and ClassNode are for class matching, and RootNode and SegmentNode are for method processing.

And we also know the class and method matching processes are majoyly in ClassNode.match() and SegmentNode.

We can also see MethodExpression is connected with SegmentNode, and ClassExpression has a parent of ClassNode, and has a root of RootNode.

Now let’s check the ResourceInvoker class:

2017-03-10-resource-invoker.png

We can see there are two types of ResourceInvoker, one is ResourceLocatorInvoker and the other is ResourceMethodInvoker. The ResourceMethodInvoker is for the method finally will be invoked by the request, and the ResourceLocatorInvoker is used for invoking Sub-resource locator.

There is another important class we haven’t investigated till now, the UriInfo and ResteasyUriInfo:

2017-03-11-resteasy-uri-info.png

We can see the UriInfo interface is in javax.ws.rs.core package, which means this is an interface defined by the SPEC that should be implemented by RESTEasy. The ResteasyUriInfo is an implementation of this UriInfo interface. Here is the javadoc in UriInfo that describes the purpose of the interface:

An injectable interface that provides access to application and request URI information.

Besides the meaning of encapsulating URI info, it also defines many methods that deals with URI info. Here is the javadoc for UriInfo.getMatchedURIs() method:

2017-03-13-get-matched-uri.png

And the getMatchedURIs() method in the RESTEasy implementation, ResteasyUriInfo, should implement the behavior as described in above text.

The UriInfo interface defines the methods to retrieve the URI info, but it doesn’t define how to build this info. It depends on the implementation side to build it properly. We can check the ResteasyUriInfo to see how does RESTEasy build the URI info. Firstly here is the initialize method used by the constructor of ResteasyUriInfo:

org.jboss.resteasy.spi.ResteasyUriInfo.initialize.png

We can see UriBuilder and ResteasyUriBuilder are involved in the initializing process to build the initial variables. Here are part of the codes in the method:

ResteasyUriBuilder absoluteBuilder = (ResteasyUriBuilder) UriBuilder.fromUri(absoluteUri);
absolutePath = absoluteBuilder.build();
requestURI = absoluteBuilder.replaceQuery(queryString).build();
encodedPath = PathHelper.getEncodedPathInfo(absolutePath.getRawPath(), contextPath);
baseURI = absolutePath;
path = UriBuilder.fromPath(encodedPath).build().getPath();

The UriBuilder is a SPEC interface, and ResteasyUriBuilder is its implementation. Here is the class diagram:

2017-03-13-uribuilder.png

Here is the javadoc of the UriBuilder:

2017-03-13-uribuilder-doc.png

Here is the javadoc of the javax.ws.rs.Path annotation:

2017-03-13-path.png

Here is the javadoc of the Path.value() attribute:

2017-03-13-path.png

Before going back to ResteasyUriInfo, we should check another SPEC interface called PathSegment, and its RESTEasy implementation PathSegmentImpl:

2017-03-13-pathsegment.png

Here is the javadoc of PathSegment:

2017-03-13-pathsegment-javadoc.png

In Section 5.3 Client Targets of the document, it uses the term “path segment” like this:

2017-03-13-pathsegment-spec.png

Here is the sequence diagram of PathSegmentImpl.parseSegmentsOptimization() method:

org.jboss.resteasy.specimpl.PathSegmentImpl.parseSegmentsOptimization.png

This method is used to split full URL path into path segments. The javadoc of the method is here:

org.jboss.resteasy.specimpl.PathSegmentImpl.parseSegmentsOptimization-javadoc.png

The above method is used in ResteasyUriInfo.processPath() method:

org.jboss.resteasy.spi.ResteasyUriInfo.processPath.png

As we have built a knowledge base on UriInfo and its UriBuilder, now we can come back to review the sequence diagrams of the ClassNode.match() method and the SegmentNode.match() method to see how they use the UriInfo. Here is the sequence diagram of the ClassNode.match() method we have seen previously:

org.jboss.resteasy.core.registry.ClassNode.match.png

Here is the sequence diagram of the SegmentNode.match() method:

org.jboss.resteasy.core.registry.SegmentNode.match.png

Here is the whole picture of RESTEasy implementation related with resource matching process:

2017-03-13-whole-picture.png

From the above class diagram, we can clearly see the design of RESTeasy core part. Firstly, the ResourceMethodRegistry is the center of these Node classes. The purpose of this part is to implement the matching process described in JAX-RS SPEC 2.0 Section 3.7. The matching result we finally get is a MethodExpression, which has a invoker instance. The Invoker interface will do the real job to run the Java method, so we can see ResourceMethodInvoker is the center of this part. We should remember the call sequence in ResourceMethodRegistry.getResourceInvoker we have already seen in above:

org.jboss.resteasy.core.ResourceMethodRegistry.getResourceInvoker.png

In conclusion, we can see the whole picture is divided into two halves: The bottom half has the ResourceMethodRegistry at center with some Node classes to do the URL path to MethodExpression matching work. The upper half has the ResourceMethodInvoker at center, to do the real method running job. The name of ResourceMethodRegistry is a little bit confusing, actually it doesn’t merely contain and deal with the method information, but also deal with resource classes. Maybe a more proper name should be ResourceClassAndMethodRegistry.

We didn’t check the upper half in much detail in this article, because this article is focused on the bottom half of the above diagram. In future, I’ll write the article that focus on the ResourceMethodInvoker side to check how does RESTEasy invoke the matched resource methods.

References

My Github Page: https://github.com/liweinan

Powered by Jekyll and Theme by solid

If you have any question want to ask or find bugs regarding with my blog posts, please report it here:
https://github.com/liweinan/liweinan.github.io/issues