Changing Annotations at Startup in CDI 2.0

Today’s CDI 2.0 topic is actually about features that have been in there for a long while, but that I don’t see much evidence of out in the wild.

A lot of people are used to injection as provided by Jersey.  This is implemented under the covers by the excellent little underappreciated HK2 project, not by CDI.

HK2 can do really neat things with annotations.  In today’s blog, I want to show you how I took an annotation that was being used by an HK2-based system to provide configuration injection and, using the sledgehammer of the CDI portable extension API, made it usable in a CDI project.

The annotation looks like this, more or less:


public class Config {
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@interface Key {
String value();
}
}

view raw

Config.Key.java

hosted with ❤ by GitHub

As you can see, it is a simple Qualifier annotation that can be applied to fields and parameters only.  It has a value element, which ends up being the name of a particular piece of configuration you want.

So you could see it being used like this:

@Inject
@Config.Key("frobnicationInterval")
private int frobnicationInterval;

Or like this:

@Inject
public Frobnicator(@Config.Key("frobnicationInterval") final int frobnicationInterval) {
  super();
  this.frobnicationInterval = frobnicationInterval;
}

HK2 has a concept somewhat analogous to CDI’s producer.  While a CDI producer can be a method or a field, it can’t be a class.  HK2, on the other hand, defines only one kind of thing-that-can-make-other-things, and calls it a Factory.  Factory‘s provide method returns the thing it can make, and you annotate the provide method in much the same way as you do a CDI producer method.

Unless, of course, you’re working with this particular annotation, as its Target meta-annotation does not allow it to be placed on methods of any kind.

In the HK2 project I was looking at, then, the Factory in question behind this Config.Key annotation simply declared that it provides Object.  No qualifiers.  Hmm.

Now, to get a Factory to be treated as a Factory by HK2, you have to mark it as such, or otherwise instruct HK2 to treat it as a Factory.  Otherwise it’s just another object that happens to implement the Factory interface.  None of those markings or instructions were immediately apparent.

The other thing you can do, though, is define something called an InjectionResolver.  If you do that, then inside that class you can do whatever you like to resolve the given injection point.  As I looked at this project, I found one for Config.Key.  It delegated its work off to the Factory I found, and other various internal configuration engines and whatnot, and the end result is that any class inside this project could do something like the examples I showed above.

I thought I’d cobble together some CDI constructs to do the same thing.

I knew that I couldn’t make any use of managed beans, because of course int and String are not managed beans.  Short of really convoluted potential other solutions, obviously I’d need a producer method.  I would just need to make a producer method that returned Object and that is qualified by Confi

Oops.  The annotation doesn’t have ElementType.METHOD as one of the places listed where it can be used. So my producer method code won’t compile, because I can’t put the Config.Key annotation on it.

Off to the portable extension toolbox.

First, I wrote a private annotation (I named it Property) that looks like this:


@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
private @interface Property {
@Nonbinding
String value() default "";
}

view raw

Property.java

hosted with ❤ by GitHub

This private annotation (an inner annotation) is solely for my bean-housing-the-producer-method’s use, and the use of the extension that installs it.

You’ll note it looks almost the same as Config.Key, but this time it has ElementType.METHOD in its Target annotation.  It also features the Nonbinding annotation applied to its value element, and the value element now has a default value of "".  We’ll talk about those things in a bit.

Then, for reasons to be clear in a bit, I wrote an AnnotationLiteral implementation for it:


private static final class PropertyLiteral extends AnnotationLiteral<Property> implements Property {
private static final long serialVersionUID = 1L;
private final String value;
private PropertyLiteral() {
this("");
}
private PropertyLiteral(final String value) {
super();
Objects.requireNonNull(value);
this.value = value;
}
@Nonbinding
@Override
public final String value() {
return this.value;
}
public static final PropertyLiteral of(final String value) {
return new PropertyLiteral(value);
}
}

The new Property annotation will let me write a producer method like this:


@Produces
@Dependent // not really needed; I like to be explicit
@Property
private final Object produceProperty(final InjectionPoint ip, final BigCoConfigurationEngine config) {
// Get the right configuration value identified by the injection point
// using the BigCoConfigurationEngine
throw new UnsupportedOperationException("Not done yet");
}

…now that I can use @Property on methods and not just on parameters and fields.

But who cares? No user code can use my new, private inner Property annotation!  So who the heck is ever going to cause this method to be invoked?

Enter portable extensions.  Now we need a portable extension that will search for injection points that feature @Config.Key and make them behave as though they were written with @Property instead.  Once we do that, then we have a linkage: the user’s code says that it wants a particular kind of Object to be injected—namely a @Config.Key-ish one—but we know that for our purposes this should be translated into a desire for a @Property-ish one, and that will be satisfied by the CDI typesafe resolution algorithm and this producer method will be invoked.

There is a container event for just this sort of thing.  It’s called ProcessInjectionPoint, and you can do things with it like this:


private final void processInjectionPoint(@Observes final ProcessInjectionPoint<?, ?> event) {
if (event != null) {
final InjectionPoint ip = event.getInjectionPoint();
assert ip != null;
final Set<Annotation> existingQualifiers = ip.getQualifiers();
if (existingQualifiers != null && !existingQualifiers.isEmpty()) {
final Set<Annotation> newQualifiers = new HashSet<>();
existingQualifiers.stream().forEach((qualifier) -> {
assert qualifier != null;
if (qualifier instanceof Key) {
newQualifiers.add(new PropertyLiteral(((Key)qualifier).value()));
} else {
newQualifiers.add(qualifier);
}
});
event.configureInjectionPoint().qualifiers(newQualifiers);
}
}
}

This (private!) method will be called on every injection point found by the container (or created by other portable extensions!).  You can restrict the types of points you’re interested in by using something other than wildcards, but this will do for our purposes.

Here, you can see that we ask the InjectionPoint directly for its qualifiers, and we effectively remove the Config.Key qualifier and replace it with an equivalent Property qualifier.

So under the covers it now looks like all client code is using Property, not Config.Key in its injection points.  Cool!

Let’s circle back to the producer method, now that we know it will be invoked.

In CDI, a producer method can take parameters.  If it does, then the parameters are supplied to it by the container as if the method had been marked with @Inject.  So our producer method is handed an InjectionPoint object and a BigCoConfigurationEngine object.  (Let’s pretend for the sake of this article that an instance of BigCoConfigurationEngine has already been found by the container.  We’ll just assume it’s there so will be successfully injected here.)

The InjectionPoint unsurprisingly represents the site of the injection that the producer method will be called upon to implement as needed (as determined by the scope, in our case Dependent).  You can get lots of useful things from it: the parameter or field being injected “into”, the class housing the injection point, and so on.

So we should be able to get the actual Property instance that caused our producer method to fire, and, using the value of its value element, ask the BigCoConfigurationEngine to get us the right configuration value.

There is one very important thing to note here.

There are a couple of “paths” to the information we would like.  Only one of them is valid.

The first path looks like an easy one: we could just call injectionPoint.getAnnotated(), and then call getAnnotation(Class) on it and pass it our Property class.

But we’ll get back no such annotation!  How can that be?  Didn’t our portable extension munge things at startup so that all Config.Key qualifiers got effectively replaced by Property qualifiers?

Yes, but on the injection point itself, not on the objects reachable from the injection point.

That gives us path #2, which is the right one, though it is more cumbersome.  We need to call injectionPoint.getQualifiers(), and then find the Property annotation in there.  That is, our portable extension affected the contents of the return value of the InjectionPoint::getQualifiers method, not the contents of the return value of injectionPoint.getAnnotated().getAnnotations().  Sit and think about that for a moment.  I’ll wait.

So our producer method ends up looking like this:


@Produces
@Dependent
@Property
private static final Object produceProperty(final InjectionPoint injectionPoint, final BigCoConfigurationEngine config) {
Objects.requireNonNull(config);
Object returnValue = null;
if (injectionPoint == null) {
// This is a case that really shouldn't happen.
return null; // ok to do with Dependent scope
} else {
final Set<Annotation> qualifiers = injectionPoint.getQualifiers();
assert qualifiers != null;
assert !qualifiers.isEmpty();
final Optional<Annotation> propertyAnnotation = qualifiers.stream().filter((annotation) -> {
return annotation instanceof Property;
}).findFirst();
assert propertyAnnotation.isPresent();
final Property property = Property.class.cast(propertyAnnotation.get());
assert property != null;
final String name = property.value();
assert name != null;
returnValue = config.get(name);
}
return returnValue;
}

The takeaway for me here was: in your producer method, if you want to be maximally flexible and a good citizen of the CDI multiverse, make sure you investigate the InjectionPoint metadata itself as much as possible for information, not the Annotated instances reachable from it.

Starting CDI beans eagerly and portably

There are several web resources out there that describe how to get a CDI bean (usually ApplicationScoped) to be instantiated when the CDI container comes up.  Here’s an arbitrarily selected one from Dan Allen:


@ApplicationScoped
@Startup
public class StartupBean
{
@PostConstruct
public void onStartup()
{
System.out.println("Application starting up.");
}
}


public class StartupBeanExtension implements Extension
{
private final Set<Bean<?>> startupBeans = new LinkedHashSet<Bean<?>>();
<X> void processBean(@Observes ProcessBean<X> event)
{
if (event.getAnnotated().isAnnotationPresent(Startup.class) &&
event.getAnnotated().isAnnotationPresent(ApplicationScoped.class))
{
startupBeans.add(event.getBean());
}
}
void afterDeploymentValidation(@Observes AfterDeploymentValidation event, BeanManager manager)
{
for (Bean<?> bean : startupBeans)
{
// the call to toString() is a cheat to force the bean to be initialized
manager.getReference(bean, bean.getBeanClass(), manager.createCreationalContext(bean)).toString();
}
}
}

Let’s look at the extension immediately above.

The AfterDeploymentValidation event is the only event a portable extension can observe in a portable manner that indicates that the container is open for business. It is guaranteed to fire last in the dance that the container performs as it starts up.

In the extension above, then, you can see that it has saved off the beans it has discovered that have been annotated with a Startup annotation (which isn’t defined in the gist, so it could be javax.ejb.Startup or a Startup annotation that Dan has defined himself). Then, for these beans, it uses the BeanManager‘s getReference() method to get an actual Java object representing those beans.

So what’s the toString() call for?

The short answer is: this is the only method that could conceivably cause the container-managed client proxy that is the object reference here to call the actual constructor of the actual underlying object that the user and everyone else is actually interested in.

For more detail, let’s look at the specification, section 6.5.3, which concerns contextual references—the return value of the getReference() method above—and which reads in part:

If the bean has a normal scope [e.g. not Dependent or Singleton], then the contextual reference for the bean is a client proxy, as defined in Client proxies, created by the container, that implements the given bean type and all bean types of the bean which are Java interfaces.

OK, so whatever the getReference() method returns is a client proxy. That means it’s going to forward any method invocation it receives to the object that it’s proxying (known as the bean’s contextual instance. CDI containers try to be efficient, so that object may not have been created yet—the proxy, in other words, might be lazy. So here, the contextual reference returned is a contextual reference (a client proxy) of type Object, and the toString() invocation causes the contextual reference (client proxy) to realize that oh, hey, the underlying contextual instance of whatever bean this is doesn’t yet exist, and to therefore invoke the “real” constructor, so that the “real” object (the contextual instance) can return something from its toString() method.

As a happy side effect, the container is of course the one causing the instantiation of the “real” object, so it is going to perform dependency injection, invoke initializer methods, invoke any PostConstruct-annotated methods, and so on.  Presto: you have a portable way for your bean to be instantiated when the container comes up.

(Side note: toString() in particular is used because there is a little nugget at the bottom of the Client Proxies section of the specification that says, innocuously (emphasis mine):

The behavior of all methods declared by java.lang.Object, except for toString(), is undefined for a client proxy. Portable applications should not invoke any method declared by java.lang.Object, except for toString(), on a client proxy.

File that one away: among other things that might mean don’t put your CDI-managed objects in Maps or Collections, since doing so will use client proxy equals(Object) and hashCode() methods!)

So.  If you’re feeling like this whole thing is a bit of a steaming hack—you call toString(), and a whole lot of very sophisticated magic happens (!)—I can’t disagree.

Fortunately, there’s a better way.

Instead of the toString() hack above, you can do the same thing in the extension in a more sanctioned, more explicit manner.

The first thing to understand is: who is doing the actual creation?  If we can understand that, then we can understand how to do it eagerly and idiomatically.

The ultimate answer is: the bean itself, via the create() method it implements from the Contextual interface. So if you ever get a Bean in your hand (or any other Contextual), you can call create() on it all day long and get new instances (this should sound scary, because it is).  For example, suppose you have an ApplicationScoped-annotated bean in your hand in the form of a Bean object.  If you call create() on it three times, you will create three instances of this object, hopefully surprising and gently horrifying you. Those instances may be wrapped in proxies (for interception and such), but they won’t be wrapped in client proxies.  Don’t do this!

Still, scary or not, we’ve found the method that some part of the container somewhere at some point will invoke when a contextual instance is needed (to further wrap in a contextual reference). But obviously when you inject an ApplicationScoped-annotated bean into, say, three different locations in your code somewhere, everything behaves as though there’s only one instance, not three. So clearly the container is not running around rampantly calling create() every time it needs an object.  It’s acquiring them from somewhere, and having them created if necessary.

The container is actually calling a particular get() method on the bean’s associated Context, the machinery that implements its scope. This method’s contract basically says that the Context implementation should decide whether to return an existing contextual instance, or a new one. So you can see that the Context underlying application scope will (hopefully!) return the One True Instance™ of the bean in question, whereas the Context implementation underlying some other scope may create new instances each time.

OK, so what do we know?  Let’s step back.

So to eagerly instantiate beans at startup while respecting their scopes, we should follow this approach in the extension as well—we’ll do basically what a lazy contextual reference (client proxy) does, in other words, when a toString() method is invoked on it.  We’ll need to effectively ask the right Context for a contextual instance of the bean in question, creating it if necessary.  This also would allow for custom-scoped beans to be instantiated eagerly, provided of course that the Context backing the custom scope is actually active.

If you have a BeanManager handy, and a Bean object of a particular type (say Bean<Object>), then you can get the relevant Context quite easily:

final Context context = beanManager.getContext(bean.getScope());

Then you need a CreationalContext, the somewhat opaque object that assists the Context with creation, should the Context decide that creation is necessary:

final CreationalContext<Object> cc = beanManager.createCreationalContext(bean);

Finally, we can ask the Context for a contextual instance directly:

final Object contextualInstanceNotContextualReference = context.get(bean, cc);

Note, as the variable name makes clear, this is a contextual instance, not a contextual reference!  This is not a client proxy!  So you really don’t want to use it after this point.

So if you replace Dan’s line 19 above with these code snippets, you will eagerly instantiate beans at startup without relying on magic side effects.