blob: 0c4d2be45e87951bcc25663d7f32493351553778 [file] [log] [blame]
<?xml version="1.0"?>
<!--
Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary
Licenses when the conditions for such availability set forth in the
Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->
<!DOCTYPE chapter [<!ENTITY % ents SYSTEM "jersey.ent" > %ents; ]>
<chapter xmlns="http://docbook.org/ns/docbook"
version="5.0"
xml:lang="en"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd
http://www.w3.org/1999/xlink http://www.w3.org/1999/xlink.xsd"
xml:id="resource-builder">
<title>Programmatic API for Building Resources</title>
<section>
<title>Introduction</title>
<para>A standard approach of developing JAX-RS application is to implement resource classes annotated with &jaxrs.Path;
with resource methods annotated with HTTP method annotations like &jaxrs.GET; or &jaxrs.POST; and
implement needed functionality. This approach is described in the chapter
<link xlink:href="jaxrs-resources.html">JAX-RS Application, Resources and Sub-Resources</link>. Besides this standard
JAX-RS approach, Jersey offers an API for constructing resources programmatically.
</para>
<para>
Imagine a situation where a deployed JAX-RS application consists of many resource classes. These resources implement
standard HTTP methods like &jaxrs.GET;, &jaxrs.POST;, &jaxrs.DELETE;. In some situations it would be useful to add
an &jaxrs.OPTIONS; method which would return some kind of meta data about the deployed resource. Ideally, you would
want to expose the functionality as an additional feature and you want to decide at the deploy time whether you want
to add your custom &lit.http.OPTIONS; method. However, when custom &lit.http.OPTIONS; method are not
enabled you would like to be &lit.http.OPTIONS; requests handled in the standard way by JAX-RS runtime. To
achieve this you would need to modify the code to add or remove custom &lit.http.OPTIONS; methods before
deployment. Another way would be to use programmatic API to build resource according to your needs.
</para>
<para>
Another use case of programmatic resource builder API is when you build any generic RESTful interface which
depends on lot of configuration parameters or for example database structure. Your resource classes would
need to have different methods, different structure for every new application deploy. You could use more solutions
including approaches where your resource classes would be built using Java byte code manipulation.
However, this is exactly the case when you can solve the problem cleanly with the programmatic resource builder API.
Let's have a closer look at how the API can be utilized.
</para>
</section>
<section>
<title>Programmatic Hello World example</title>
<para>
Jersey Programmatic API was designed to fully support JAX-RS resource model. In other words, every resource that can
be designed using standard JAX-RS approach via annotated resource classes can be also modelled using Jersey
programmatic API. Let's try to build simple hello world resource using standard approach first and
then using the Jersey programmatic resource builder API.
</para>
<para>
The following example shows standard JAX-RS "Hello world!" resource class.
<example>
<title>A standard resource class</title>
<programlisting language="java" linenumbering="numbered">
@Path("helloworld")
public class HelloWorldResource {
@GET
@Produces("text/plain")
public String getHello() {
return "Hello World!";
}
}
</programlisting>
</example>
This is just a simple resource class with one GET method returning "Hello World!" string that will be serialized
as text/plain media type.
</para>
<para>
Now we will design this simple resource using programmatic API.
<example>
<title>A programmatic resource</title>
<programlisting language="java" linenumbering="numbered">
package org.glassfish.jersey.examples.helloworld;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.process.Inflector;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceMethod;
public static class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
final Resource.Builder resourceBuilder = Resource.builder();
resourceBuilder.path("helloworld");
final ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod("GET");
methodBuilder.produces(MediaType.TEXT_PLAIN_TYPE)
.handledBy(new Inflector&lt;ContainerRequestContext, String>() {
@Override
public String apply(ContainerRequestContext containerRequestContext) {
return "Hello World!";
}
});
final Resource resource = resourceBuilder.build();
registerResources(resource);
}
}
</programlisting>
</example>
First, focus on the content of the <literal>MyResourceConfig</literal> constructor in the example.
The Jersey programmatic resource model is constructed from <literal>Resource</literal>s that contain
<literal>ResourceMethod</literal>s. In the example, a single resource would be constructed from a
<literal>Resource</literal> containing one &lit.http.GET; resource method that returns "Hello World!".
The main entry point for building programmatic resources in Jersey is the
<emphasis><literal>Resource.Builder</literal></emphasis> class. <literal>Resource.Builder</literal> contains just
a few methods like the <literal>path</literal> method used in the example, which sets the name of the path. Another
useful method is a <literal>addMethod(String path)</literal> which adds a new method to the resource builder and
returns a resource method builder for the new method. Resource method builder contains methods which
set consumed and produced media type, define name bindings, timeout for asynchronous executions, etc. It is always
necessary to define a resource method handler (i.e. the code of the resource method that will return "Hello World!").
There are more options how a resource method can be handled. In the example the implementation of
<literal>Inflector</literal> is used. The Jersey <literal>Inflector</literal> interface defines a simple contract for
transformation of a request into a response. An inflector can either return a &jaxrs.core.Response; or directly
an entity object, the way it is shown in the example. Another option is to setup a Java method handler using
<literal>handledBy(Class&lt;?&gt; handlerClass, Method method)</literal> and pass it the chosen
<literal>java.lang.reflect.Method</literal> instance together with the enclosing class.
</para>
<para>
A resource method model construction can be explicitly completed by invoking <literal>build()</literal> on the
resource method builder. It is however not necessary to do so as the new resource method model will be built
automatically once the parent resource is built. Once a resource model is built, it is registered
into the resource config at the last line of the <literal>MyResourceConfig</literal> constructor in the example.
</para>
<section>
<title>Deployment of programmatic resources</title>
<para>
Let's now look at how a programmatic resources are deployed.
The easiest way to setup your application as well as register any programmatic resources in Jersey
is to use a Jersey &lit.jersey.server.ResourceConfig; utility class, an extension of &jaxrs.core.Application;
class.
If you deploy your Jersey application into a Servlet container you will need to provide a &jaxrs.core.Application;
subclass that will be used for configuration. You may use a <literal>web.xml</literal> where you would define a
<literal>org.glassfish.jersey.servlet.ServletContainer</literal> Servlet entry there and specify initial parameter
<literal>javax.ws.rs.Application</literal> with the class name of your JAX-RS Application that you
wish to deploy. In the example above, this application will be <literal>MyResourceConfig</literal> class. This is
the reason why <literal>MyResourceConfig</literal> extends the &lit.jersey.server.ResourceConfig; (which, if you
remember extends the <literal>javax.ws.rs.Application</literal>).
</para>
<para>
The following example shows a fragment of <literal>web.xml</literal> that can be used to deploy the
&lit.jersey.server.ResourceConfig; JAX-RS application.
<example>
<title>A programmatic resource</title>
<programlisting language="xml" linenumbering="numbered">
...
&lt;servlet&gt;
&lt;servlet-name&gt;org.glassfish.jersey.examples.helloworld.MyApplication&lt;/servlet-name&gt;
&lt;servlet-class&gt;org.glassfish.jersey.servlet.ServletContainer&lt;/servlet-class&gt;
&lt;init-param&gt;
&lt;param-name&gt;javax.ws.rs.Application&lt;/param-name&gt;
&lt;param-value&gt;org.glassfish.jersey.examples.helloworld.MyResourceConfig&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;org.glassfish.jersey.examples.helloworld.MyApplication&lt;/servlet-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
...
</programlisting>
</example>
</para>
<para>
If you use another deployment options and you have accessible instance of ResourceConfig which you use
for configuration, you can register programmatic resources directly by <literal>registerResources()</literal>
method called on the ResourceConfig. Please note that the method registerResources() replaces all the previously
registered resources.
</para>
<para>
Because Jersey programmatic API is not a standard JAX-RS feature the &lit.jersey.server.ResourceConfig; must be
used to register programmatic resources as shown above. See <link linkend="deployment">deployment chapter</link>
for more information.
</para>
</section>
</section>
<section>
<title>Additional examples</title>
<para>
<example>
<title>A programmatic resource</title>
<programlisting language="java" linenumbering="numbered">
final Resource.Builder resourceBuilder = Resource.builder(HelloWorldResource.class);
resourceBuilder.addMethod("OPTIONS")
.handledBy(new Inflector&lt;ContainerRequestContext, Response&gt;() {
@Override
public Response apply(ContainerRequestContext containerRequestContext) {
return Response.ok("This is a response to an OPTIONS method.").build();
}
});
final Resource resource = resourceBuilder.build();
</programlisting>
</example>
</para>
<para>
In the example above the <literal>Resource</literal> is built from
a <literal>HelloWorldResource</literal> resource class. The resource model built this way
contains a &lit.http.GET; method producing <literal>'text/plain'</literal> responses with "Hello World!" entity.
This is quite important as it allows you to build any Resource objects based on introspecting
existing JAX-RS resources and use builder API to enhance such these standard resources.
In the example we used already implemented <literal>HelloWorldResource</literal> resource class
and enhanced it by &lit.http.OPTIONS; method. The &lit.http.OPTIONS; method is handled by an Inflector which
returns &jaxrs.core.Response;.
</para>
<para>
The following sample shows how to define sub-resource methods (methods that contains sub-path).
</para>
<para>
<example>
<title>A programmatic resource</title>
<programlisting language="java" linenumbering="numbered">
final Resource.Builder resourceBuilder = Resource.builder("helloworld");
final Resource.Builder childResource = resourceBuilder.addChildResource("subresource");
childResource.addMethod("GET").handledBy(new GetInflector());
final Resource resource = resourceBuilder.build();
</programlisting>
</example>
</para>
<para>
Sub-resource methods are defined using so called <emphasis>child resource models</emphasis>. Child
resource models (or child resources) are programmatic resources build in the same way as any other
programmatic resource but they are registered as a child resource of a parent resource. The child resource
in the example is build directly from the parent builder using method
<literal>addChildResource(String path)</literal>.
However, there is also an option to build a child resource model separately as a standard resource and then
add it as a child resource to a selected parent resource. This means that child resource objects
can be reused to define child resources in different parent resources (you just build a single child resource
and then register it in multiple parent resources). Each child resource groups methods with the same sub-resource
path. Note that a child resource cannot have any child resources as there is nothing like sub-sub-resource
method concept in JAX-RS. For example if a sub resource method contains more path segments in its path
(e.g. "/root/sub/resource/method" where "root" is a path of the resource and "sub/resource/method" is the sub
resource method path) then parent resource will have path "root" and child resource will have path
"sub/resource/method" (so, there will not be any separate nested sub-resources for "sub", "resource" and "method").
</para>
<para>
See the javadocs of the resource builder API for more information.
<link xlink:href="&jersey.javadoc.root.uri;/org/glassfish/jersey/server/model/Resource.Builder.html">Resource.Builder</link>
</para>
</section>
<section>
<title>Model processors</title>
<para>
Jersey gives you an option to register so called <emphasis>model processor providers</emphasis>. These providers
are able to modify or enhance the application resource model during application deployment. This is an advanced
feature and will not be needed in most use cases. However, imagine you would like to add &lit.http.OPTIONS; resource
method to each deployed resource as it is described at the top of this chapter. You would want to do it for every
programmatic resource that is registered as well as for all standard JAX-RS resources.
</para>
<para>
To do that, you first need to register a model processor provider in your application, which implements
<literal>org.glassfish.jersey.server.model.ModelProcessor</literal> extension contract. An example of
a model processor implementation is shown here:
<example>
<title>A programmatic resource</title>
<programlisting language="java" linenumbering="numbered">
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.process.Inflector;
import org.glassfish.jersey.server.model.ModelProcessor;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceMethod;
import org.glassfish.jersey.server.model.ResourceModel;
@Provider
public static class MyOptionsModelProcessor implements ModelProcessor {
@Override
public ResourceModel processResourceModel(ResourceModel resourceModel, Configuration configuration) {
// we get the resource model and we want to enhance each resource by OPTIONS method
ResourceModel.Builder newResourceModelBuilder = new ResourceModel.Builder(false);
for (final Resource resource : resourceModel.getResources()) {
// for each resource in the resource model we create a new builder which is based on the old resource
final Resource.Builder resourceBuilder = Resource.builder(resource);
// we add a new OPTIONS method to each resource
// note that we should check whether the method does not yet exist to avoid failures
resourceBuilder.addMethod("OPTIONS")
.handledBy(new Inflector&lt;ContainerRequestContext, String&gt;() {
@Override
public String apply(ContainerRequestContext containerRequestContext) {
return "On this path the resource with " + resource.getResourceMethods().size()
+ " methods is deployed.";
}
}).produces(MediaType.TEXT_PLAIN)
.extended(true); // extended means: not part of core RESTful API
// we add to the model new resource which is a combination of the old resource enhanced
// by the OPTIONS method
newResourceModelBuilder.addResource(resourceBuilder.build());
}
final ResourceModel newResourceModel = newResourceModelBuilder.build();
// and we return new model
return newResourceModel;
};
@Override
public ResourceModel processSubResource(ResourceModel subResourceModel, Configuration configuration) {
// we just return the original subResourceModel which means we do not want to do any modification
return subResourceModel;
}
}
</programlisting>
</example>
The <literal>MyOptionsModelProcessor</literal> from the example is a relatively simple model processor which
iterates over all registered resources and for all of them builds a new resource that is equal to the old resource
except it is enhanced with a new &lit.http.OPTIONS; method.
</para>
<para>
Note that you only need to register such a ModelProcessor as your custom extension provider in the same way as you
would register any standard JAX-RS extension provider. During an application deployment, Jersey will look for any
registered model processor and execute them. As you can see, model processors are very powerful as they can do
any manipulations with the resource models. A model processor can even, for example, completely ignore
the old resource model and return a new custom resource model with a single "Hello world!" resource, which would
result in only the "Hello world!" resource being deployed in your application. Of course, it would not not make
much sense to implement such model processor, but the scenario demonstrates how powerful the model processor concept
is. A better, real-life use case scenario would, for example, be to always add some custom new resource to each
application that might return additional metadata about the deployed application. Or, you might want to
filter out particular resources or resource methods, which is another situation where a model processor could
be used successfully.
</para>
<para>
Also note that <literal>processSubResource(ResourceModel subResourceModel, Configuration configuration)</literal>
method is executed for each sub resource returned from the sub resource locator. The example is simplified and does
not do any manipulation but probably in such a case you would want to enhance all sub resources with
a new &lit.http.OPTIONS; method handlers too.
</para>
<para>
It is important to remember that any model processor must always return valid resource model. As you might have
already noticed, in the example above this important rule is not followed. If any of the resources in the original
resource model would already have an &lit.http.OPTIONS; method handler defined, adding another handler would cause
the application fail during the deployment in the resource model validation phase. In order to retain the consistency
of the final model, a model processor implementation would have to be more robust than what is shown in the example.
</para>
</section>
</chapter>