| <?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 jakarta.ws.rs.container.ContainerRequestContext; | |
| import jakarta.ws.rs.core.Application; | |
| import jakarta.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<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<?> 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>jakarta.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>jakarta.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"> | |
| ... | |
| <servlet> | |
| <servlet-name>org.glassfish.jersey.examples.helloworld.MyApplication</servlet-name> | |
| <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> | |
| <init-param> | |
| <param-name>jakarta.ws.rs.Application</param-name> | |
| <param-value>org.glassfish.jersey.examples.helloworld.MyResourceConfig</param-value> | |
| </init-param> | |
| <load-on-startup>1</load-on-startup> | |
| </servlet> | |
| <servlet-mapping> | |
| <servlet-name>org.glassfish.jersey.examples.helloworld.MyApplication</servlet-name> | |
| <url-pattern>/*</url-pattern> | |
| </servlet-mapping> | |
| ... | |
| </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<ContainerRequestContext, Response>() { | |
| @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 whatever 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 jakarta.ws.rs.GET; | |
| import jakarta.ws.rs.Path; | |
| import jakarta.ws.rs.Produces; | |
| import jakarta.ws.rs.container.ContainerRequestContext; | |
| import jakarta.ws.rs.core.Application; | |
| import jakarta.ws.rs.core.Configuration; | |
| import jakarta.ws.rs.core.MediaType; | |
| import jakarta.ws.rs.core.Response; | |
| import jakarta.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<ContainerRequestContext, String>() { | |
| @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> |