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

ratpack.guice.Guice Maven / Gradle / Ivy

Go to download

Integration with Google Guice for Ratpack applications - https://code.google.com/p/google-guice/

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.guice;

import com.google.common.collect.Lists;
import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Stage;
import com.google.inject.util.Modules;
import ratpack.config.ConfigObject;
import ratpack.func.Action;
import ratpack.func.Function;
import ratpack.guice.internal.DefaultBindingsSpec;
import ratpack.guice.internal.GuiceUtil;
import ratpack.guice.internal.InjectorRegistryBacking;
import ratpack.guice.internal.RatpackBaseRegistryModule;
import ratpack.handling.Chain;
import ratpack.handling.Context;
import ratpack.handling.Handler;
import ratpack.impose.Impositions;
import ratpack.registry.Registry;
import ratpack.server.ServerConfig;

import java.util.List;
import java.util.Optional;

import static com.google.inject.Guice.createInjector;
import static ratpack.util.Exceptions.uncheck;

/**
 * Static utility methods for creating Google Guice based Ratpack infrastructure.
 * 

* Any non trivial application will require supporting objects for the handler implementations, for persistence for example. * These supporting objects can be managed by Guice and made available to handlers (either by dependency injection or registry lookup) * for increased reusability, modularity and testability. *

* The Guice integration is not part of the Ratpack core library, but is available as a separate add on. * That said, Ratpack is designed to support something like Guice as an integral piece via it's {@link Registry} abstraction. * Guice is the “official” solution. *

*

Starting a Guice Ratpack app

*

* Below is a complete example for bootstrapping a Guice based Ratpack application. *

*
{@code
 * import com.google.inject.AbstractModule;
 * import ratpack.guice.Guice;
 * import ratpack.handling.Context;
 * import ratpack.handling.Handler;
 * import ratpack.test.embed.EmbeddedApp;
 *
 * import javax.inject.Inject;
 * import javax.inject.Singleton;
 *
 * import static org.junit.Assert.*;
 *
 * public class Example {
 *
 *   static class SomeService {
 *     private final String value;
 *
 *     SomeService(String value) {
 *       this.value = value;
 *     }
 *
 *     public String getValue() {
 *       return value;
 *     }
 *   }
 *
 *   static class SomeOtherService {
 *     private final String value;
 *
 *     SomeOtherService(String value) {
 *       this.value = value;
 *     }
 *
 *     public String getValue() {
 *       return value;
 *     }
 *   }
 *
 *   public static class ServiceModule extends AbstractModule {
 *     protected void configure() {
 *       bind(SomeService.class).toInstance(new SomeService("foo"));
 *       bind(SomeOtherService.class).toInstance(new SomeOtherService("bar"));
 *       bind(InjectedHandler.class);
 *     }
 *   }
 *
 *   {@literal @}Singleton
 *   static class InjectedHandler implements Handler {
 *     private final SomeService service;
 *
 *     {@literal @}Inject
 *     public InjectedHandler(SomeService service) {
 *       this.service = service;
 *     }
 *
 *     public void handle(Context ctx) {
 *       ctx.render(service.getValue() + "-" + ctx.get(SomeOtherService.class).getValue());
 *     }
 *   }
 *
 *   public static void main(String... args) throws Exception {
 *     EmbeddedApp.of(s -> s
 *       .registry(Guice.registry(b -> b.module(ServiceModule.class)))
 *       .handlers(chain -> {
 *         // The registry in a Guice backed chain can be used to retrieve objects that were bound,
 *         // or to create objects that are bound “just-in-time”.
 *         chain.get("some/path", InjectedHandler.class);
 *       })
 *     ).test(httpClient -> {
 *       assertEquals("foo-bar", httpClient.get("some/path").getBody().getText());
 *     });
 *   }
 *
 * }
 * }
*

Accessing Guice bound objects in Handlers

*

* There are two ways to use Guice bound objects in your handler implementations. *

*

Dependency Injected Handlers

*

* The {@code handler()} methods used to create a Guice backed application take an {@link Action} that operates on a {@link Chain} instance. * This chain instance given to this action provides a Guice backed {@link Registry} via its {@link Chain#getRegistry()} method. * This registry is able to retrieve objects that were explicitly bound (i.e. defined by a module), and bind objects “just in time”. * This means that it can be used to construct dependency injected {@link Handler} implementations. *

*

* Simply pass the class of the handler implementation to create a new dependency injected instance of to this method, * then add it to the chain. *

*

* See the code above for an example of this. *

*

Accessing dependencies via context registry lookup

*

* The {@link Context} object that is given to a handler's {@link Handler#handle(Context)} * method is also a registry implementation. In a Guice backed app, Guice bound objects can be retrieved via the {@link Registry#get(Class)} method of * the context. You can retrieve any bound object via its publicly bound type. *

*

* However, this will not create “just-in-time” bindings. Only objects that were explicitly bound can be retrieved this way. *

*

Guice modules as Ratpack “plugins”.

*

* Add on Ratpack functionality is typically provided via Guice modules. * For example, the Jackson integration for JSON serialisation * is provided by the {@code ratpack-jackson} add-on which ships a Guice module. * To use its functionality simply register the module it provides with the {@link BindingsSpec} used to bootstrap the application. *

*

Groovy Applications

*

* The Ratpack Groovy add-on provides application modes that automatically incorporate Guice (namely the “Ratpack Script” mode). * The module registration process is simpler and more convenient in this mode, and there are additional options for obtaining * Guice bound objects. *

*

* See the Groovy add-on's documentation for more details. *

*/ public abstract class Guice { private Guice() { } /** * Creates a Ratpack {@link Registry} backed by the given {@link Injector} that will NOT create objects via “just-in-time” binding. * * @param injector The injector to back the registry * @return A registry that wraps the injector */ public static Registry registry(Injector injector) { return Registry.backedBy(new InjectorRegistryBacking(injector)); } public static Function registry(Action bindings) { return baseRegistry -> registry(bindings, baseRegistry, newInjectorFactory(baseRegistry.get(ServerConfig.class))); } public static Function registry(Injector parentInjector, Action bindings) { return baseRegistry -> registry(bindings, baseRegistry, childInjectorFactory(parentInjector)); } private static Registry registry(Action bindings, Registry baseRegistry, Function injectorFactory) throws Exception { Injector injector = buildInjector(baseRegistry, bindings, injectorFactory); return registry(injector); } public static Function newInjectorFactory(final ServerConfig serverConfig) { final Stage stage = serverConfig.isDevelopment() ? Stage.DEVELOPMENT : Stage.PRODUCTION; return from -> from == null ? createInjector(stage) : createInjector(stage, from); } private static Function childInjectorFactory(final Injector parent) { return from -> from == null ? parent.createChildInjector() : parent.createChildInjector(from); } static Injector buildInjector(Registry baseRegistry, Action bindingsAction, Function injectorFactory) throws Exception { List> binderActions = Lists.newLinkedList(); List modules = Lists.newLinkedList(); ServerConfig serverConfig = baseRegistry.get(ServerConfig.class); BindingsSpec bindings = new DefaultBindingsSpec(serverConfig, binderActions, modules); modules.add(new RatpackBaseRegistryModule(baseRegistry)); modules.add(new ConfigModule(serverConfig.getRequiredConfig())); try { bindingsAction.execute(bindings); } catch (Exception e) { throw uncheck(e); } modules.add(new AdHocModule(binderActions)); Optional bindingsImposition = Impositions.current().get(BindingsImposition.class); if (bindingsImposition.isPresent()) { BindingsImposition imposition = bindingsImposition.get(); List> imposedBinderActions = Lists.newLinkedList(); List imposedModules = Lists.newLinkedList(); BindingsSpec imposedBindings = new DefaultBindingsSpec(serverConfig, imposedBinderActions, imposedModules); imposition.getBindings().execute(imposedBindings); imposedModules.add(new AdHocModule(imposedBinderActions)); Module imposedModule = imposedModules.stream().reduce((acc, next) -> Modules.override(acc).with(next)).get(); modules.add(imposedModule); } Module masterModule = modules.stream().reduce((acc, next) -> Modules.override(acc).with(next)).get(); return injectorFactory.apply(masterModule); } private static class AdHocModule implements Module { private final List> binderActions; public AdHocModule(List> binderActions) { this.binderActions = binderActions; } @Override public void configure(Binder binder) { for (Action binderAction : binderActions) { binderAction.toConsumer().accept(binder); } } } private static class ConfigModule implements Module { private final Iterable> config; public ConfigModule(Iterable> config) { this.config = config; } @Override public void configure(Binder binder) { for (ConfigObject configObject : config) { bind(binder, configObject); } } private static void bind(Binder binder, ConfigObject configObject) { binder.bind(GuiceUtil.toTypeLiteral(configObject.getTypeToken())).toInstance(configObject.getObject()); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy