Bring in guice 3 as a maven dependency.
Have some injection infrastructure.

The Injector used by storm tasks is a lazy-initialized static member of HBaseComponent.  When created lazy, the bindings are supplied by the (currently empty) ProductionModule.

A package-private static setter is present for tests, which is exploited by HBaseComponentFixture.  The (empty) bindings it employs are taken from an anonymous module that can bind dependencies to various fixture mocks.

Injection takes place within start(), which both HBaseSpout and HBaseBolt call in their prepare-like events.

Since Storm ultimately creates component instances, we obviously can't use Guice to construct them.  Instead we use Injector#injectMembers (which is designed for this exact case) to supply dependencies of existing instances.
Start flushing out ProductionModule.

In particular, grab the storm conf and topology context from start and bind them.

This commit would also deal with the ExecutorService, but it looks like I need to tackle the connector first.
Move ProductionModule to config package.
Add request scoping.

We use guice's built-in RequestScope, which can be used outside of an http context, as a "task" scope: the granularity of injection caching appropriate for individual tasks.

Note this is not the same as singleton scope, which we'd like to share across all tasks sharing the worker process VM.

The request/task scope has embedded in it the storm configuration and topology context that are received at start time.  The topology context truly does wish to be scoped this way as it contains a "current" task id: each task receives a different context.  The storm config could conceivably be shared but we simply don't see it before start.

These two dependencies, which guice is of course unable to create, have to be inserted somehow.  We opt to use a child injector, which binds the two deps to the passed instances.  The child injector is then used only within the context of a new request scope, provided by ServletScopes.scopeRequest.  In this way request-scoped dependencies supplied by the child injector can be correctly cached, and request scoping doesn't bleed out into the common parent injector.

To prove this is not just the mumblings of a madman, we use this structure to inject the TopologyContext into the HBaseComponent itself.
Use the honest-to-god passed type for storm config.
Inject the storm config.
Register the request scope itself, so @RequestScoped works.

Do so in a new cross-environment CommonModule.
Break out an inject package, looks like this may get a little wierd.
Rename CommonModule -> RootModule, inject HBaseComponentConfig.

The config injection is tricky.  We are simultaneously catering to two cases, expressed as the if inside the new RequestModule (which is the extraction of the anonymous module from scopeAndInject.)

When the config is passed at component construction, RequestModule binds it literally and we're done.

When it is not, RequestModule binds ConfigProvider instead, which knows how to parse a new config from json.

Either way, the config member of the base class now has a @Inject annotation, and the lazy initialization in the getter is gone.

In the default-constructor case, we already know we don't use the config until the RequestModule is active, and so the json parsing binding is live when injection takes place: the null config member gets correctly injected.

In the passed-at-construction case, the member is already populated, but gets *injectect with itself* again.  We can't avoid this (there is no "condtional" annotation in java), but it provably overwrites the member with the value it already had: a no-op.
Inline ConfigProvider.
Rename ConfigProvider -> ComponentCommonJsonConfigProvider.
Drop a manual injection method.
Provide input and output bindings.
This can be private.
Provide the flusher thread pool.
Provide the ScheduledExecutorService.

This is tricky, since we don't want to include the "heavy" thread dependency in our RequestModule (which is used in unit tests.)

So, introduce a ScheduledExecutorServiceFactory interface, bound in ProductionModule and the fixture, and use it to provide a ScheduledExecutorService of the expected size.
Whoops, wrong folder.
Add an executor (thread local) scope, extract StormScopes.

We hijack the standard RequestScoped and rename it to TaskScoped for clarity.  We then add ExecutorScoped, which is a reentrant variant of the thread local SimpleScope from the guice docs.

Being reentrant, we're free to enter/exit the executor-level scope within a component's start/stop.
unused import