Installers¶
Installer is a core integration concept: every extension point has it's own installer. Installers are registered manually or detected by classpath scan.
Default installers¶
- Rest resource
- Dropwizard task
- Dropwizard managed object
- Jetty lifecycle
- Health check
- Jersey extensions
- Jersey feature
- @EagerSingleton
- Plugins support
- Http servlet
- Http filter
- Servlet context, request, session listener
Tip
In real application more installers may be available due to 3rd party bundles. Use installers report to see all available installers.
How it works¶
All registered manually extensions, classes from classpath scan and unqualified guice bindings are recognized by registered installers:
public class FeatureInstaller{ boolean matches(Class<?> type); }
Detected extensions are bound to guice context either with default binder.bind(foundClass)
or by installer itself
(default binding is required to support guice .requireExplicitBindings()
option).
After injector creation, installers register extension in dropwizard (not necessary, but most often).
For example, installation of extension instance (obtained from injector injector.getInstance(foundClass)
):
public interface InstanceInstaller<T> { void install(Environment environment, T instance); }
Jersey-related extensions are installed later, during jersey context startup:
public interface JerseyInstaller<T> { void install(AbstractBinder binder, Injector injector, Class<T> type); }
Installers are ordered.
Each extension is installed by only one installer!
If extension could be recognized by more then one installers, it will be installed only by first matching installer (according to installers order).
Writing custom installer¶
Just for example, suppose we have some scheduling framework and we want to detect extensions,
implementing ScheduledTask
class.
First of all, installer must implement FeatureInstaller
interface. Here extension detection must be implemented
public class ScheduledInstaller implements FeatureInstaller { @Override public boolean matches(final Class<?> type) { return FeatureUtils.is(type, ScheduledTask.class); } // NOTE: report() method will describe later }
Next, installer must register extension somehow. There may be different options:
BindingInstaller
allows custom guice bindings. If installer doesn't implement this interface simplebind(type)
will be called to register in guice.TypeInstaller
used for registration based on type (no instance created during installation).InstanceInstaller
used for instance registration. Instance created usinginjector.getInstance(type)
.JerseyInstaller
used for registration of bindings in HK2 context.
Note that extensions may use @LazyBinding
annotation. In general case such extensions will not be registered in guice.
In case of BindingInstaller
, special hint will be passed and installer should decide how to handle it (may throw exception as not supported).
BindingInstaller
called in time of injector creation, whereas TypeInstaller
and InstanceInstaller
are called just after injector creation.
JerseyInstaller
is called on jersey start.
Installers are not guice beans! So injections can't be used inside them. This is because installers also used during initialization phase and instantiated before injector creation.
For example, our installer would register extension instance into some scheduler framework:
public class ScheduledInstaller implements FeatureInstaller, InstanceInstaller<ScheduledTask> { ... @Override public void install(Environment environment, ScheduledTask instance) { SchedulerFramework.registerTask(instance); } }
Tip
TypeInstaller
and InstanceInstaller
could access injector with
InjectorLookup.getInjector(environment).get();
And shared state:
SharedConfigurationState.get(environment).get();
The last remaining part is reporting - we must see all installed beans in console:
public class ScheduledInstaller implements FeatureInstaller, InstanceInstaller<ScheduledTask> { private final Reporter reporter = new Reporter(ScheduledInstaller.class, "scheduled tasks ="); ... @Override public void install(Environment environment, ScheduledTask instance) { SchedulerFramework.registerTask(instance); // register for reporting reporter.line("(%s)", FeatureUtils.getInstanceClass(instance).getName()); } @Override public void report() { reporter.report(); } }
Report method will be called automatically after all extensions installation. More complex installers may require special reporter (like jersey extensins installer).
Another example, suppose CustomFeature
is a base class for our jersey extensions.
Then installer will be:
public class CustomInstaller implements FeatureInstaller, JerseyInstaller<CustomFeature> { @Override public boolean matches(final Class<?> type) { return FeatureUtils.is(type, CustomFeature.class); } @Override public void install(final AbstractBinder binder, final Class<CustomFeature> type) { JerseyBinding.bindComponent(binder, type, false, false); ... } @Override public void report() { ... } }
Jersey extensions are more usually complex due to binding aspects (especially for native jersey extensions). But, hopefully you'll never need to do it yourself.
Tip
For jersey installers see AbstractJerseyInstaller
base class, containing common utilities.
Ordering¶
In order to support ordering, installer must implement Ordered
interface.
Important
If installer doesn't implement Ordering
extensions will not be sorted,
even if extensions has @Order
annotations.
As example, see ManagedInstaller
Options¶
Installer could also use guicey options:
- it must implement
WithOptions
marker interface - or extend form
InstallerOptionsSupport
base class (implemented boilerplate)
Reporting¶
Installers report()
method will be called after it finish installation of all found extensions. Report provides
user visibility of installed extensions.
To simplify reporting use predefined Reporter class. See example usage in ManagedInstaller
INFO [2016-08-21 23:49:49,534] ru.vyarus.dropwizard.guice.module.installer.feature.ManagedInstaller: managed = (ru.vyarus.dropwizard.guice.support.feature.DummyManaged)
For complex cases, reporter may be extended to better handle installed extensions. As examples see plugin installer reporter and provider installer reporter
INFO [2016-08-21 23:49:49,535] ru.vyarus.dropwizard.guice.module.installer.feature.plugin.PluginInstaller: plugins = Set<PluginInterface> (ru.vyarus.dropwizard.guice.support.feature.DummyPlugin1) (ru.vyarus.dropwizard.guice.support.feature.DummyPlugin2) Map<DummyPluginKey, PluginInterface> ONE (ru.vyarus.dropwizard.guice.support.feature.DummyNamedPlugin1) TWO (ru.vyarus.dropwizard.guice.support.feature.DummyNamedPlugin2)
Generics¶
Guicey brings generics-resolver which you can use in installers implementation.
For example, to get extension interface parametrization:
interface Extension<V> {} class ListExtension implements Extension<List<String>> {} GenericsResolver.resolve(ListExtension.class) .type(Extension.class) .genericType("V") == List<String> // (ParameterizedType)
Guicey itself use it for:
- types resolution during configuration introspection (
ConfigTreeBuilder
) - to introspect type hierarchy and recognize all jersey extensions (
JerseyProviderInstaller
) - format type for console reporting (
ProviderReporter
) - bing jersey extensions to correct types (
JerseyBinding
)