Java EE CDI programmatic dependency disambiguation example – Injection Point inspection
In this tutorial we shall see how we can avoid programmatic dependency disambiguation when injecting Java EE CDI beans. We have already shown in the Jave EE dependency disambiguation example how to avoid dependency disambiguation in CDI beans. Here we shall show you how to avoid dependency disambiguation in a dynamic way. We will achieve this by inspecting the injection point of the bean that injects another bean’s implementation.
The programmatic disambiguation with injection point inspection will be examined by creating a simple service with two implementations. Then we will create a Producer
method to produce and inject both implementations in an application.
Our preferred development environment is Eclipse. We are using Eclipse Juno (4.2) version, along with Maven Integration plugin version 3.1.0. You can download Eclipse from here and Maven Plugin for Eclipse from here. The installation of Maven plugin for Eclipse is out of the scope of this tutorial and will not be discussed. Tomcat 7 is the application server used.
Let’s begin,
1. Create a new Maven project
Go to File -> Project ->Maven -> Maven Project.
In the “Select project name and location” page of the wizard, make sure that “Create a simple project (skip archetype selection)” option is unchecked, hit “Next” to continue with default values.
Here the maven archetype for creating a web application must be added. Click on “Add Archetype” and add the archetype. Set the “Archetype Group Id” variable to "org.apache.maven.archetypes"
, the “Archetype artifact Id” variable to "maven-archetype-webapp"
and the “Archetype Version” to "1.0"
. Click on “OK” to continue.
In the “Enter an artifact id” page of the wizard, you can define the name and main package of your project. Set the “Group Id” variable to "com.javacodegeeks.snippets.enterprise"
and the “Artifact Id” variable to "cdibeans"
. The aforementioned selections compose the main project package as "com.javacodegeeks.snippets.enterprise.cdibeans"
and the project name as "cdibeans"
. Set the “Package” variable to "war"
, so that a war file will be created to be deployed to tomcat server. Hit “Finish” to exit the wizard and to create your project.
The Maven project structure is shown below:
- It consists of the following folders:
- /src/main/java folder, that contains source files for the dynamic content of the application,
- /src/test/java folder contains all source files for unit tests,
- /src/main/resources folder contains configurations files,
- /target folder contains the compiled and packaged deliverables,
- /src/main/resources/webapp/WEB-INF folder contains the deployment descriptors for the Web application ,
- the pom.xml is the project object model (POM) file. The single file that contains all project related configuration.
2. Add all the necessary dependencies
You can add the dependencies in Maven’s pom.xml
file, by editing it at the “Pom.xml” page of the POM editor, as shown below:
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.snippets.enterprise.cdi</groupId> <artifactId>cdibeans</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>cdibeans Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.jboss.weld.servlet</groupId> <artifactId>weld-servlet</artifactId> <version>1.1.10.Final</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.faces</artifactId> <version>2.1.7</version> </dependency> </dependencies> <build> <finalName>cdibeans</finalName> </build> </project>
As you can see Maven manages library dependencies declaratively. A local repository is created (by default under {user_home}/.m2 folder) and all required libraries are downloaded and placed there from public repositories. Furthermore intra – library dependencies are automatically resolved and manipulated.
3. Create a simple Service
GreetingCard.java
a simple service that creates a greeting message for the application that uses it. It is an interface with a method that produces the greeting message.
GreetingCard.java
package com.javacodegeeks.snippets.enterprise.cdibeans; public interface GreetingCard { void sayHello(); }
The implementations of the service are shown below:
GreetingCardImpl.java
package com.javacodegeeks.snippets.enterprise.cdibeans.impl; import com.javacodegeeks.snippets.enterprise.cdibeans.GreetingCard; public class GreetingCardImpl implements GreetingCard { public void sayHello() { System.out.println("Hello!!!"); } }
AnotherGreetingCardImpl.java
package com.javacodegeeks.snippets.enterprise.cdibeans.impl; import com.javacodegeeks.snippets.enterprise.cdibeans.GreetingCard; public class AnotherGreetingCardImpl implements GreetingCard { public void sayHello() { System.out.println("Have a nice day!!!"); } }
4. Create a Producer method to inject the bean
In order to inject the service to another bean, we create our own annotation. CDI allows us to create our own Java annotation, that is the GreetingType.java
, and then use it in the injection point of our application to get the correct implementation of the GreetingCard
according to the GreetingType
of the bean.
The Greetings
is an enumeration parameterized with the implementations of the service, as shown below:
GreetingType.java
package com.javacodegeeks.snippets.enterprise.cdibeans; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import com.javacodegeeks.snippets.enterprise.cdibeans.impl.AnotherGreetingCardImpl; import com.javacodegeeks.snippets.enterprise.cdibeans.impl.GreetingCardImpl; @Retention(RUNTIME) @Target({ FIELD, TYPE, METHOD }) public @interface GreetingType { Greetings value(); public enum Greetings { HELLO(GreetingCardImpl.class), HI(AnotherGreetingCardImpl.class); Class<? extends GreetingCard> clazz; private Greetings(Class<? extends GreetingCard> clazz){ this.clazz = clazz; } public Class<? extends GreetingCard> getClazz() { return clazz; } } }
Now we can create a Producer
to provide applications instances of the GreetingCard
service implementations. The GreetingCardFactory.java
class is a Producer
that has a method, getGreetingCard
. The method takes two parameters. The first parameter is a javax.enterprise.inject.Instance
parameterized with the the required bean type, that is the GreetingCard
here. It is annotated with the @Any
annotation that allows an injection point to refer to all beans or all events of a certain bean type. The second parameter is the javax.enterprise.inject.spi.InjectionPoint
that is the field in the client application that will inject the bean using the @Inject
annotation. So the method will return the correct implementation of the service according to the service type and the annotations in the injection point.
GreetingCardFactory.java
package com.javacodegeeks.snippets.enterprise.cdibeans; import javax.enterprise.inject.Any; import javax.enterprise.inject.Instance; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.Annotated; import javax.enterprise.inject.spi.InjectionPoint; public class GreetingCardFactory { @Produces @GreetingsProducer public GreetingCard getGreetingCard(@Any Instance<GreetingCard> instance, InjectionPoint ip){ Annotated gtAnnotated = ip.getAnnotated(); GreetingType gtAnnotation = gtAnnotated.getAnnotation(GreetingType.class); Class<? extends GreetingCard> greetingCard = gtAnnotation.value().getClazz(); return instance.select(greetingCard).get(); } }
Note that the method is annotated with an extra annotation, apart from the @Produces
annotation that defines the method as Producer
. The @GreetingsProducer
annotation is used to the injection point to define that it makes use of the specified Producer
method to inject a bean instance. It is actually a CDI Qualifier, shown below:
GreetingsProducer.java
package com.javacodegeeks.snippets.enterprise.cdibeans; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; @Qualifier @Retention(RUNTIME) @Target({ FIELD, TYPE, METHOD }) public @interface GreetingsProducer { }
5. Run the application
In order to run the application, we have created a simple servlet.
In the servlet below both implementations are injected. Each injection point in the servlet is a field, where the @Inject
annotation is used. It is also annotated with the @GreetingsProducer
annotation to specify the Producer
that will be used as also with the @GreetingType
annotation that specifies which implementation will be produced by the Producer
.
GreetingServlet.java
package com.javacodegeeks.snippets.enterprise.cdibeans.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.javacodegeeks.snippets.enterprise.cdibeans.GreetingCard; import com.javacodegeeks.snippets.enterprise.cdibeans.GreetingType; import com.javacodegeeks.snippets.enterprise.cdibeans.GreetingType.Greetings; import com.javacodegeeks.snippets.enterprise.cdibeans.GreetingsProducer; @WebServlet(name = "greetingServlet", urlPatterns = {"/sayHello"}) public class GreetingServlet extends HttpServlet { private static final long serialVersionUID = 2280890757609124481L; @Inject @GreetingsProducer @GreetingType(Greetings.HELLO) private GreetingCard greetingCard; @Inject @GreetingsProducer @GreetingType(Greetings.HI) private GreetingCard anotherGreetingCard; public void init() throws ServletException { } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<h1>" + greetingCard.sayHello() + "</h1>"); out.println("<h1>" + anotherGreetingCard.sayHello() + "</h1>"); } public void destroy(){ } }
To run the example we must build the project with Maven, and then place the war
file produced in webbaps
folder of tomcat. Then, we can hit on :
http://localhost/8080/cdibeans/sayHello
and the result is the one shown below:
This was a tutorial of Java EE CDI programmatic dependency disambiguation with injection point inspection.
Download the source code of this tutorial: CDIDependencyDisambiguationIPInspection.zip
Hi Theodora I seem to be having trouble running this example on WAS 8.5. It appears that GreetingType is not passed as qualifier into Producer method. And subsequently an attempt to extract annotation of GreetingType.class yields null, because (for some reason) there is no such an annotation type in injection point. GreetingType gtAnnotation = gtAnnotated.getAnnotation(GreetingType.class); my question is if you annotated injection point it this way: @Inject @GreetingsProducer @GreetingType(Greetings.HI) private GreetingCard anotherGreetingCard; would you not expect the producer being matched (by @GreetingProducer qualifier) and @GreetingType being attached to injection point as well? The later does not happen in my test… Read more »
You can find the complete example code here : https://github.com/M-Kurek/Arquillian-testing-simple-CDI.
It is extended with Arquillian test.
Thank you for your help, Maciek
The problem I was having appeared to be WAS 8.5 specific as described in this reference:
http://www-01.ibm.com/support/docview.wss?uid=swg1PM51802
once server’s JVM custom property is added:
com.ibm.ws.webbeans.trackInjectionPointsWithStack and set its value to true, the problem goes away and the example described in the article works.
thanks
victor
what if even the GreetingType will be resolved at runtime? perhaps resolved from incoming request?