Skip to content

Concepts overview

Section briefly describes new concepts introduced by guicey and explains why it was done that way. For usage instruction and basic examples see getting-started section.

Injector creation phase

Dropwizard declares two phases:

  • initialization (App.initialize method) - when dropwizard app must be configured
  • run (App.run method) - when configuration is available and extensions could be registered in environment

If we create injector in initialization phase then we will not have access to Configuration and Environment in guice modules, but configuration could be required, especially for 3rd party modules, which does not support lazy configuration.

Guicey creates injector at run phase to allow using configuration (and environment) in guice modules.

Many people ask why not just use HK2 instead of guice as it's already provided. Unfortunately, it's hard to use it in the same elegant way as we can use guice. HK2 context is launched too late (after dropwizard run phase). For example, it is impossible to use HK2 to instantiate dropwizard managed object because managed must be registered before HK2 context starts.

Guice module

But gucie modules are still registered in initialization phase (in GuiceBundle), when configuration is not available. To overcome this, guicey provides marker interfaces like ConfigurationAwareModule to set configuration object into module before injector creation.

Tip

If possible, use DropwizardAwareModule as base module class to avoid boilerplate

public class SampleModule extends DropwizardAwareModule<Configuration> {

    @Override
    protected void configure() {
        configuration() // access configuration        
        environment() // access environment
        bootstrap()  // access dropwizard bootstrap
        configuratonTree() // configuration as tree of values
        confuguration(Class) // unique sub configuration
        configuration(String) // configuration value by yaml path
        configurations(Class) // sub configuration objects by type (including subtypes)
        options() // access guicey options
    }
}

Note

This works only for top level modules registered in root bundle or guicey bundles.

Bindings

Guicey always apply it's own module (GuiceBootstrapModule) to injector. This module adds all extra bindings (for dropwizard and jersey objects).

  • io.dropwizard.setup.Bootstrap
  • io.dropwizard.Configuration
  • io.dropwizard.setup.Environment

Bindings below are not immediately available as HK2 context starts after guice:

  • javax.ws.rs.core.Application
  • javax.ws.rs.ext.Providers
  • org.glassfish.hk2.api.ServiceLocator
  • org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider

Request-scoped bindings:

  • javax.ws.rs.core.UriInfo
  • javax.ws.rs.container.ResourceInfo
  • javax.ws.rs.core.HttpHeaders
  • javax.ws.rs.core.SecurityContext
  • javax.ws.rs.core.Request
  • org.glassfish.jersey.server.ContainerRequest
  • org.glassfish.jersey.server.internal.process.AsyncContext
  • javax.servlet.http.HttpServletRequest
  • javax.servlet.http.HttpServletResponse

Request scoped objects must be used through provider:

@Inject Provider<HttpServletRequest> requestProvider;

Configuration bindings

It is quite common need to access configuration value by path, instead of using entire configuration object. Often this removes boilerplate when one option is used in multiple places, compare:

@Inject MyConfiguration config
...

// in each usage
config.getSub().getFoo()

and

@Inject @Config("sub.foo") String foo;

// and use direct value in all places

Also, often you have some unique configuration sub object, e.g.

public class MyConfig extends Configuration {
    @JsonProperty
    AuthConfig auth;
}

It may be more convenient to bind it directly, instead of full configuration:

@Inject @Config AuthConfig auth;

Note

This is especially helpful for extensions: if extension can be sure that it's configuration object would be used only once in your configuration - it could inject it directly, without dealing with your root configuration class.

You can see all available bindings from your configuration using configuration report.

Extensions

In raw dropwizard you register extensions like:

public class App extends Application<Configuration> {

    ...

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        // resource registration
        environment.jersey().register(MyResource.class);
        // if extension requires configuration:
        environment.lifecycle().manage(new MyManaged(configuration.getSomething()));
    }    
}

Very easy, just need to remember where it should be registered.

If we want to use guice for extensions management, then all extensions must be obtained from guice injector, but it's a boilerplate.

Instead, guicey introduce Extension-Installer concept: you create extension (e.g. MyResource) and Installer knows how to install it. Guicey only need to know extension class.

If you use classpath scanning, then you don't need to do anything: guicey will recognize extensions and install them.

For example, ResourceInstaller will:

  1. recognize MyResource class as rest resource by @Path annotation
  2. gets instance from injector (injector.getInstance(MyResource.class)) and performs registration environment.jersey().register(guiceManagedInstance)

The same way, MangedInstaller recognize MyManaged as managed extension (by implemented interface) and installs guice managed instance (and so all other extensions).

Summary

With classpath scan you don't need to do anything to install extension and in manual mode you only need to specify extension classes.

Tip

Most installer implementations are very simple, so you can easily understand how it works (all core installers are declared in core installers bundle).

Jersey extensions

It is important to note that jersey extensions (resources and other) are forced to be singletons (if explicit scope annotation is not set).

This force you to always use all request scoped objects through Provider. But, from the other side, this avoids a jvm garbage from creating them for each request and makes everything a bit faster (no extra DI work required for each request).

If you think that developer comfort worth more then small performance gain, then:

  • You can use explicit scope annotations to change singleton scope (@RequestScoped, @Prototype)
  • Switch off forced singletons (.option(InstallerOptions.ForceSingletonForJerseyExtensions, false))
  • Delegate some extensions or resources management to HK2 using @HK2Managed
  • Use HK2 by default for jersey extensions

Warning

Guice AOP will not work on extensions managed by HK2

For more guice-HK2 integration specifics read lifecycle section.

Custom extensions

Installers are not limited to dropwizard only features: you can use custom installers to write any 3rd party integration. The only condition is extension classes must have some unique identity (usually annotation).

For example, guice has .asEeagerSingleton() configuration option, which declares service as singleton and grants it's initialization in time of injector creation (even in Development scope). Yes, in dropwizard case it's better to use Managed extensions instead, but sometimes eager singletons are convenient for quick hacking something (besides, it's just an example). To simplify eager singleton integrations we create new annotation @EagerSingleton and installer (EagerSingletonInstaller) which recognize annotation and register extensions as eager singletons. Now we need to just annotate class and (assuming classpath scan) it will be registered automatically.

Tip

Installers are also discovered and registered during classpath scan.

Another example is PluginInstaller which allows you to declare plugins (e.g. implementing some interface) and inject all of them at once (as Set<PluginInterface>).

Extensions project provides special installer to register events in guava eventBus: EventBusInstaller check class methods and if any method is annotated with @Subscribe - register extension as event bus listener.

Not existing, but possible extension for scheduled tasks: we can create @Schedule annotation and write installer to automatically register such classes in scheduler framework.

Summary

In essence, custom installers could help you avoid many boilerplate operations.

Warning

Each extension could be installed only by one installer. It will be the first installer which recognize the extension in class (according to installers order).

Core installers override

It is also possible to replace any core installer (e.g. to change it's behaviour) - you just need to disable core installer and install a replacement:

bootstrap.addBundle(GuiceBundle.builder()
                ...
                .disableInstaller(ManagedInstaller.class)
                .installers(MyCustomManagedInstaller.class)       
                .build());

Tip

You can disable all installers enabled by default with:

.noDefaultInstallers()
In this case, you will have to register some installers manually (even if it would be just a few of guicey's own installers).

Bundles

Dropwizard use bundles for re-usable logic. Bundle also support both dropwizard phases (in essence, it's the same as application)

public interface ConfiguredBundle<T> {
    void initialize(Bootstrap<?> bootstrap);    
    void run(T configuration, Environment environment) throws Exception;
}

The concept is great, but, in context of guice, dropwizard bundle did not allow us to register guice modules (and, of course, guicey installers and extensions). So there is no way to elegantly re-use dropwizard bundles mechanism.

Guicey introduce it's own bundles:

public interface GuiceyBundle {
    void initialize(GuiceyBootstrap bootstrap);
}

GuiceyBootstrap provides almost all the same methods as main GuiceBundle, allowing you to register installers, extensions, modules and other bundles. Also, it provides access to dropwizard objects (bootstrap, configuration, environment)

Warning

Guicey bundles are called under dropwizard run phase (method name may be confusing, comparing to dropwizard bundles). That means you can't register dropwizard bundles inside guicey bundle (too late for that). Run phase used to provide all dropwizard objects (environment and configuration) and only one method used because guicey bundles usually don't need to register dropwizard bundles)

Tip

For special cases, there is a way to mix guicey bundle with dropwizard: class must just implement both bundle interfaces.

Bundles usage difference

In dropwizard bundles are helpful not just for extracting re-usable extensions, but for separation of application logic.

In guicey, you don't need to write registration code and, with auto scan enabled, don't need to configure much at all. This makes guicy bundles mostly usable for 3rd party integrations (or core modules extraction for large projects), where you can't (and should not) rely on class path scan and must declare all installers and extensions manually.

Guicey itself comes with multiple bundles:

Lookup

Dropwizard force you to always register bundles manually, and this is good for clarity. But, sometimes, it is desirable to apply bundles under some condition. For example, you want custom installers to be registered when 3rd party integrations jar is available or you may want to enable some bundles under integration tests only.

Guicey provides such ability:

ServiceLookup based lookup is ideal for 3rd party integrations. For example, suppose you have scheduler framework integration which provides installers for custom annotation (@Job). All you need to do to apply extension is to put integration jar into classpath - installers will be implicitly registered.

Important

ServiceLookup requires you to prepare extra files so you prepare your bundle for using that way. It does not mean guicey loads all bundles in classpath!

Note

ServiceLoader and property based lookups are always enabled, but you can switch them off if required with .disableBundleLookup() bundle option.

Disabling

As you have seen in overriding installers example, you can disable installers. But actually you can disable almost anything: installers, extensions, guice modules and guicey bundles.

Mostly this is required for testing (to be able to exclude entire application parts and, maybe, replace with something else).

But, in some cases, you may want to change behaviour of 3rd party module: as an example above (with replaced installer), you can replace extension, guice module (registered by some bundle), or even prevent entire bundle (with transitive bundles) installation.

Suppose you have some 3rd party bundle:

public class XBundle implements GuiceyBundle {
    public void initialize(GuiceyBootstrap bootstrap) {
        bootstrap
            .extensions(...)
            .modules(new XModule(), new XAddonModule());
    }    
}

And, for some reason, you don't need XAddonModule guice module, then you can simply disable it:

bootstrap.addBundle(GuiceBundle.builder()
            .bundles(new XBundle())
            .disableModules(XAddonModule.class)
            .build())

Tip

You can even perform mass disables by predicate. For example, disable all installations (extensions, bundles etc) from package:

.disable(Disables.inPackage("some.package.here"))

Guice bindings overrides

Guice allows you to override any binding using Modules.override(). With it you can override any service in context. Guicey provides direct shortcut for using this feature.

Mostly, this is handful for tests, but could be used to override some service, registered by 3rd party module (probably registered by some bundle).

Options

Dropwizard configuration covers most configuration cases, except development specific cases. For example, some trigger may be useful during application testing and be useless on production (so no reason to put it in configuration). Other example is an ability of low level tuning for 3rd party bundles.

Options are developer configurations: either required only for development or triggers set during development and not intended to be changed later.

  • Options are declared with enum (each enum represents options group) with value type declaration to grant safety.
  • Option could be set only in main bundle (in your application class)
  • You can access options anywhere: guice module, guicey bundle, and in any guice service by injecting special service
  • Options report is included into diagnostic report so you can see all option values.

For example, guicey use two option groups: GuiceyOptions and InstallersOptions. GuiceyOptions used for storing main bundle configurations like packages to scan, injector stage, HK2 bridge usage etc. That means you have access to all these application configurations from anywhere in your code (for example, you can know if classpath scan is enabled or not in 3rd party bundle).

Another good example is InstallersOptions.JerseyExtensionsManagedByGuice which changes the way jersey extensions are handled: with guice or with HK2. This is developer time decision and must be selected by developer (because it affects behaviour a lot). Thanks to generic mechanism other bundles could know what was chosen.

HK2 usage is highly dependent on HK2-guice-bridge presence and with option we can verify it:

Preconditions.checkState(options.get(GuiceyOptions.UseHkBridge), 
                            "HK2 guice bridge is required!")

Dynamic options

You can bind option to system property in order to introduce special "hidden" application flag:

GuiceBuilder.builder()
    ...
    .options(new OptionsMapper()
                    .prop("myprop", Myoptions.SomeOption)
                    .map())
    .build()                

Now you can run application with -Dmyprop=value and this value will be mapped to option (accessible everywhere in the application).

Note

There are built-in basic value conversions from string, but in complex cases you can do manual conversion

 .prop("myprop", Myoptions.SomeOption, val -> convertVal(val)) 

Note

You can map options from sys properties, environment variables or strings (obtained somewhere else). You can even allow mass binding to allow external definition of any option .props("prefix"). See options lookup doc.

You don't need to remember all this

All guicey features could be revealed from main bundle methods. So you don't need to remember everything - just look methods.

There is a special group of print[Something] methods, which are intended to help you understand internal state (and help with debugging).

As you have seen, real life configuration could be quite complex because you may have many extensions, observed with classpath scan, bundles, bundles installing other bundles, many gucie modules. Also, some bundles may disable extensions, installers, guice modules (and some modules could even override bindings).

During startup guicey tracks all performed configurations and you can even access this information at runtime using @Inject GuiceyConfigurationInfo info.

Out of the box, guicey could print all this into console, you just need to add .printDiagnosticInfo():

bootstrap.addBundle(GuiceBundle.builder()
            .printDiagnosticInfo()
            .build())

You can see additional logs in console like:

    GUICEY started in 453.3 ms
    │   
    ├── [0,88%] CLASSPATH scanned in 4.282 ms
    │   ├── scanned 5 classes
    │   └── recognized 4 classes (80% of scanned)
    │   
    ├── [4,2%] COMMANDS processed in 19.10 ms
    │   └── registered 2 commands
    │   
    ├── [6,4%] BUNDLES processed in 29.72 ms
    │   ├── 2 resolved in 8.149 ms
    │   └── 6 processed
    ...


    APPLICATION
    ├── extension  FooBundleResource            (r.v.d.g.d.s.bundle)       
    ├── module     FooModule                    (r.v.d.g.d.s.features)     
    ├── module     GuiceBootstrapModule           (r.v.d.guice.module)       
    ├── -disable   LifeCycleInstaller           (r.v.d.g.m.i.feature)      
    │   
    ├── Foo2Bundle                   (r.v.d.g.d.s.bundle)       
    │   ├── extension  FooBundleResource            (r.v.d.g.d.s.bundle)       *IGNORED
    │   ├── module     FooBundleModule              (r.v.d.g.d.s.bundle)       
    │   ├── -disable   ManagedInstaller             (r.v.d.g.m.i.feature)      
    ...

And other logs giving you inside look on configuration.

Other helpful reports:

  • printAvailableInstallers() - see all registered installers to know what features you can use
  • printConfigurationBindings() - show available configuration bindings (by path and unique objects)
  • printCustomConfigurationBindings() - the same as above, but without dropwizard configuration (shorter report)
  • printLifecyclePhases() - indicate running steps in logs
  • printLifecyclePhasesDetailed() - very detailed startup reports

Not mentioned