(Disclaimer: I work for Oracle doing Java EE architecture and other things, but none of my writings here or anywhere else on this site have anything to do with my day job there. In short, as always, these are just the writings of a Java EE hacker using publicly available stuff.)
In the last post, we saw how you could write a portable extension that starts up a JAX-RS runtime after the CDI container is open for business. (I recommend reading all the posts in this series, starting with the first one.)
Here’s the extension again:
For this extension to work, though, line 34 has to return a non-null HttpServer. As you can see, there’s nothing in this extension that makes such a thing. The extension is, fortunately, relatively fault-tolerant (or at least that’s the idea 😀), so if no such HttpServer is around, then the extension should just silently do nothing.
In CDI in general, you write things so that the supplying of an object you need is SEP (someone else’s problem). That is, quite apart from the mechanics of injection and so on, the important part of dependency injection is that if you need something, you just presume that it will be handed to you. So here we presume that somehow, some way, an HttpServer will be available in the CDI container. If it turns out that no such object exists, well, OK, we just do no harm.
So how could an HttpServer get in to the container so that it would be picked up by this extension? Well, it could be a bean itself. Maybe we’ll get lucky? But probably not, as the Javadoc shows. Sure enough, although HttpServer has a zero argument constructor, and hence could be a CDI bean, there are no further injection points on it, and furthermore the Grizzly module of which it is a part does not feature a META-INF/beans.xml descriptor, suggesting, though not proving, that we’re not going to get lucky. So HttpServer is technically speaking a valid CDI bean, but a pretty limited one if discovered or added programmatically to a CDI container as-is.
How else could it get in there? Well, there could be a producer method. This seems like a good way to go. A producer method makes bean instances out of raw materials available in the CDI container. That sounds like exactly what we want. So let’s say that ServerCo, writes one to make an HttpServer using the features of the GrizzlyHttpServerFactory class present in Jersey. Let’s say it looks like this, and, just for kicks, lives in a different jar file than the one that houses the portable extension:
Let’s look at line 46 above. This is a producer method that creates an HttpServer if one is needed. To do so, it requires a GrizzlyHttpContainer, but such a thing might not exist. To express this kind of optional behavior, it requests that an Instance<GrizzlyHttpContainer> be supplied to it. The CDI container will make such a thing available whether its underlying “payload” exists or not, so we can test things about it here. See line 48: if the Instance is unsatisfied—that is, if there isn’t a GrizzlyHttpContainer in the CDI container anywhere, nor a means for one to be synthesized or manufactured—then we can return null here, and the portable extension we saw earlier will effectively quietly become one big no-op.
Very cool. OK, so where does the GrizzlyHttpContainer come from?
That’s the purpose of the first producer method, that you can see at line 34. That method says how a GrizzlyHttpContainer can be created, provided that someone supplies a javax.ws.rs.core.Application object. Obviously, if no such object is available in the CDI container then the method should effectively quietly do nothing, so you can see at lines 36 and 37 that’s what happens.
OK, so clearly the first producer method makes something that the second producer method needs. And the first producer method works if there’s an Application. And the portable extension consumes the output of the second producer method and uses it to start a server when the container comes up.
So where does the Application come from?
Let us turn back to our poor developer, who, you recall from my last post, was done. She had written a JAX-RS application and a root resource class and—right at that point—wanted to be finished. She simply wanted some magic to happen that would let her application start. She didn’t want to write a main method. She didn’t want to go start an application server and run some complicated deployment recipe.
Recall also that if you’re keeping track we have several notional jar files (or directory locations—classpath roots, really) that we’ve stuck in fictional corners. We have:
- A CDI 2.0 EDR2 implementation (like weld-se-core version 3.0.0.Alpha17 or later)
- the developer’s classpath root, containing nothing but her Application subclass and her root resource class
- ServerCo’s portable extension jar file described in Part 2
- A classpath root that contains the boilerplate main class that brings a CDI container up and shuts it down, described in my first post
- ServerCo’s jar file containing the producer methods noted above
What’s really neat is if you run this:
java -classpath /path/to/weld-se-core-3.0.0.Alpha17.jar:/path/to/weld-se-core/dependencies:/path/to/developer/code:/path/to/serverco/portable-extension-1.0.jar:/path/to/boilerplate/code:/path/to/serverco/producers-1.0.jar:/path/to/serverco/dependencies com.foobar.Main
…then I hope to have shown that you will get an HTTP endpoint up and running on localhost port 80 that runs our developer’s JAX-RS application.