ratpack.guice.Guice Maven / Gradle / Ivy
Show all versions of ratpack-guice Show documentation
/*
* 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.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Stage;
import ratpack.guice.internal.DefaultGuiceBackedHandlerFactory;
import ratpack.guice.internal.InjectorBackedRegistry;
import ratpack.guice.internal.JustInTimeInjectorRegistry;
import ratpack.handling.Chain;
import ratpack.handling.Handler;
import ratpack.launch.LaunchConfig;
import ratpack.registry.Registry;
import ratpack.func.Action;
import ratpack.func.Transformer;
import static com.google.inject.Guice.createInjector;
import static ratpack.handling.Handlers.chain;
/**
* 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
*
* The user entry point for Ratpack applications is the {@link ratpack.launch.HandlerFactory} implementation
* that is given to the {@link ratpack.launch.LaunchConfigBuilder#build(ratpack.launch.HandlerFactory)} method.
* This implementation can use one of the {@code handler(...) } static methods.
*
* Below is a complete example for bootstrapping a Guice based Ratpack application.
*
*
* import ratpack.handling.*;
* import ratpack.registry.*;
* import ratpack.guice.*;
* import ratpack.launch.*;
* import ratpack.func.*;
* import com.google.inject.*;
* import javax.inject.*;
*
* // Some service to be dependency injected
* class SomeService {}
*
* // A Guice module that provides the service
* class ServiceModule extends AbstractModule {
* protected void configure() {
* bind(SomeService.class);
* }
* }
*
* // An action that registers the module with the registry, making it part of the application
* class ModuleBootstrap implements Action<ModuleRegistry> {
* public void execute(ModuleRegistry modules) {
* modules.register(new ServiceModule());
* }
* }
*
* // A handler implementation that is dependency injected
* {@literal @}Singleton
* class InjectedHandler implements Handler {
* private final SomeService service;
*
* {@literal @}Inject
* public InjectedHandler(SomeService service) {
* this.service = service;
* }
*
* public void handle(Context exchange) {
* // …
* }
* }
*
* // A chain configurer that adds a dependency injected handler to the chain
* class HandlersBootstrap implements Action<Chain> {
* public void execute(Chain 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”.
* Registry<Object> registry = chain.getRegistry();
* Handler injectedHandler = registry.get(InjectedHandler.class);
*
* // Add the handler into the chain
* chain.get("some/path", injectedHandler);
* }
* }
*
* // A HandlerFactory implementation used to bootstrap the application
* class MyHandlerFactory implements HandlerFactory {
* public Handler create(LaunchConfig launchConfig) {
* return Guice.handler(launchConfig, new ModuleBootstrap(), new HandlersBootstrap());
* }
* }
*
* // Building a launch config with our handler factory
* LaunchConfig launchConfig = LaunchConfigBuilder.baseDir(new File("appRoot")).build(new MyHandlerFactory());
*
* 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 ratpack.handling.Context} object that is given to a handler's {@link Handler#handle(ratpack.handling.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 ModuleRegistry} 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 handler that can be used as the entry point for a Guice backed Ratpack app.
*
* Modules are processed eagerly. Before this method returns, the modules will be used to create a single {@link com.google.inject.Injector} instance.
* This injector is available to the given handler via service lookup.
*
* @param launchConfig The launch config of the server
* @param moduleConfigurer The configurer of the {@link ModuleRegistry} to back the created handler
* @param chainConfigurer The configurer that builds the handler chain
* @return A handler that makes all Guice bound objects available to the handlers added to the {@link Chain} given to {@code chainConfigurer}.
*/
public static Handler handler(LaunchConfig launchConfig, Action super ModuleRegistry> moduleConfigurer, final Action super Chain> chainConfigurer) throws Exception {
return new DefaultGuiceBackedHandlerFactory(launchConfig).create(moduleConfigurer, newInjectorFactory(launchConfig), new InjectorHandlerTransformer(launchConfig, chainConfigurer));
}
/**
* Creates a handler that can be used as the entry point for a Guice backed Ratpack app.
*
* This is a lower level version of {@link #handler(ratpack.launch.LaunchConfig, ratpack.func.Action, ratpack.func.Action)}
* that supports a custom final {@link Handler} creation strategy.
*
* @param launchConfig The launch config of the server
* @param moduleConfigurer The configurer of the {@link ModuleRegistry} to back the created handler
* @param injectorTransformer Takes the final {@link Injector} instance that was created from the modules and returns the {@link Handler} to use
* @return A handler that makes all Guice bound objects available to the handlers added to the {@link Chain} given to {@code chainConfigurer}.
*/
public static Handler handler(LaunchConfig launchConfig, Action super ModuleRegistry> moduleConfigurer, Transformer super Injector, ? extends Handler> injectorTransformer) throws Exception {
return new DefaultGuiceBackedHandlerFactory(launchConfig).create(moduleConfigurer, newInjectorFactory(launchConfig), injectorTransformer);
}
/**
* Creates a handler that can be used as the entry point for a Guice backed Ratpack app.
*
* Similar to {@link #handler(ratpack.launch.LaunchConfig, ratpack.func.Action, ratpack.func.Action)},
* but provides the opportunity to use a parent injector when creating the injector to use for the application.
* See Guice documentation for the semantics of parent/child injectors.
*
* Typically only required when making a Ratpack application a component of a larger application.
*
* @param launchConfig The launch config of the server
* @param parentInjector The injector to use as a parent of the injector to be created
* @param moduleConfigurer The configurer of the {@link ModuleRegistry} to back the created handler
* @param chainConfigurer The configurer that builds the handler chain
* @return A handler that makes all Guice bound objects available to the handlers added to the {@link Chain} given to {@code chainConfigurer}.
*/
public static Handler handler(LaunchConfig launchConfig, Injector parentInjector, Action super ModuleRegistry> moduleConfigurer, final Action super Chain> chainConfigurer) throws Exception {
return new DefaultGuiceBackedHandlerFactory(launchConfig).create(moduleConfigurer, childInjectorFactory(parentInjector), new InjectorHandlerTransformer(launchConfig, chainConfigurer));
}
/**
* Creates a handler that can be used as the entry point for a Guice backed Ratpack app.
*
* This is a lower level version of {@link #handler(ratpack.launch.LaunchConfig, com.google.inject.Injector, ratpack.func.Action, ratpack.func.Action)}
* that supports a custom final {@link Handler} creation strategy.
*
* @param launchConfig The launch config of the server
* @param parentInjector The injector to use as a parent of the injector to be created
* @param moduleConfigurer The configurer of the {@link ModuleRegistry} to back the created handler
* @param injectorTransformer Takes the final {@link Injector} instance that was created from the modules and returns the {@link Handler} to use
* @return A handler that makes all Guice bound objects available to the handlers added to the {@link Chain} given to {@code chainConfigurer}.
*/
public static Handler handler(LaunchConfig launchConfig, Injector parentInjector, Action super ModuleRegistry> moduleConfigurer, Transformer super Injector, ? extends Handler> injectorTransformer) throws Exception {
return new DefaultGuiceBackedHandlerFactory(launchConfig).create(moduleConfigurer, childInjectorFactory(parentInjector), injectorTransformer);
}
/**
* Creates a Ratpack {@link Registry} backed by the given {@link Injector} that will create objects via “just-in-time” binding.
*
* Typically used in conjuction with the {@link ratpack.handling.Handlers#chain(LaunchConfig, ratpack.registry.Registry, ratpack.func.Action)}
* method.
*
* @param injector The injector to back the registry
* @return A registry that wraps the injector
*/
public static Registry justInTimeRegistry(Injector injector) {
return new JustInTimeInjectorRegistry(injector);
}
/**
* 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 new InjectorBackedRegistry(injector);
}
/**
* Creates a transformer that can build an injector from a module.
*
* The module given to the {@code transform()} method may be {@code null}.
*
* @return a transformer that can build an injector from a module
*/
public static Transformer newInjectorFactory(final LaunchConfig launchConfig) {
final Stage stage = launchConfig.isReloadable() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
return new Transformer() {
@Override
public Injector transform(Module from) {
return from == null ? createInjector(stage) : createInjector(stage, from);
}
};
}
/**
* Creates a transformer that can build an injector from a module, as a child of the given parent.
*
* The module given to the {@code transform()} method may be {@code null}.
*
* @return a transformer that can build an injector from a module, as a child of the given parent.
*/
public static Transformer childInjectorFactory(final Injector parent) {
return new Transformer() {
@Override
public Injector transform(Module from) {
return from == null ? parent.createChildInjector() : parent.createChildInjector(from);
}
};
}
private static class InjectorHandlerTransformer implements Transformer {
private final LaunchConfig launchConfig;
private final Action super Chain> action;
public InjectorHandlerTransformer(LaunchConfig launchConfig, Action super Chain> action) {
this.launchConfig = launchConfig;
this.action = action;
}
public Handler transform(Injector injector) throws Exception {
return chain(launchConfig, justInTimeRegistry(injector), action);
}
}
}