Basic CDI (in WebSphere 8.5, for JAX-WS)
Dependency Injection (DI) is a really, really useful pattern, IMO. I imagine most folks get that already. The Spring framework was my introduction to DI, and I still use and like Spring for many projects, in many ways.
However, with object lifecycles now managed by JEE containers, like for JAX-WS or JAX-RS services, sometimes using Spring-managed beans is not as direct as I’d like. JEE 6’s Contexts and Dependency Injection (CDI) has been suggested to me multiple times as an alternative, built-into the JEE server and directly tied to its object lifecycles.
Most of my projects use Spring for other things as well, so I hadn’t been willing to look down the CDI path just yet. However, I’m now working on some SOAP Web Service projects that don’t yet use Spring, so I thought I’d give CDI a look.
What follows is the basics of configuring a “web” application that is both a JAX-WS service and a JAX-WS client to one or more other services. I need to inject both some interface implementation classes and some JEE Resource References into my service. In case documenting this particular example helps anyone else. Or just helps me clarify what I did and remember it in the future :-)
@Inject
In my @WebService JAX-WS implementation class (or any other “service” / “servlet” class managed by the JEE container), I need to make use of a “business facade” interface implementation singleton. JEE 6 annotation @Inject enables this.
@Inject
private BusinessFacade facade;
If I only have one such BusinessFacade implementation class defined in the application, this is sufficient to locate it. (Classes don’t have to be annotated in order to have their instance(s) injectable as “Producers”. They just have to meet some conventions: see About CDI Managed Beans.)
If you have multiple implementation classes, you’ll need to qualify the @Inject like with @Named.
@ApplicationScoped
By default, the “scope” of a Managed Bean is “Dependent”. That is, its lifecycle matches the lifecycle of the bean into which it is injected. In this case, the lifecycle of my @WebService instance. Which, at least under WebSphere 8.5.5, is created and destroyed with each HTTP request.
Since I want this BusinessFacade instance to be a singleton, only initialized once for the whole application, I need to tell my implementation class that I want instances of it to have “Application” scope. I do that by annotating that class with @ApplicationScoped.
@ApplicationScoped
public class BusinessFacadeImpl implements BusinessFacade
(@Singleton is from the EJB spec and is somewhat different from CDI’s @ApplicationScoped.)
@Resource
Now, somewhere down my object chain, I have another object that calls another Web Service, via a JAX-WS client wrapper, and I need to inject the endpoint URL into that wrapper.
I manage that URL through a web.xml <resource-ref>
that I can map to server Resource at deploy time:
<resource-ref>
<res-ref-name>serviceLocation</res-ref-name>
<res-type>java.net.URL</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Unshareable</res-sharing-scope>
</resource-ref>
To get that injected into my hand-written caller class, I use @Resource, specifying the same name used in <res-ref-name>
:
@Resource(name="serviceLocation")
private URL serviceEndpointUrl;
@PostConstruct
Now, specific to JAX-WS client wrappers, I want to override the endpoint URL that was specified in the WSDL used to create the client code. The approach that seems cleanest to me is using the BindingProvider interface.
The @PostConstruct annotation indicates a method (named whatever you want) to be called after a class instance is created. I’ll use this to bind a (singleton) JAX-WS client wrapper (ServicePortType in the example) to the endpoint URL specified in the above injected @Resource:
private ServicePortType serviceEndpoint = null;
@PostConstruct
public void init()
{
serviceEndpoint = new Service().getServicePortTypeEndpoint();
if (serviceEndpoint != null)
{
((BindingProvider) serviceEndpoint).getRequestContext().put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
serviceEndpointUrl.toString());
}
}
(Where Service and ServicePortType match whatever was generated from your WSDL.)
Note that I’ve annotated this particular class with @ApplicationScoped, so this @PostConstruct will only be called once. Another potential pattern would be to have this class not be a singleton but have the @PostConstruct method lazy-initialize and reuse a static ServicePortType instance.
beans.xml
Finally, logistically, your application must contain a beans.xml
file in its WEB-INF
directory in order for WebSphere to know to scan it for CDI annotations. This file can be completely empty.
Notes
- I think technically some of these annotations aren’t part of the CDI spec. But they all work in conjunction with each other to allow me to do simple DI within my JEE container, without additional 3rd-party dependencies.
- For me, one of the downsides of annotation-based injection in general is that, best I can tell, if I want to switch between two interface implementations, say a mock implementation and a real one, I have to change the annotation and recompile the code. With Spring and XML configuration, I change an external file and restart the application. (I agree with this StackOverflow question.)
- Another downside of annotation-based “coding/wiring”, as opposed to configuration file wiring, is that you have to look all over the place to see the whole structure of the application. Being able to look in one (or a few) file(s) is useful at times.
- Best I can tell, CDI doesn’t have an easy way to pull in properties files or override properties from System properties. Like I describe here for Spring.
Update
From fellow IBMer Kenneth Stephen:
W.r.t your second bullet in your “Notes” - this is exactly what alternatives are for. See here: http://www.ibm.com/developerworks/websphere/techjournal/1301_stephen/1301_stephen.html#alts The referenced StackOverflow question has a lengthy response by ColinD which is really good, but got this slightly wrong. He refers to the solution as qualfiers - which is not quite what is needed. He goes on to talk about a problem with alternatives where one could potentially need multiple instances of the code with different behaviors, and this problem is really solved by qualifiers.