| <?xml version="1.0"?> |
| <!-- |
| |
| Copyright (c) 2012, 2018 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<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>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"> |
| ... |
| <servlet> |
| <servlet-name>org.glassfish.jersey.examples.helloworld.MyApplication</servlet-name> |
| <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> |
| <init-param> |
| <param-name>javax.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 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. |
| <!--TODO javadoc 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<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 seem, model processors are very powerful as they can do |
| whatever manipulation with the resource model they like. 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> |