With the invaluable help of Bruno Harbulot (and Thierry and Jerome!), I checked
in a preliminary version of org.restlet.ext.guice to the incubator sources. I
confirmed that it works for one somewhat artificial example (see
org.restlet.ext.guice.example), but it needs more eyes on it, it needs tests,
and it needs some real use.
The design is very much like what I reported blogging about at the beginning of
this thread, except that FinderFactory is now called DependencyInjection
because it adds a few "extension methods" for attach/attachDefault (on Routers
and Virtual Hosts) and setNext (on Filters, Servers, and ServerLists).
The idea is that you "wrap" regular attach/attachDefault/setNext calls with
DependencyInjection calls to inject a target ServerResource. For example:
DependencyInjection di = ...;
TemplateRoute route = di.attach(router, "/path", MyResource.class);
// same as router.attach("/path", di.finderFor(MyResource.class));
// Secret is a Qualifier (JSR-330's version of BindingAnnotation)
di.setNext(filter, AnotherResource.class, Secret.class);
// same as filter.setNext(di.finderFor(AnotherResource.class, Secret.class));
I moved the FinderFactoryModule into the RestletGuice class, which was
previously just a container for static methods, so it is now
RestletGuice.Module. It implements DependencyInjection, and it still has the
property that if it hasn't been used to create an Injector by the time one of
its DependencyInjection methods is called, an Injector is created lazily from
it.
I removed all mention of com.google.inject from DependencyInjection, so this
interface would be suitable for use with any JSR-330-compliant DI framework.
All this meant was replacing methods that take a Key argument with variants
taking a Class<? extends ServerResource> and a Class<? extends Annotation>,
where the latter must be a javax.inject.Qualifier (or
com.google.inject.BindingAnnotation).
I removed support for Restlet 1.1 Handlers.
I don't think I'm using any features of Guice introduced in 2.0, but I haven't
checked to see if it still works with Guice 1.x.
I haven't touched the code that I put in originally to deal with use of a
single instance of a RestletGuice.Module by multiple Injectors; I suspect it
isn't correct. I'll fix it eventually, but it seems like a pretty marginal use,
so it's not a big priority.
I haven't provided any custom Scopes because no one has told me of a Scope need
that is so common and yet so tricky that it's worth complicating this extension
to keep people from having to roll it themselves. If you think you have such a
need, let me know.
ResletGuice.Module binds providers for Application, Context, Request, and
Response that by default use the getCurrent class method of the corresponding
class. The getCurrent methods rely on a ThreadLocal value that is set properly
for most purposes, including tasks executed via a TaskService; if you need to
change the default behavior, you can override newApplicationProvider,
newContextProvider, newRequestProvider, and newResponseProvider in a subclass
of RestletGuice.Module.
There should be no barriers to using RestletGuice.Module in conjunction with
other frameworks, including guice-servlet. All you need to do is ensure that
your resource bindings are made in Modules passed to the constructor of a
RestletGuice.Module that is used to create the final Injector.
I was going to extend Router to make the functionality a little more
transparent, but that would prevent the extension of Router for other purposes.
Also, for completeness I would have had to extend VirtualHost, Filter, Server,
and ServerList, and you'd have to use those new subclass names to get access to
the variant that takes a qualifier annotation type. It would have been a lot of
API noise instead of just one interface.
I considered another approach that I'd like to get feedback on, a mini-DSL,
where the previous examples would look like this:
TemplateRoute route = di.with(router).attach("/path", MyResource.class);
di.with(filter).setNext(AnotherResource.class, Secret.class);
That would add a few more interfaces for the intermediate types in the DSL, but
they wouldn't be in your face. Would this be a better API?
--tim
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=2427214