RESTEasy Application Support
RESTEasy Application Support
In this article I’d like to give you a brief introduction on RESTEasy Application Support.
Application is an JAX-RS spec defined feature that support users to register restful resources into containers. Here are the descriptions to the Application in section 2.3.2 Servlet
of the jsr339-jaxrs-2.0-final-spec
:
Please note Application is used with Servlet container, that means, not all the containers need to follow this Application workflow to register resources. For example, resteasy-netty4
container doesn’t need Application to work. Here is an example to use resteasy-netty4
:
ResteasyDeployment deployment = new ResteasyDeployment();
netty = new NettyJaxrsServer();
netty.setDeployment(deployment);
netty.setPort(port);
netty.setRootResourcePath("");
netty.setSecurityDomain(null);
netty.start();
deployment.getRegistry().addPerRequestResource(BasicResource.class);
As the example shown above, we can see that the BasicResource
is added into Registry
directly, and Registry
is contained in ResteasyDeployment
. We can see the whole process doesn’t involve Application. This makes us understanding two things: The first one is that Application is just a way to provide root resource path and resources to the container; The second thing is that RESTEasy can directly accept resource classes and store it in its classes.
Now let’s check how does RESTEasy deals with the Application. There is a createApplication()
method defined in ResteasyDeployment
:
public static Application createApplication(String applicationClass, Dispatcher dispatcher, ResteasyProviderFactory providerFactory) {
Class<?> clazz = null;
try {
clazz = Thread.currentThread().getContextClassLoader().loadClass(applicationClass);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Application app = (Application) providerFactory.createProviderInstance(clazz);
dispatcher.getDefaultContextObjects().put(Application.class, app);
ResteasyProviderFactory.pushContext(Application.class, app);
PropertyInjector propertyInjector = providerFactory.getInjectorFactory().createPropertyInjector(clazz, providerFactory);
propertyInjector.inject(app);
return app;
}
From the above code, we can see the method will create an Application class instance from the String applicationClass
defined by user, so it will be the user extended Application class. The following is the sequence diagram of above code:
From the above analysis, we can see the instance of Application will be stored into multiple classes, such as Dispatcher
and ResteasyProviderFactory
. Here are the two places that are using createApplication()
method:
As the screenshot shown above, one is in ServletContainerDispatcher.init()
, and the other one is ResteasyDeployment.start()
. Here is the usage of createApplication()
method in ServletContainerDispatcher.init()
:
We can see there is a processApplication()
method after createApplication()
method. Here is the sequence diagram of the ServletContainerDispatcher.processApplication()
method:
We can see the processApplication()
method will fetch the resources from Application instance and register them into various classes. This is the usage of Application in RESTEasy. Next let’s see the createApplication()
method in ResteasyDeployment.start()
:
Please note RESTEasy doesn’t process the @ApplicationPath
annotation by itself, it’s the container’s responsibility to deal with it. WadlUndertowConnector
is a good example:
package org.jboss.resteasy.wadl;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.ServletInfo;
import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import static io.undertow.servlet.Servlets.servlet;
/**
* Created by weli on 7/26/16.
*/
public class WadlUndertowConnector {
public UndertowJaxrsServer deployToServer(UndertowJaxrsServer server, Class<? extends Application> application) {
ApplicationPath appPath = application.getAnnotation(ApplicationPath.class);
String path = "/";
if (appPath != null) path = appPath.value();
return deployToServer(server, application, path);
}
public UndertowJaxrsServer deployToServer(UndertowJaxrsServer server, Class<? extends Application> application, String contextPath) {
ResteasyDeployment deployment = new ResteasyDeployment();
deployment.setApplicationClass(application.getName());
DeploymentInfo di = server.undertowDeployment(deployment);
ServletInfo resteasyWadlServlet = servlet("ResteasyWadlServlet", ResteasyWadlServlet.class)
.setAsyncSupported(false)
.setLoadOnStartup(1)
.addMapping("/application.xml");
di.addServlet(resteasyWadlServlet);
di.setClassLoader(application.getClassLoader());
di.setContextPath(contextPath);
di.setDeploymentName("Resteasy" + contextPath);
return server.deploy(di);
}
}
From the above code, we can see the ApplicationPath
is extracted from Application instance:
ApplicationPath appPath = application.getAnnotation(ApplicationPath.class);
And then it’s injected into the Undertow1 container:
di.setContextPath(contextPath);
From the above study, we know that Application can help users to register resources into Servlet container in a cleaner way, nevertheless RESTEasy doesn’t rely on Application to register classes. In addition, some RESTEasy non-servlet containers such as Netty4 and Sun JDK HTTP Server doesn’t need Application to register resources.
For Servlet Container, RESTEasy provides helper classes such as ServletContainerDispatcher
to help the container to prepare RESTEasy classes more conveniently. Here are the relative classes:
RESTEasy Undertow server is a servlet container, and UndertowJaxrsServer
uses ServletContainerDispatcher
like this:
ServletInfo resteasyServlet = servlet("ResteasyServlet", HttpServlet30Dispatcher.class)
.setAsyncSupported(true)
.setLoadOnStartup(1)
.addMapping(mapping);
I won’t dig into more details in this article, but you should get a good understanding on RESTEasy Application support now.