Chapter 13. Declarative Hyperlinking
Prev |  | Next

Links: Table of Contents | Single HTML

# Chapter 13. Declarative Hyperlinking

RESTful APIs must be hypertext-driven. JAX-RS currently offers UriBuilder to simplify URI creation but Jersey adds an additional annotation-based alternative that is described here.

### Important

This API is currently under development and experimental so it is subject to change at any time.

## 13.1. Dependency

To use Declarative Linking you need to add `jersey-declarative-linking` module to your `pom.xml` file:

    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-declarative-linking</artifactId>
        <version>4.0.0-M1</version>
    </dependency>

Additionally you will need to add the following dependencies, if you are not deploying into a container that is already including them:

    <dependency>
        <groupId>jakarta.el</groupId>
        <artifactId>jakarta.el-api</artifactId>
        <version>6.0.0-RC1</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>jakarta.el</artifactId>
        <version>5.0.0-M1</version>
    </dependency>

If you're not using Maven make sure to have all needed dependencies (see jersey-declarative-linking) on the classpath.

## 13.2. Links in Representations

Links are added to representations using the `@InjectLink` 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:

    @Path("widgets")
    public class WidgetsResource {
        @GET
        public Widgets get() {
            return new Widgets();
        }
    }

    public class Widgets {
        @InjectLink(resource=WidgetsResource.class)
        URI u;
    }

After a call to `WidgetsResource#get`, the Jersey runtime will inject the value `"/context/widgets"` [1] into the returned `Widgets` instance. If an absolute URI is desired instead of an absolute path then the annotation can be changed to `@InjectLink(resource=WidgetsResource.class, style=ABSOLUTE)`.

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: `@InjectLink(value="widgets", style=ABSOLUTE)`.

## 13.3. List of Link Injection

You can inject multiple links into an array or a List collection type. E.g.:

    @InjectLinks({@InjectLink(resource=WidgetsResource.class, rel = "self")})
    List<Link> links

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.

## 13.4. Links from Resources

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 `@ProvideLink`. 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 `@InjectLink`. The entity classes need to have a field annotated with `@InjectLinks`, which can be empty.

The `@ProvideLink` annotation can be repeated to add links to different entities using different options. Entities are defined via the `value` property. If the entities are similar in structure they can also be declared as an array. `@ProvideLink` also works with class hierarchies, e.g., contributions defined for a superclass will also be injected into the derived classes (interfaces are not supported).

    @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)

## 13.5. Binding Template Parameters

Referenced or literal templates may contain parameters. Two forms of parameters are supported:

- URI template parameters, e.g. `widgets/{id}` where `{id}` represents a variable part of the URI.

- EL expressions, e.g. `widgets/${instance.id}` where `${instance.id}` is an EL expression.

Parameter values can be extracted from three implicit beans:

`instance`

  Represents the instance of the class that contains the annotated field.

`entity`

  Represents the entity class instance returned by the resource method.

`resource`

  Represents the resource class instance that returned the entity.

By default URI template parameter values are extracted from the implicit `instance` bean, i.e. the following two annotations are equivalent:

    @InjectLink("widgets/{id}")

    @InjectLink("widgets/${instance.id}")

The source for URI template parameter values can be changed using the `@Binding` annotation, E.g. the following three annotations are equivalent:

    @InjectLink(value="widgets/{id}", bindings={
        @Binding(name="id" value="${resource.id}"}
    )

    @InjectLink(value="widgets/{value}", bindings={
        @Binding("${resource.id}")})
    @InjectLink("widgets/${resource.id}")

## 13.6. Conditional Link Injection

Link value injection can be made conditional by use of the `condition` 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.:

    @InjectLink(value="widgets/${instance.id}/offers",
        condition="${instance.offers}")
    URI offers;

In the above, a URI will only be injected into the `offers` field if the `offers` property of the instance is `true`.

## 13.7. Link Headers

HTTP Link headers can also be added to responses using annotations. Instead of annotating the fields of an entity class with `@InjectLink`, you instead annotate the entity class itself with `@InjectLinks`. E.g.:

    @InjectLinks(
        @InjectLink(value="widgets/${resource.nextId}", rel="next")
    )

The above would insert a HTTP Link header into any response whose entity was thus annotated. The `@InjectLink` annotation contains properties that map to the parameters of the HTTP Link header. The above specifies the link relation as `next`. All properties of the `@InjectLink` annotation may be used as described above.

Multiple link headers can be added by use of the `@InjectLinks` annotation which can contain multiple `@InjectLink` annotations.

Resource links via `@ProvideLink` are currently not supported for link headers.

## 13.8. Prevent Recursive Injection

By default, Jersey will try to recursively find all `@InjectionLink` annotations in the members of your object unless this member is annotated with `@XmlTransient`. But in some cases, you might want to control which member will be introspected regardless of the `@XmlTransient` annotation. You can prevent Jersey to look into an object by adding `@InjectLinkNoFollow` to a field.

    @InjectLinkNoFollow
    Context context;

## 13.9. Meta-annotation support

The `@ProvideLink` annotation can be used as a meta-annotation, i.e., annotating your own annotation. This enables you to create custom annotations to reuse `@ProvideLink` configurations instead of copy pasting them on each method. There is a special marker class `ProvideLink.InheritFromAnnotation` that can be used in place of the actual entity class, this indicates that the `Class<?> value()` from the custom annotation should be used instead. Repeated annotations are currently unsupported for this feature. Also the `Class<?> value()` method must return a single class and not an array of classes.

Here is an example (getter/setter omitted for brevity) of how a meta annotation can be used. The example app uses a `Page` class as a base class for all entities that contain paged data.

    public class Page {
        private int number;

        private int size;

        private boolean isPreviousPageAvailable;

        private boolean isNextPageAvailable;

        @InjectLinks
        private List<Link> links;
    }

Instead of duplicating the `@ProvideLink` annotations for the next and previous links on every method, we create the following `@PageLinks` annotation.

    @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();
    }

The annotation can the then be used on the resource methods with the actual entity class as `value`.

    @PageLinks(OrderPage.class)
    @GET
    public Response list(@QueryParam("page") @DefaultValue("0") int page,
                         @QueryParam("size") @DefaultValue("20") int size)

The entity just extends from `Page` and declares its content. It is necessary to use distinct classes instead of just a generic page to have a unique target for `@ProvideLink`, otherwise every method annotated with `@ProvideLink(value=Page.class)` would contribute to the entity.

    public class OrderPage extends Page {
        private List<Order> orders;
    }

## 13.10. Configure and register

In order to add the Declarative Linking feature register DeclarativeLinkingFeature

Example 13.1. Creating JAX-RS application with Declarative Linking feature enabled.

    // Create JAX-RS application.
    final Application application = new ResourceConfig()
            .packages("org.glassfish.jersey.examples.linking")
            .register(DeclarativeLinkingFeature.class);

[1] Where `/context` is the application deployment context.

Prev |  | Next
Chapter 12. URIs and Links | Home | Chapter 14. Programmatic API for Building Resources