I started trying to implement a pre destroy hook using the injectable
type hooks. The idea being that if a type has a method annotated with
@PreDestroy (or whatever the close hook is - e.g. its
DisposableBean.destroy() in Spring), we register a handler into some
'close handler' object in the same scope (assuming
application/request/session scopes etc).
The problem is, there's no way to really know for sure what scope the
type/injectee is in. Well we could filter all objects which are
annotated with @Singleton and add the hook for those and repeat for
each scope annotation. The problem is that would not fire for objects
bound to a scope using the module code. e.g.
bind(Foo.class).in(Singleton.class);
As Foo might not be annotated with Singleton.class in this case
I wonder if the InjectionListener should also expose the Binding as
well (which has its Scoping?). Scoping is currently not on the Binding
interface but is on BindingImpl (and I don't mind a hacky cast now and
again :). Maybe we should add getScopeAnnotation() to Binding?
Another problem I have is that given the injectee I've got to look up
the 'close handler' object for the same scope as the injectee so I can
register the handler into the right scope. A scope annotation isn't a
binding annotation, so I can't do
getProvider(Key.get(CloseHandler.class, someScopeAnnotation)).
However if I can find the scope annotation for the injectee, I can
then try to get all the bindings for the "close handler" type - then
find the one with the same scope annotation - then use that one.
You'd hope scopes are fairly small in number; so I could create a sub
class "close handler" for each scope and annotate it with the scope
annotation, then look that up; then I just need to keep a map of scope
annotations to the sub type of the close handler. It'd work around
some limitations; though its a tad icky in that its now hard coded to
the number of scope annotations out there.
I just wondered if folks had any other cunning ideas to implement this
in a less sucky way?
Exposing the binding to the injectee / InjectionListener seems
necessary really; to avoid only implementing types with a scope
annotation.
Or another approach is to just apply some kind of patch like I
submitted before - so there's basically some kinda way to iterate
through all the objects in a scope; afterall each scope stores the
objects in its scope - being able to iterate through those does seem
way simpler and more extensible.
Thoughts?
--
James
-------
http://macstrac.blogspot.com/
Open Source Integration
http://fusesource.com/
OK - I thought that creating an instance to pass into the
On 03/04/2009, Bob Lee <craz...@crazybob.org> wrote:
> I actually have a plan for this! :-)
>
> We can't expose a binding to the injection listener. The listener only knows
> about injectable types which are orthogonal to bindings.
InjectionListener would have used the binding to know if it should
even bother creating an instance. I'll take your word for it though
:-)
Would this approach only work for singleton? What about other scopes
>
> My plan is to make singleton scope overridable in Guice 2 (using
> Modules.override() internally). Then, you can bind your own singleton scope
> implementation that has a destroy() method and looks for @PreDestroy
> methods. This will work for all singletons, not just those annotated with
> @Singleton.
like request/session/conversation/test or other wacky scopes folks
might use?
Would it be so bad for a Scope to have an iterate() method so tools &
> Ideally, we'd have a callback on the Scope interface so we could tell an
> impl at initialization time about all of the bindings in that scope (and you
> could validate the types, etc.), but this will have to wait for Guice 3.
frameworks could iterate through all the objects that have been
created in a scope?
On 04/04/2009, Dhanji R. Prasanna <dha...@gmail.com> wrote:
> On Sat, Apr 4, 2009 at 10:07 AM, James Strachan
> <james.s...@gmail.com>wrote:
>> Would it be so bad for a Scope to have an iterate() method so tools &
>> frameworks could iterate through all the objects that have been
>> created in a scope?
>
>
> This would make Scope source incompatible with all Guice 1.0 and
> transitional code. =(
How would a Scope optionally implementing Iterable cause a problem?
If we add a method to Scope it will break a lot of existing code out there that doesn't implement that method. Not to mention binaries that are compiled against the current version of Scope.
OK how's this for another idea - one that keeps the scopes nice and
simple, doesn't require hacking the InjectionListener API - or indeed
the public guice API at all, but just requires an optional minor
refactor in each Scope to implement (and a few trivial delegation
methods here and there - more on that later...).
So a Scope by definition of its interface is just a proxy Provider
which takes the Key and the real provider, looks up some cached value
and if its not set, lazily creates it from the real provider. e.g.
something like this..
public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) {
return new Provider<T>() {
public T get() {
T t = getCachedValue(key);
if (t == null) {
t = creator.get();
setCachedValue(key, t);
}
return t;
}
}
}
This is how singleton/request/session scopes all work and I'm sure
pretty much all scopes are like this.
Now from an Injector we can get the Bindings which expose the
Provider. So we already have an easy way to iterate through all the
bindings/scopes - it'd be trivial to filter out all the bindings for a
certain scope or whatever etc.
The only thing that is missing is that from a provider is the ability
to expose the getScopeValue(). i.e. if we introduced an internal
interface (none of this has to be part of the public Guice API/SPI as
its only a couple of tooling methods & frameworks that are gonna use
it) called CachingProvider
/** A scope may implement this interface so it can expose its cached value */
interface CachingProvider<T> extends Provider<T> {
/** Returns the cached value for the given key or null if its not
cached or supported */
T getCachedValue(Key<T> key);
}
then a very minor and optional refactor in scopes would look like this...
public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) {
return new CachingProvider<T>() {
public T get() {
T t = getCachedValue(key);
if (t == null) {
t = creator.get();
setCachedValue(key, t);
}
return t;
}
}
public T getCachedValue(Key<T> key) {
...
}
}
So no new iteration required on the scopes; we purely have an optional
interface on the providers the scopes create.
The only issue remaining now is that this provider gets wrapped a few
times before its put into the binding; so we'd need to add a few
delegates of CachingProvider on the proxies & facades that are invoked
before the scope's provider is. Then anyone who knows this trick could
iterate through all the objects in a scope if they want - or folks
could implement their own close/pre-destroy hooks above Guice - while
keeping the core of Guice nice and clean and Bob doesn't have to ever
see a pre-destroy hook :).
I've created a patch for this to show how trivial it is which works
for singleton/request/session with very minimal changes to those
scopes...
http://code.google.com/p/google-guice/issues/detail?id=354
Thoughts?