| <?xml version="1.0" encoding="UTF-8"?> | |
| <!-- | |
| Copyright (c) 2010, 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="declarative-linking"> | |
| <title>Declarative Hyperlinking</title> | |
| <para> | |
| <link xlink:href="http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven">RESTful APIs must be | |
| hypertext-driven</link>. JAX-RS currently offers &jaxrs.core.UriBuilder; to simplify URI | |
| creation but Jersey adds an additional annotation-based alternative that is described here. | |
| </para> | |
| <important> | |
| <para> | |
| This API is currently under development and experimental so it is subject to change at any time. | |
| </para> | |
| </important> | |
| <section> | |
| <title>Dependency</title> | |
| <para> | |
| To use Declarative Linking you need to add &lit.jersey-declarative-linking; module to your &lit.pom.xml; file: | |
| <programlisting language="xml" linenumbering="unnumbered"><dependency> | |
| <groupId>org.glassfish.jersey.ext</groupId> | |
| <artifactId>jersey-declarative-linking</artifactId> | |
| <version>&version;</version> | |
| </dependency></programlisting> | |
| Additionally you will need to add the following dependencies, if you are not deploying | |
| into a container that is already including them: | |
| <programlisting language="xml" linenumbering="unnumbered"><dependency> | |
| <groupId>jakarta.el</groupId> | |
| <artifactId>jakarta.el-api</artifactId> | |
| <version>&jakarta.el.version;</version> | |
| </dependency></programlisting> | |
| <programlisting language="xml" linenumbering="unnumbered"><dependency> | |
| <groupId>org.glassfish.web</groupId> | |
| <artifactId>jakarta.el</artifactId> | |
| <version>&jakarta.el-impl.version;</version> | |
| </dependency></programlisting> | |
| If you're not using Maven make sure to have all needed dependencies (see &jersey.ext.declarative-linking.deps.link;) on | |
| the classpath. | |
| </para> | |
| </section> | |
| <section> | |
| <title>Links in Representations</title> | |
| <para> | |
| Links are added to representations using the <literal>@InjectLink</literal> annotation on | |
| entity class fields. The Jersey runtime is responsible for injecting the appropriate URI | |
| into the field prior to serialization by a message body writer. E.g. consider the | |
| following resource and entity classes: | |
| </para> | |
| <programlisting language="java">@Path("widgets") | |
| public class WidgetsResource { | |
| @GET | |
| public Widgets get() { | |
| return new Widgets(); | |
| } | |
| } | |
| public class Widgets { | |
| @InjectLink(resource=WidgetsResource.class) | |
| URI u; | |
| }</programlisting> | |
| <para> | |
| After a call to <literal>WidgetsResource#get</literal>, the Jersey runtime will inject the value | |
| <literal>"/context/widgets"</literal> | |
| <footnote> | |
| <para> | |
| Where <literal>/context</literal> is the application deployment context. | |
| </para> | |
| </footnote> | |
| into the returned <literal>Widgets</literal> instance. If an absolute URI is | |
| desired instead of an absolute path then the annotation can be changed to | |
| <literal>@InjectLink(resource=WidgetsResource.class, style=ABSOLUTE)</literal>. | |
| </para> | |
| <para> | |
| The above usage works nicely when there's already a URI template on a class that you | |
| want to reuse. If there's no such template available then you can use a literal value | |
| instead of a reference. E.g. the following is equivalent to the earlier example: | |
| <literal>@InjectLink(value="widgets", style=ABSOLUTE)</literal>. | |
| </para> | |
| </section> | |
| <section> | |
| <title>List of Link Injection</title> | |
| <para> | |
| You can inject multiple links into an array or a List collection type. E.g.: | |
| <programlisting language="java">@InjectLinks({@InjectLink(resource=WidgetsResource.class, rel = "self")}) | |
| List<Link> links</programlisting> | |
| The field doesn't need to be initialized. However, if it already contains a collection with manually created links, | |
| then it will merge those with the generated links into a new collection which then replaces the field value. | |
| </para> | |
| </section> | |
| <section> | |
| <title>Links from Resources</title> | |
| <para> | |
| As an alternative to defining the links in the entity class, they can also be defined in the resource classes by | |
| annotating the resource methods with <literal>@ProvideLink</literal>. This has the benefit, that the target | |
| method is already known and doesn't need to be referenced. Other than that it has the same parameters and behaviors | |
| as <literal>@InjectLink</literal>. The entity classes need to have a field annotated with | |
| <literal>@InjectLinks</literal>, which can be empty. | |
| </para> | |
| <para> | |
| The <literal>@ProvideLink</literal> annotation can be repeated to add links to different entities using different | |
| options. Entities are defined via the <literal>value</literal> property. If the entities are similar in structure they | |
| can also be declared as an array. <literal>@ProvideLink</literal> also works with class hierarchies, e.g., contributions | |
| defined for a superclass will also be injected into the derived classes (interfaces are not supported). | |
| <programlisting language="java">@ProvideLink(value = Order.class,rel = "self", | |
| bindings = @Binding(name = "orderId", value = "${instance.id}")) | |
| @ProvideLink(value = PaymentConfirmation.class, rel = "order", | |
| bindings = @Binding(name = "orderId", value = "${instance.orderId}")) | |
| @GET | |
| @Path("/{orderId}") | |
| public Response get(@PathParam("orderId") String orderId)</programlisting> | |
| </para> | |
| </section> | |
| <section> | |
| <title>Binding Template Parameters</title> | |
| <para> | |
| Referenced or literal templates may contain parameters. Two forms of parameters are | |
| supported: | |
| <itemizedlist> | |
| <listitem><para> | |
| URI template parameters, e.g. <literal>widgets/{id}</literal> where <literal>{id}</literal> represents | |
| a variable part of the URI. | |
| </para></listitem> | |
| <listitem><para> | |
| EL expressions, e.g. <literal>widgets/${instance.id}</literal> where <literal>${instance.id}</literal> | |
| is an EL expression. | |
| </para></listitem> | |
| </itemizedlist> | |
| </para> | |
| <para> | |
| Parameter values can be extracted from three implicit beans: | |
| <variablelist> | |
| <varlistentry> | |
| <term> | |
| <literal>instance</literal> | |
| </term> | |
| <listitem> | |
| <para>Represents the instance of the class that contains the annotated field.</para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term> | |
| <literal>entity</literal> | |
| </term> | |
| <listitem> | |
| <para>Represents the entity class instance returned by the resource method.</para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term> | |
| <literal>resource</literal> | |
| </term> | |
| <listitem> | |
| <para>Represents the resource class instance that returned the entity.</para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| </para> | |
| <para> | |
| By default URI template parameter values are extracted from the implicit <literal>instance</literal> bean, | |
| i.e. the following two annotations are equivalent: | |
| <programlisting language="java">@InjectLink("widgets/{id}") | |
| @InjectLink("widgets/${instance.id}")</programlisting> | |
| </para> | |
| <para> | |
| The source for URI template parameter values can be changed using the <literal>@Binding</literal> | |
| annotation, E.g. the following three annotations are equivalent: | |
| <programlisting language="java">@InjectLink(value="widgets/{id}", bindings={ | |
| @Binding(name="id" value="${resource.id}"} | |
| ) | |
| @InjectLink(value="widgets/{value}", bindings={ | |
| @Binding("${resource.id}")}) | |
| @InjectLink("widgets/${resource.id}")</programlisting> | |
| </para> | |
| </section> | |
| <section> | |
| <title>Conditional Link Injection</title> | |
| <para> | |
| Link value injection can be made conditional by use of the <literal>condition</literal> property. | |
| The value of this property is a boolean EL expression and a link will only be injected if the condition | |
| expression evaluates to true. E.g.: | |
| <programlisting language="java">@InjectLink(value="widgets/${instance.id}/offers", | |
| condition="${instance.offers}") | |
| URI offers;</programlisting> | |
| </para> | |
| <para> | |
| In the above, a URI will only be injected into the <literal>offers</literal> field if the | |
| <literal>offers</literal> property of the instance is <literal>true</literal>. | |
| </para> | |
| </section> | |
| <section> | |
| <title>Link Headers</title> | |
| <para> | |
| <link xlink:href="http://tools.ietf.org/html/rfc5988#section-5">HTTP Link headers</link> can also be added | |
| to responses using annotations. Instead of annotating the fields of an entity class with | |
| <literal>@InjectLink</literal>, you instead annotate the entity class itself with | |
| <literal>@InjectLinks</literal>. E.g.: | |
| <programlisting language="java">@InjectLinks( | |
| @InjectLink(value="widgets/${resource.nextId}", rel="next") | |
| )</programlisting> | |
| </para> | |
| <para> | |
| The above would insert a HTTP Link header into any response whose entity was thus annotated. | |
| The <literal>@InjectLink</literal> annotation contains properties that map to the parameters | |
| of the HTTP Link header. The above specifies the link relation as <literal>next</literal>. | |
| All properties of the <literal>@InjectLink</literal> annotation may be used as described above. | |
| </para> | |
| <para> | |
| Multiple link headers can be added by use of the <literal>@InjectLinks</literal> annotation | |
| which can contain multiple <literal>@InjectLink</literal> annotations. | |
| </para> | |
| <para> | |
| Resource links via <literal>@ProvideLink</literal> are currently not supported for link headers. | |
| </para> | |
| </section> | |
| <section> | |
| <title>Prevent Recursive Injection</title> | |
| <para> | |
| By default, Jersey will try to recursively find all <literal>@InjectionLink</literal> annotations in | |
| the members of your object unless this member is annotated with <literal>@XmlTransient</literal>. | |
| But in some cases, you might want to control which member will be introspected regardless of the | |
| <literal>@XmlTransient</literal> annotation. | |
| You can prevent Jersey to look into an object by adding <literal>@InjectLinkNoFollow</literal> to a field. | |
| <programlisting language="java">@InjectLinkNoFollow | |
| Context context;</programlisting> | |
| </para> | |
| </section> | |
| <section> | |
| <title>Meta-annotation support</title> | |
| <para> | |
| The <literal>@ProvideLink</literal> annotation can be used as a meta-annotation, i.e., annotating your own annotation. | |
| This enables you to create custom annotations to reuse <literal>@ProvideLink</literal> configurations instead of | |
| copy pasting them on each method. There is a special marker class <literal>ProvideLink.InheritFromAnnotation</literal> | |
| that can be used in place of the actual entity class, this indicates that the <literal>Class<?> value()</literal> | |
| from the custom annotation should be used instead. | |
| Repeated annotations are currently unsupported for this feature. Also the <literal>Class<?> value()</literal> | |
| method must return a single class and not an array of classes. | |
| </para> | |
| <para> | |
| Here is an example (getter/setter omitted for brevity) of how a meta annotation can be used. | |
| The example app uses a <literal>Page</literal> class as a base class for all entities that contain paged data. | |
| <programlisting language="java">public class Page { | |
| private int number; | |
| private int size; | |
| private boolean isPreviousPageAvailable; | |
| private boolean isNextPageAvailable; | |
| @InjectLinks | |
| private List<Link> links; | |
| }</programlisting> | |
| Instead of duplicating the <literal>@ProvideLink</literal> annotations for the next and previous links on every | |
| method, we create the following <literal>@PageLinks</literal> annotation. | |
| <programlisting language="java">@ProvideLink(value = ProvideLink.InheritFromAnnotation.class, rel = "next", | |
| bindings = { | |
| @Binding(name = "page", value = "${instance.number + 1}"), | |
| @Binding(name = "size", value = "${instance.size}"), | |
| }, condition = "${instance.nextPageAvailable}") | |
| @ProvideLink(value = ProvideLink.InheritFromAnnotation.class, rel = "prev", | |
| bindings = { | |
| @Binding(name = "page", value = "${instance.number - 1}"), | |
| @Binding(name = "size", value = "${instance.size}"), | |
| }, condition = "${instance.previousPageAvailable}") | |
| @Target({ElementType.METHOD}) | |
| @Retention(RetentionPolicy.RUNTIME) | |
| @Documented | |
| public @interface PageLinks { | |
| Class<?> value(); | |
| }</programlisting> | |
| The annotation can the then be used on the resource methods with the actual entity class as <literal>value</literal>. | |
| <programlisting language="java">@PageLinks(OrderPage.class) | |
| @GET | |
| public Response list(@QueryParam("page") @DefaultValue("0") int page, | |
| @QueryParam("size") @DefaultValue("20") int size)</programlisting> | |
| The entity just extends from <literal>Page</literal> and declares its content. It is necessary to use distinct classes | |
| instead of just a generic page to have a unique target for <literal>@ProvideLink</literal>, otherwise every method | |
| annotated with <literal>@ProvideLink(value=Page.class)</literal> would contribute to the entity. | |
| <programlisting language="java">public class OrderPage extends Page { | |
| private List<Order> orders; | |
| }</programlisting> | |
| </para> | |
| </section> | |
| <section> | |
| <title>Configure and register</title> | |
| <para> | |
| In order to add the Declarative Linking feature register &jersey.linking.DeclarativeLinkingFeature; | |
| <example> | |
| <title>Creating JAX-RS application with Declarative Linking feature enabled.</title> | |
| <programlisting language="java">// Create JAX-RS application. | |
| final Application application = new ResourceConfig() | |
| .packages("org.glassfish.jersey.examples.linking") | |
| .register(DeclarativeLinkingFeature.class);</programlisting> | |
| </example> | |
| </para> | |
| </section> | |
| </chapter> |