Skip to main content

REST Server — Programmatic Fluent Builder

As of 10.0, every REST resource class can be configured entirely in code — without placing a @Rest annotation on it — through the RestBuilder<SELF> / AbstractRestBuilder<R, SELF> CRTP builder API.

Overview​

The RestBuilder<SELF> interface is a public, fluent, self-typed configuration surface covering every @Rest annotation member. AbstractRestBuilder<R, SELF> provides the single shared implementation: each fluent setter writes into a backing RestAnnotation.Builder (an override bag) and returns self() with the concrete builder's type, so bespoke setters on a builder subclass chain with true covariant returns without any overriding.

At RestContext construction time, the override bag is serialized into a synthetic highest-priority @Rest annotation and prepended to the resource's @Rest chain. Builder-supplied values take precedence over annotation values declared directly on the resource class.

Constructor trio​

The framework autodetects which constructor the resource class exposes and uses the most-specific one available:

ConstructorWhen used
MyResource(RestBuilder<?> builder)Preferred. The builder is passed in; the resource can read builder state or store the builder for later use.
MyResource()Fallback no-arg constructor. Builder state is stashed on the instance after construction via an internal setBuilder(AbstractRestBuilder) call.
(none — RestResource.Builder.build() or static builder() factory)For flavor builders (RestServlet.Builder, RestResource.Builder, RestMixin.Builder) that construct the resource type via AbstractRestBuilder.createResource().

AbstractRestBuilder.createResource() uses ClassInfo reflection to probe for the (RestBuilder<?>) constructor first, then the no-arg constructor, and throws IllegalStateException if neither is present.

builder(Class<T>) factory autodetection​

The recommended idiomatic pattern is to expose a static builder() factory on the resource class:

@Rest(path="/orders")
public class OrderResource extends BasicRestResource {

// Constructor-injection form
public OrderResource(RestBuilder<?> b) { /* read builder if needed */ }

public static RestResource.Builder<OrderResource> builder() {
return RestResource.builder(OrderResource.class);
}
}

RestResource.builder(Class<R>) (and the equivalent RestServlet.builder(Class<R>) / RestMixin.builder(Class<R>)) constructs the appropriate flavor builder parameterized on R, so:

OrderResource r = OrderResource.builder()
.path("/api/v2/orders") // overrides @Rest(path)
.allowedHeaderParams("X-Foo") // overrides @Rest(allowedHeaderParams)
.build();

Builder values win over the class's @Rest annotation for every member that has been set on the builder; unset members fall through to the annotation as normal.

Building a resource entirely in code (without @Rest)​

The most common use case for the programmatic builder is test fixtures and Spring @Bean methods where you want to configure or override a resource without editing its annotation:

@Configuration
public class AppConfig {

@Bean
Servlet rootServlet() {
return OrderResource.builder()
.path("/api/v2/orders")
.maxInput("5MB")
.build();
}
}

It is also possible to author a resource with no @Rest annotation at all, supplying everything through the builder:

public class MinimalResource extends BasicRestResource {

public MinimalResource(RestBuilder<?> b) {}

public static RestObject.Builder<MinimalResource> builder() {
return RestObject.builder(MinimalResource.class)
.path("/minimal")
.title("Minimal programmatic resource");
}

@RestGet("/ping")
public String ping() { return "pong"; }
}

CRTP self-type​

AbstractRestBuilder<R, SELF> implements RestBuilder<SELF> where SELF is the concrete builder type (e.g. RestServlet.Builder). Every fluent setter returns self() cast to SELF, so builder subclasses adding bespoke setters can declare them as returning SELF without overriding the base setters:

public class MyServletBuilder extends RestServlet.Builder<MyServletBuilder> {

private String extra;

public MyServletBuilder extra(String v) { this.extra = v; return self(); }
}

MyServlet s = new MyServletBuilder()
.path("/foo")
.extra("bar") // covariant return — no override needed
.build();

See also​