In the comments thread for this post, Bill got me thinking again about a problem that's been bugging me for a while.
The problem is that there are certain kinds of dependent objects
(in the sense of the Web Beans @Dependent scope) that need to know
something about the object or injection point into which they are
injected in order to be able to do what they do. For example:
- the log category for a Logger depends upon the class of the object that owns it
- injection of a HTTP parameter or header value depends upon what parameter or header name was specified at the injection point
- injection of the result of an EL expression evaluation depends upon the expression that was specified at the injection point
The solution I've come up with is to allow injection of a special metadata object into any injected dependent object.
(Note that this functionality is specifically for @Dependent scope Web Beans - objects with normal scopes are shared between many clients and many injection points, so their behavior cannot depend upon anything relating to the client.)
Let's look at an example:
class LogFactory { @Produces Logger createLogger(InjectionPoint injectionPoint) { return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); } }
This clever little producer method lets you inject a JDK Logger with the right log category. Instead of typing:
Logger log = Logger.getLogger(MyClass.class.getName());
You can write:
@Current Logger log;
which is shorter, and less vulnerable to refactoring problems.
Not convinced? OK then, let's see a second example...
To inject HTTP parameters, we need to define a binding type:
@BindingType @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @annotation HttpParam { @NonBinding public String value(); }
We would use this binding type at injection points as follows:
@HttpParam("username") String username; @HttpParam("password") String password;
The following producer method does the work:
class HttpParams @Produces @HttpParam("") String getParamValue(ServletRequest request, InjectionPoint ip) { return request.getParameter(ip.getAnnotation(HttpParam.class).value()); } }
I suppose this stuff is mainly useful for framework-type development, but it might also have other applications.
So how hard is it to add this feature to Web Beans? Not hard at all! The Web Beans specification would define the following API:
public interface InjectionPoint { public Type getType(); public Set<Annotation> getBindingTypes(); public Object getInstance(); public Bean<?> getBean(); public Member getMember(): public <T extends Annotation> T getAnnotation(Class<T> annotation); public Set<T extends Annotation> getAnnotations(); }
And the Web Bean manager would be required to provide a built-in Web Bean that implements this interface.
What do you think?