Interface HandlerDecorator


  • public interface HandlerDecorator
    Decorates a handler, effectively wrapping it.

    Handler decorators can be used to contribute to the handler chain from the server registry. It is often used by libraries/extensions to participate in request handling in order to set up infrastructure for downstream handlers.

    The prepend(Handler) method is a convenient way to create a decorator that simply prepends a given handler to the application handlers.

    
     import ratpack.core.handling.Handler;
     import ratpack.core.handling.HandlerDecorator;
     import ratpack.core.handling.Context;
     import ratpack.func.Types;
     import ratpack.test.embed.EmbeddedApp;
     import static org.junit.jupiter.api.Assertions.*;
    
     import java.util.LinkedList;
     import java.util.Arrays;
    
     public class Example {
       public static class AddListToRequestRegistry implements Handler {
         public void handle(Context context) {
           context.getRequest().add(Types.listOf(String.class), new LinkedList<String>());
           context.next();
         }
       }
    
       public static class AddStringToList implements Handler {
         private final String string;
         public AddStringToList(String string) { this.string = string; }
         public void handle(Context ctx) {
           ctx.getRequest().get(Types.listOf(String.class)).add(string);
           ctx.next();
         }
       }
    
       public static void main(String... args) throws Exception {
         EmbeddedApp.of(s -> s
           .registryOf(r -> r
             .add(HandlerDecorator.prepend(new AddListToRequestRegistry()))
             .add(HandlerDecorator.prepend(new AddStringToList("foo")))
             .add(HandlerDecorator.prepend(new AddStringToList("bar")))
           )
           .handler(r ->
             ctx -> ctx.render(ctx.getRequest().get(Types.listOf(String.class)).toString())
           )
         ).test(httpClient ->
           assertEquals(Arrays.asList("foo", "bar").toString(), httpClient.getText())
         );
       }
     }
     

    Handler decorators can't do anything that handlers can't do. In the above example, the same result could have been achieved by just using the handler implementations. Decorators are usually more useful when using something like Guice and where it is desired for a module to contribute handlers.

    
     import ratpack.core.handling.Handler;
     import ratpack.core.handling.HandlerDecorator;
     import ratpack.core.handling.Context;
     import ratpack.exec.registry.Registry;
     import ratpack.test.embed.EmbeddedApp;
    
     import ratpack.guice.Guice;
     import com.google.inject.AbstractModule;
     import com.google.inject.Inject;
     import com.google.inject.multibindings.Multibinder;
    
     import static org.junit.jupiter.api.Assertions.*;
    
     import java.util.LinkedList;
     import java.util.Arrays;
    
     public class Example {
    
       public static class MaintenanceWindow {
         public boolean active = true;
       }
    
       public static class HandlerDecoratorImpl implements HandlerDecorator {
         private final MaintenanceWindow maintenanceWindow;
    
         @Inject
         public HandlerDecoratorImpl(MaintenanceWindow maintenanceWindow) {
           this.maintenanceWindow = maintenanceWindow;
         }
    
         public Handler decorate(Registry serverRegistry, Handler rest) {
           return ctx -> {
             if (maintenanceWindow.active) {
               ctx.render("down for maintenance!");
             } else {
               ctx.insert(rest);
             }
           };
         }
       }
    
       public static class MaintenanceWindowModule extends AbstractModule {
         protected void configure() {
           bind(MaintenanceWindow.class);
           // Important to use a multi binding to allow other modules to also contribute handlers
           Multibinder.newSetBinder(binder(), HandlerDecorator.class).addBinding().to(HandlerDecoratorImpl.class);
         }
       }
    
       public static void main(String... args) throws Exception {
         EmbeddedApp.of(s -> s
           .registry(Guice.registry(b -> b
             .module(MaintenanceWindowModule.class)
           ))
           .handler(r ->
             ctx -> ctx.render("ok!")
           )
         ).test(httpClient -> {
           assertEquals("down for maintenance!", httpClient.getText());
         });
       }
     }
     

    Handler decorators are invoked in the reverse order in which they are defined in the server registry. That is, the reverse of the order that they were added to the server registry. This means that earlier decorators decorate the handlers returned by later decorators. Said another way, the handler returned by the first decorator returned from the server registry will be the first handler to handle requests.

    • Method Detail

      • noop

        static HandlerDecorator noop()
        A handler decorator implementation that does not decorate the handler. That is, it just returns the given handler.
        Returns:
        a handler decorator that does not decorate the handler
      • decorate

        Handler decorate​(Registry serverRegistry,
                         Handler rest)
                  throws Exception
        Creates a new handler that decorates the application handlers, given as the rest argument.

        As the rest argument encapsulates the application handlers, the returned handler should generally delegate to it (via Context.insert(Handler...)).

        Parameters:
        serverRegistry - the server registry
        rest - the rest of the handlers of the application
        Returns:
        a new handler
        Throws:
        Exception - any
      • prepend

        static HandlerDecorator prepend​(Handler handler)
        A factory for decorator impls that effectively inserts the given handler before the “rest” of the handlers.

        The given handler should call Context.next() to delegate to the rest of the handlers.

        Parameters:
        handler - the handler to prependHandlers
        Returns:
        a handler decorator implementation
      • prepend

        static HandlerDecorator prepend​(Class<? extends Handler> handler)
        A factory for decorator impls that effectively inserts the given handler before the “rest” of the handlers.

        This is useful for cases when a handler is installed from the registry outside of the usual RatpackServerSpec.handlers(Action)} method, e.g. from an external module.

        The given handler should call Context.next() to delegate to the rest of the handlers.

        Parameters:
        handler - a class defining the handler
        Returns:
        a handler decorator implementation
        Since:
        1.5
      • prependHandlers

        static HandlerDecorator prependHandlers​(Action<? super Chain> handlers)
        A factory for decorator impls that effectively inserts the given chain before the “rest” of the handlers. The chain may define handlers that render results or call Context.next() to delegate to the rest of the handlers.
        Parameters:
        handlers - an action defining a handler chain
        Returns:
        a handler decorator implementation
        Since:
        1.5
      • prependHandlers

        static HandlerDecorator prependHandlers​(Class<? extends Action<? super Chain>> handlers)
        A factory for decorator impls that effectively inserts the given chain before the “rest” of the handlers. The chain may define handlers that render results or call Context.next() to delegate to the rest of the handlers.

        This is useful for cases when a group of handlers is installed from the registry outside of the usual RatpackServerSpec.handlers(Action)} method, e.g. from an external module.

        Parameters:
        handlers - a class defining an action for a handler chain
        Returns:
        a handler decorator implementation
        Since:
        1.5