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

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 simple bind(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 using injector.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)