Search

CDI Dependency Injection - An Introductory Tutorial Part 1 - Java EE -2



Code Listing:
 AutomatedTellerMachineImpl using Instance 
view source
print?
01.package org.cdi.advocacy;
02. 
03.import java.math.BigDecimal;
04. 
05.import javax.annotation.PostConstruct;
06.import javax.enterprise.inject.Default;
07.import javax.enterprise.inject.Instance;
08.import javax.inject.Inject;
09.import javax.inject.Named;
10. 
11.@Named("atm")
12.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
13. 
14.@Inject @Soap
15.private Instance<ATMTransport> soapTransport;
16. 
17.@Inject @Json
18.private Instance<ATMTransport> jsonTransport;
19. 
20.@Inject @Default
21.private Instance<ATMTransport> defaultTransport;
22. 
23.private ATMTransport transport;
24. 
25.@PostConstruct
26.protected void init() {
27.if (!defaultTransport.isUnsatisfied()) {
28.System.out.println("picked Default");
29.transport = defaultTransport.iterator().next();
30.else if (!jsonTransport.isUnsatisfied()) {
31.System.out.println("picked JSON");
32.transport = jsonTransport.iterator().next();
33.else if (!soapTransport.isUnsatisfied()) {
34.System.out.println("picked SOAP");
35.transport = soapTransport.iterator().next();
36.}
37.}
Notice we are using *`Instance`* as the field type instead of ATMTransport. Then we look up the actual transport. We can query a Instance with the Instance.isUnsatisfied to see it this transport actually exist. There is an Instance.get method to retrieve a single transport, but I used *`Instance.iterator().next()`* to highlight an important aspect of Instance, namely, it can return more than one. For example, there could be 20 @Default based transports in the system.
Imagine if you were implementing a chain of responsibility pattern or a command pattern, and you wanted an easy way to discover the actions or commands that were on the classpath. Instance would be that way. CDI makes this type of plugin development very easy.
If it could find a single @Default, the one we have been using since the start, on the classpath. The output from the above would be as follows:
Output
view source
print?
1.picked Default
2.deposit called
3.communicating with bank via Standard transport
Continue reading... Click on the navigation links below the author bio to read the other pages of this article.
Be sure to check out part I of this series as well: CDI DI Tutorial!

About the author 
This article was written with CDI advocacy in mind by
 Rick Hightower with some collaboration from others. Rick Hightower has worked as a CTO, Director of Development and a Developer for the last 20 years. He has been involved with J2EE since its inception. He worked at an EJB container company in 1999. He has been working with Java since 1996, and writing code professionally since 1990. Rick was an early Spring enthusiast. Rick enjoys bouncing back and forth between C, Python, Groovy and Java development.
Although not a fan of EJB 3, Rick is a big fan of the potential of CDI and thinks that EJB 3.1 has come a lot closer to the mark.
Rick Hightower is CTO of Mammatus and is an expert on Java and Cloud Computing. Rick is invovled in Java CDI advocacy and Java EE. CDI Implementations - Resin Candi - Seam Weld - Apache OpenWebBeans

Now to test how the Instance.isUnsatisfied by commenting out the implements ATMTransport in StandardAtmTransport class definition. You are essentially taking StandardAtmTransport out of the pool of possible injection of ATMTransport. There are no more defaults configured so it should be an unsatisfied.


Code Listing:
 StandardAtmTransport commenting out implements ATMTransport soInstance.isUnsatisfied returns true 
view source
print?
01.package org.cdi.advocacy;
02. 
03.import javax.enterprise.inject.Default;
04. 
05.@Default
06.public class StandardAtmTransport { //implements ATMTransport {
07. 
08.public void communicateWithBank(byte[] datapacket) {
09.System.out.println("communicating with bank via Standard transport");
10.}
11. 
12.}
Now the output is this:
view source
print?
1.picked JSON
2.deposit called
3.communicating with bank via JSON REST transport
Reread this section if you must and make sure you understand why you get the above output.
You can use Instance to load more than one bean as mentioned earlier. Let's lookup all installed installed @Default transports. To setup this example remove all of the annotations in theATMTransport interfaces and make the beans.xml empty again (so no Alternative is active).


Code Listing:
 SoapAtmTransport making it @Default by removing @Soap qualifier 
view source
print?
01.package org.cdi.advocacy;
02. 
03.//import javax.enterprise.inject.Alternative;
04. 
05.//@Soap
06.public class SoapAtmTransport implements ATMTransport {
07. 
08.public void communicateWithBank(byte[] datapacket) {
09.System.out.println("communicating with bank via Soap transport");
10.}
11. 
12.}


Code Listing:
 JsonRestAtmTransport making it @Default by removing @Json qualifier 
view source
print?
01.package org.cdi.advocacy;
02. 
03.//import javax.enterprise.inject.Alternative;
04. 
05.//@Alternative @Json
06.public class JsonRestAtmTransport implements ATMTransport {
07. 
08.public void communicateWithBank(byte[] datapacket) {
09.System.out.println("communicating with bank via JSON REST transport");
10.}
11. 
12.}


Code Listing:
 StandardAtmTransport making it @Default by removing any qualifiers from it 
view source
print?
01.package org.cdi.advocacy;
02. 
03. 
04.//Just make sure there are no qualifiers
05.public class StandardAtmTransport implements ATMTransport {
06. 
07.public void communicateWithBank(byte[] datapacket) {
08.System.out.println("communicating with bank via Standard transport");
09.}
10. 
11.}
We also need to make sure that the beans.xml file is empty. 

Code Listing:
 {classpath}/META-INF/beans.xml removing all alternatives 
view source
print?
1.<beans xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.xsi:schemaLocation="
3.http://java.sun.com/xml/ns/javaee
4.http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
5.</beans>
Now use every transport that is installed using the annotation.
view source
print?
01.package org.cdi.advocacy;
02. 
03.import java.math.BigDecimal;
04.import java.util.Iterator;
05. 
06.import javax.annotation.PostConstruct;
07.import javax.enterprise.inject.Any;
08.import javax.enterprise.inject.Instance;
09.import javax.inject.Inject;
10.import javax.inject.Named;
11. 
12.@Named("atm")
13.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
14. 
15.@Inject
16.private Instance<ATMTransport> allTransports;
17. 
18.@PostConstruct
19.protected void init() {
20.System.out.println("Is this ambiguous? " + allTransports.isAmbiguous() );
21.System.out.println("Is this unsatisfied? " + allTransports.isUnsatisfied() );
22.}
23. 
24.public void deposit(BigDecimal bd) {
25.System.out.println("deposit called");
26. 
27.for (ATMTransport transport : this.allTransports) {
28.transport.communicateWithBank(null);
29.}
30. 
31.}
32. 
33.public void withdraw(BigDecimal bd) {
34.System.out.println("withdraw called");
35. 
36.for (ATMTransport transport : this.allTransports) {
37.transport.communicateWithBank(null);
38.}
39. 
40.}
41. 
42.}
In this context ambiguous means more than one. Therefore, CDI found more than one possibility for injection if ambiguous returns true. It should find three defaults.
Your output should look like this (or something close to this).
Output
view source
print?
1.Is this ambiguous? true
2.Is this unsatisfied? false
3.deposit called
4.communicating with bank via JSON REST transport
5.communicating with bank via Soap transport
6.communicating with bank via Standard transport
7.communicating with bank via the Super Fast transport
Note that we changed deposit to iterate through the available instances.
Now try something new comment out the implements ATMTransports in SuperFastAtmTransport,JsonRestAtmTransport and SoapRestAtmTransport. JsonRestAtmTransport andSoapRestAtmTransport transport class definition should have this //implements ATMTransport {.
Now rerun the example. You get this output.
Output
view source
print?
1.Is this ambiguous? false
2.Is this unsatisfied? false
3.deposit called
4.communicating with bank via Standard transport
Since the only transport left is the standard transport (StandardAtmTransport), only it is in the output. The Instance is no longer ambiguous, there is only one so it prints false. CDI finds the one so it is not unsatisfied.
Now comment out all of //implements ATMTransport, and you get this:
view source
print?
1.Is this ambiguous? false
2.Is this unsatisfied? true
3.deposit called
Continue reading... Click on the navigation links below the author bio to read the other pages of this article.
Be sure to check out part I of this series as well: CDI DI Tutorial!

About the author 
This article was written with CDI advocacy in mind by
 Rick Hightower with some collaboration from others. Rick Hightower has worked as a CTO, Director of Development and a Developer for the last 20 years. He has been involved with J2EE since its inception. He worked at an EJB container company in 1999. He has been working with Java since 1996, and writing code professionally since 1990. Rick was an early Spring enthusiast. Rick enjoys bouncing back and forth between C, Python, Groovy and Java development.
Although not a fan of EJB 3, Rick is a big fan of the potential of CDI and thinks that EJB 3.1 has come a lot closer to the mark.
Rick Hightower is CTO of Mammatus and is an expert on Java and Cloud Computing. Rick is invovled in Java CDI advocacy and Java EE. CDI Implementations - Resin Candi - Seam Weld - Apache OpenWebBeans
Notice there a no longer any ATMTransport transport implementations in the system at all.
The @Any qualifier states that you want all instances of an implementation. It does not matter what qualifiers they have, you want them all @Jsons, @Soaps, @SuperFasts, whatever.
Add the all of the annotations we commented out back to all of the transports. Add the @Any to the transport injection as follows:


Code Listing:
 AutomatedTellerMachineImpl @Inject @Any *`Instance`* to inject all transport instances 
view source
print?
01....
02.import javax.enterprise.inject.Any;
03....
04.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
05. 
06.@Inject @Any
07.private Instance<ATMTransport> allTransports;
08. 
09.private ATMTransport transport;
10. 
11....
12.}
The output of this should be: Output
view source
print?
1.Is this ambigous? true
2.Is this unsatisfied? false
3.deposit called
4.communicating with bank via JSON REST transport
5.communicating with bank via Soap transport
6.communicating with bank via Standard transport
7.communicating with bank via the Super Fast transport
@Any finds all of the transports in the system. Once you inject the instances into the system, you can use the select method of instance to query for a particular type. Here is an example of that:


Code Listing:
 AutomatedTellerMachineImpl using selects to find a particular transport from the list you loaded 
view source
print?
01....
02.import javax.enterprise.inject.Any;
03....
04.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
05. 
06.@Inject @Any
07.private Instance<ATMTransport> allTransports;
08. 
09.private ATMTransport transport;
10. 
11.@PostConstruct
12.protected void init() {
13.transport = allTransports.select(new AnnotationLiteral<Default>(){}).get();
14. 
15.if (transport!=null) {
16.System.out.println("Found standard transport");
17.return;
18.}
19. 
20.transport = allTransports.select(new AnnotationLiteral<Json>(){}).get();
21. 
22. 
23.if (transport!=null) {
24.System.out.println("Found JSON standard transport");
25.return;
26.}
27. 
28. 
29.transport = allTransports.select(new AnnotationLiteral<Soap>(){}).get();
30. 
31. 
32.if (transport!=null) {
33.System.out.println("Found SOAP standard transport");
34.return;
35.}
36. 
37.}
38. 
39. 
40.public void deposit(BigDecimal bd) {
41.System.out.println("deposit called");
42. 
43.transport.communicateWithBank(...);
44.}
45. 
46....
47.}
Here is the expected format. Output
view source
print?
1.Found standard transport
2.deposit called
3.communicating with bank via Standard transport
Now imagine there being a set of settings that are configured in a db or something and the code might look like this to find a transport (this should look familiar to you by now).


Code Listing:
 AutomatedTellerMachineImpl using selects and some business logic to decide which transport to use 
view source
print?
01.package org.cdi.advocacy;
02. 
03.import java.math.BigDecimal;
04. 
05.import javax.annotation.PostConstruct;
06.import javax.enterprise.inject.Any;
07.import javax.enterprise.inject.Default;
08.import javax.enterprise.inject.Instance;
09.import javax.enterprise.util.AnnotationLiteral;
10.import javax.inject.Inject;
11.import javax.inject.Named;
12. 
13.@Named("atm")
14.public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
15. 
16.@Inject @Any
17.private Instance<ATMTransport> allTransports;
18.private ATMTransport transport;
19. 
20.private boolean useJSON = true;
21.private boolean behindFireWall = true;
22. 
23.@SuppressWarnings("serial")
24.@PostConstruct
25.protected void init() {
26. 
27.ATMTransport soapTransport, jsonTransport, standardTransport;
28.standardTransport = allTransports.select(new AnnotationLiteral<Soap>() {}).get();
29.jsonTransport = allTransports.select(new AnnotationLiteral<Json>() {}).get();
30.soapTransport = allTransports.select(new AnnotationLiteral<Default>() {}).get();
31. 
32.System.out.println(standardTransport.getClass().getName());
33.System.out.println(jsonTransport.getClass().getName());
34.System.out.println(soapTransport.getClass().getName());
35. 
36.if (!behindFireWall) {
37.transport = standardTransport;
38.else {
39.if (useJSON) {
40.transport = jsonTransport;
41.else {
42.transport = soapTransport;
43.}
44.}
45.}
46. 
47.public void deposit(BigDecimal bd) {
48.System.out.println("withdraw called");
49.transport.communicateWithBank(null);
50. 
51.}
52. 
53.public void withdraw(BigDecimal bd) {
54.System.out.println("withdraw called");
55. 
56.transport.communicateWithBank(null);
57. 
58.}
59. 
60.}
Exercise: Please combine the use of Instance with a producer to define the same type of lookup but have the business logic and select lookup happen in the TrasportFactory. Send me your answers on the CDI group mailing list. The first one to send gets put on the CDI wall of fame. (All others get honorable mentions.)

The dirty truth about CDI and Java SE

The dirty truth is this. CDI is part of JEE 6. It could easily be used outside of a JEE 6 container as these examples show. The problem is that there is no standard interface to use CDI outside of a JEE 6 container so the three main implementations Caucho Resin Candi, Red Hat JBoss Weld andApache OpenWebBeans all have their own way to run a CDI container standalone.
As part of the promotion and advocacy of CDI, we (Andy Gibson, Rob Williams, and others) came up with a standard way to run CDI standalone. It is a small wrapper around CDI standalone containers. It works with Resin Candi, Weld and OpenWebBeans. If you used the examples, in the CDI DI or this article then you used the first artifact that the CDISource organization put together. We plan on coming up with ways to unit test JPA outside of the container, and a few other things. As we find holes in the CDI armor we want to work with the community at large to fill the holes. CDI, although standard, is very new. We are hoping that the groundwork that CDI has laid down can get used outside of Java EE as well as inside of Java EE (we are not anti-Java EE).

Conclusion

Dependency Injection (DI) refers to the process of supplying an external dependency to a software component.
CDI is the Java standard for dependency injection and interception (AOP). It is evident from the popularity of DI and AOP that Java needs to address DI and AOP so that it can build other standards on top of it. DI and AOP are the foundation of many Java frameworks. I hope you share my vision of CDI as a basis for other JSRs, Java frameworks and standards.
This article discussed more advanced CDI dependency injection in a tutorial format. It covered some of the features of CDI such as processing annotation data and working with multiple instances of various types using the Instance class to tap into the powerful CDI class scanner capabilities.
CDI is a foundational aspect of Java EE 6. It is or will be shortly supported by Caucho's Resin, IBM's !WebSphere, Oracle's Glassfish, Red Hat's JBoss and many more application servers. CDI is similar to core Spring and Guice frameworks. However CDI is a general purpose framework that can be used outside of JEE 6.
CDI simplifies and sanitizes the API for DI and AOP. Through its use of Instance and @Produces, CDI provides a pluggable architecture. With this pluggable architecture you can write code that finds new dependencies dynamically.
CDI is a rethink on how to do dependency injection and AOP (interception really). It simplifies it. It reduces it. It gets rid of legacy, outdated ideas.
CDI is to Spring and Guice what JPA is to Hibernate, and Toplink. CDI will co-exist with Spring and Guice. There are plugins to make them interoperate nicely. There is more integration option on the way.
This is just a brief taste. There is more to come.

No comments:

Post a Comment