All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ratpack.handling.InjectionHandler Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc-1
Show newest version
/*
 * Copyright 2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ratpack.handling;

import com.google.common.reflect.TypeToken;
import ratpack.handling.internal.Extractions;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import static ratpack.util.ExceptionUtils.uncheck;

/**
 * A super class that removes the boiler plate of retrieving objects from the context registry by injecting them based on a method signature.
 * 

* Subclasses must implement exactly one method named {@code "handle"} that accepts a {@link Context} as the first parameter, * and at least one other parameter of any type. *

* The {@code handle(Context)} method of this class will delegate to the subclass handle method, supplying values for each parameter * by retrieving objects from the context and request (which are registries). * The context takes precedence over the request. * That is, if the context provides a value for the requested type it will be used regardless of whether the request also provides this type. *

* The following two handlers are functionally equivalent: *

{@code
 * import ratpack.handling.Context;
 * import ratpack.handling.Handler;
 * import ratpack.handling.InjectionHandler;
 * import ratpack.test.embed.EmbeddedApp;
 *
 * public class Example {
 *
 *   static class Thing {
 *     public final String name;
 *
 *     public Thing(String name) {
 *       this.name = name;
 *     }
 *   }
 *
 *   static class VerboseHandler implements Handler {
 *     public void handle(Context context) {
 *       Thing thing = context.get(Thing.class);
 *       context.render(thing.name);
 *     }
 *   }
 *
 *   static class SuccinctHandler extends InjectionHandler {
 *     public void handle(Context context, Thing thing) {
 *       context.render(thing.name);
 *     }
 *   }
 *
 *   public static void main(String... args) {
 *     EmbeddedApp.fromChain(chain -> chain
 *         .register(r -> r.add(new Thing("foo")))
 *         .get("verbose", new VerboseHandler())
 *         .get("succinct", new SuccinctHandler())
 *     ).test(httpClient -> {
 *       assert httpClient.getText("verbose").equals("foo");
 *       assert httpClient.getText("succinct").equals("foo");
 *     });
 *   }
 *
 * }
 * }
*

* If the parameters cannot be satisfied, a {@link ratpack.registry.NotInRegistryException} will be thrown. * The {@link java.util.Optional} type can be used to inject registry entries that may not exist. *

{@code
 * import ratpack.handling.Context;
 * import ratpack.handling.InjectionHandler;
 * import ratpack.test.embed.EmbeddedApp;
 *
 * import java.util.Optional;
 *
 * public class Example {
 *
 *   static class OptionalInjectingHandler extends InjectionHandler {
 *     public void handle(Context context, Optional string, Optional integer) {
 *       context.render(string.orElse("missing") + ":" + integer.orElse(0));
 *     }
 *   }
 *
 *   public static void main(String... args) {
 *     EmbeddedApp.fromChain(chain -> chain
 *         .register(r -> r.add("foo")) // no Integer in registry
 *         .get(new OptionalInjectingHandler())
 *     ).test(httpClient -> {
 *       assert httpClient.getText().equals("foo:0");
 *     });
 *   }
 *
 * }
 * }
*

* If there is no suitable {@code handle(Context, ...)} method, a {@link NoSuitableHandleMethodException} will be thrown at construction time. */ public abstract class InjectionHandler implements Handler { private final List> types; private final Method handleMethod; /** * Constructor. * * @throws NoSuitableHandleMethodException if this class doesn't provide a suitable handle method. */ protected InjectionHandler() throws NoSuitableHandleMethodException { Class thisClass = this.getClass(); Method handleMethod = null; for (Method method : thisClass.getDeclaredMethods()) { if (!method.getName().equals("handle")) { continue; } Class[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length < 2) { continue; } if (!parameterTypes[0].equals(Context.class)) { continue; } handleMethod = method; break; } if (handleMethod == null) { throw new NoSuitableHandleMethodException(thisClass); } try { handleMethod.setAccessible(true); } catch (SecurityException e) { throw new NoSuitableHandleMethodException(thisClass, e); } this.handleMethod = handleMethod; Type[] parameterTypes = handleMethod.getGenericParameterTypes(); this.types = new ArrayList<>(parameterTypes.length - 1); for (int i = 1; i < parameterTypes.length; ++i) { this.types.add(TypeToken.of(parameterTypes[i])); } } /** * Invokes the custom "handle" method, extracting necessary parameters from the context to satisfy the call. * * @param context The context to handle */ public final void handle(Context context) { Object[] args = new Object[types.size() + 1]; args[0] = context; Extractions.extract(types, context.getRequest().join(context), args, 1); try { handleMethod.invoke(this, args); } catch (IllegalAccessException e) { throw uncheck(e); } catch (InvocationTargetException e) { Throwable root = e.getTargetException(); throw uncheck(root); } } /** * Exception thrown if the subclass doesn't provide a valid handle method. */ public static class NoSuitableHandleMethodException extends RuntimeException { private static final long serialVersionUID = 0; private NoSuitableHandleMethodException(Class clazz) { super("No injectable handle method found for " + clazz.getName()); } public NoSuitableHandleMethodException(Class clazz, SecurityException cause) { super("Unable to make handle method accessible for " + clazz.getName(), cause); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy