Rick has posted 25 posts at DZone. You can read more from them at their website. View Full User Profile

CDI Dependency Injection - Tutorial II - Annotation Processing and Plugins - Java EE

04.05.2011
| 94548 views |
  • submit to reddit

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
...
import javax.enterprise.inject.Any;
...
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
	
    @Inject @Any 
    private Instance<ATMTransport> allTransports;
	
    private ATMTransport transport;

       ...
}

The output of this should be: Output
Is this ambigous? true
Is this unsatisfied? false
deposit called
communicating with bank via JSON REST transport
communicating with bank via Soap transport
communicating with bank via Standard transport
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
...
import javax.enterprise.inject.Any;
...
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
	
    @Inject @Any 
    private Instance<ATMTransport> allTransports;
	
    private ATMTransport transport;

    @PostConstruct
    protected void init() {
        transport = allTransports.select(new AnnotationLiteral<Default>(){}).get();

        if (transport!=null) {
            System.out.println("Found standard transport");
            return;
        }

        transport = allTransports.select(new AnnotationLiteral<Json>(){}).get();


        if (transport!=null) {
            System.out.println("Found JSON standard transport");
            return;
        }


        transport = allTransports.select(new AnnotationLiteral<Soap>(){}).get();


        if (transport!=null) {
            System.out.println("Found SOAP standard transport");
            return;
        }

    }
	

	public void deposit(BigDecimal bd) {
        System.out.println("deposit called");

        transport.communicateWithBank(...);
    }

       ...
}

Here is the expected format. Output
Found standard transport
deposit called
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
package org.cdi.advocacy;

import java.math.BigDecimal;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.Instance;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import javax.inject.Named;

@Named("atm")
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {

    @Inject @Any
    private Instance<ATMTransport> allTransports;
    private ATMTransport transport;

    private boolean useJSON = true;
    private boolean behindFireWall = true;

    @SuppressWarnings("serial")
    @PostConstruct
    protected void init() {

        ATMTransport soapTransport, jsonTransport, standardTransport;
        standardTransport = allTransports.select(new AnnotationLiteral<Soap>() {}).get();
        jsonTransport = allTransports.select(new AnnotationLiteral<Json>() {}).get();
        soapTransport = allTransports.select(new AnnotationLiteral<Default>() {}).get();

        System.out.println(standardTransport.getClass().getName());
        System.out.println(jsonTransport.getClass().getName());
        System.out.println(soapTransport.getClass().getName());

        if (!behindFireWall) {
            transport = standardTransport;
        } else {
            if (useJSON) {
                transport = jsonTransport;
            } else {
                transport = soapTransport;
            }
        }
    }

    public void deposit(BigDecimal bd) {
        System.out.println("withdraw called");
        transport.communicateWithBank(null);

    }

    public void withdraw(BigDecimal bd) {
        System.out.println("withdraw called");

        transport.communicateWithBank(null);

    }

}

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 and Apache 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.



Resources




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

There are 35 code listings in this article
Published at DZone with permission of its author, Rick Hightower.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Rogerio Liesenfeld replied on Tue, 2011/04/05 - 11:18am

Great stuff!

Unless I missed something, though, CDI does not (yet?) support injection into non-managed beans, as provided through @Configurable in Spring. Is this planned? And if it is, with build-time or load-time bytecode modification, or with either mechanism as in Spring?

Frank Schwarz replied on Tue, 2011/04/05 - 12:36pm

The source code is missing some angle-bracketed stuff, isn't it?

Rick Hightower replied on Tue, 2011/04/05 - 2:58pm in response to: Rogerio Liesenfeld

If you have a beans.xml under META-INF then that resource (classpath location or jar file) will get processed. It will process all of the Java classes in that jar.
All of the examples were tested with Resin CANDI and WELD.
I reserve the right to be wrong, but I am pretty sure I am right.

Rick Hightower replied on Tue, 2011/04/05 - 2:59pm in response to: Frank Schwarz

Well there was some angle brackets in the first article to activate Alternatives, but nothing in this article. No XML. No XML is needed.

Rick Hightower replied on Tue, 2011/04/05 - 5:35pm in response to: Rick Hightower

Clarification: The AutomatedTellerMachineImpl from the first article is not marked with any @Service, @Managed, etc. and it uses @Inject. But, AutomatedTellerMachineImpl is being "managed" when we look it up through the container.

The example would not work if you "new"ed AutomatedTellerMachineImpl instead of looking it up.

Dan Allen replied on Tue, 2011/04/05 - 9:12pm

You can also add beans programmatically using a CDI extension. Observe the BeforeBeanDiscovery event and use the addAnnotatedType method:

event.addAnnotatedType(beanManager.createAnnotatedType(MyClass.class));

That's one idea. We were using this in Solder for a while, but we opted to switch back to using beans.xml.

Samuli Saarinen replied on Wed, 2011/04/06 - 12:24am

Hi,

 Is the something missing from the last example code snippet or how this can work with identical annotation literals?

standardTransport = allTransports.select(new AnnotationLiteral(){}).get();
jsonTransport = allTransports.select(new AnnotationLiteral(){}).get();
soapTransport = allTransports.select(new AnnotationLiteral(){}).get();

 

Rogerio Liesenfeld replied on Wed, 2011/04/06 - 10:18am in response to: Rick Hightower

Yes, of course, I could do that.

But suppose I don't want to depend on a "ServiceLocator" lookup in top-level components, ie, I don't want to have to make calls like

AutomatedTellerMachine atm = BeanContainerManager.getInstance().getBeanByType(AutomatedTellerMachine.class);

in AtmMain.java. Instead, I would prefer to simply write

AutomatedTellerMachine atm = new AutomatedTellerMachineImpl();

BTW, this "Impl" class would probably be the only implementation in practice, so in most cases it would make more sense to not have the separate interface.

Personally, I prefer my business service classes to have initial state passed through a constructor. With injection into non-managed beans, it would be possible to use custom constructors and final instance fields, while still having dependencies injected into instances created with "new".

Rick Hightower replied on Wed, 2011/04/06 - 12:52pm in response to: Samuli Saarinen

Samuli,

Good eye. It appears I have a slight error in my code listing. DOH! I will fix it and repost that last listing. I meant to pass the annotation to annotation literal and then to the select. It appears I misfired. :)

Pobody's Nerfect.

Rick Hightower replied on Wed, 2011/04/06 - 7:12pm in response to: Samuli Saarinen

http://code.google.com/p/jee6-cdi/wiki/DependencyInjectionAnIntroductoryTutorial_Part2#Code_Listing:_AutomatedTellerMachineImpl_using_selects_and_some Ok... the angle bracket stuff was in it when I wrote it. :) It seems the JavaLobby engine thinks it is a tag and then strips it from the post (on the way out... the tags are in the post).

Rick Hightower replied on Wed, 2011/04/06 - 7:19pm in response to: Frank Schwarz

Ok... Frank... It has all of the angle bracket stuff back.

Rick Hightower replied on Wed, 2011/04/06 - 8:02pm in response to: Rogerio Liesenfeld

(I got the ideas from this response from someone... I am not sure if they want credit, but please chime in if you do... you know who you are....)

"There is no standards-defined way of injecting into objects instantiated via 'new' in CDI as you would do in the case of Spring @Configurable."

"It is doubtful whether there will ever be. The issue is that it would basically require mandating that all CDI implementations use AspectJ style byte-code manipulation as opposed to code generation, proxying, etc."

"If this is something that is really needed, you could create an AspectJ based tool for CDI that takes a class definition and adds CDI run-time look-ups behind the scenes to mimic injection/other CDI functionality. If run-time weaving is necessary, you would then need to specify a weaver for class-loading. In my opinion, this is not really something that needs to be done (as part of the JSR) since it adds additional build/compile/class-loading complexity for a very rare, domain-driven-design purist case. For example, I know of no Spring shops that use @Configurable or compile time weaving and a very small handful that uses load-time weaving."

I could see us adding something like this to CDISource.

Mark Struberg replied on Thu, 2011/04/07 - 12:50am

Hi Rick!

 

Good series!  I just like to add a few oftern overlooked gems. Today it's the @Typed Annotation. It can be used to explicitely declare the type of a bean. There are 2 cases where this is reaaaly handy.

 

Use case 1: 'disabling' a java class as bean

 

Let's say we have a 


@Typed()
public class Menu {
private String name;
private String action;
private Menu parent;
}

and we have a producer method like

@ApplicationScoped
public class MenuProducer {
private @Inject menuSvc;
private @Inject User currentUser;

public @Produces @SessionScoped Menu menuForUser() {
Menu menu = menuSvc.getMenuForUser(currentUser);
return menu;
}
}

 

 A Menu is naturally a tree with a 1:n parent menu relationship. So this will just be created via an @ApplicationScoped MenuServiceImpl or such - but never directly as a bean! Annotate the Menu class as @Typed() effectively says: this bean has no Type at all, thus it will not satisfy any injection point. This has almost the same effect as veto() ing a bean in an Extension (via @Observes ProcessAnnotatedType) but without needing to write an Extension. If we wouldn't annotate our Menu class as @Typed(), it would get picked up as @Dependent scoped bean automatically (I personally don't like that, but thats what the spec defines) - leading to an AmbiguousResolutionException.

 

The 2nd case where @Typed is really handy is if you have subclasses which should not get picked up as the base type:

 

/** 
* This menu service will provide
* read operations only!
*/
@ApplicationScoped
public class MenuService {
public Menu getMenuOfUser(User user) {
..
}
}
/** 
* This will add Administration
* functionality to our base MenuService
*/
@ApplicationScoped
@Typed(MenuAdminService.class)
public class MenuAdminService extends MenuService {
public addMenuPermission(Menu menuTree, String role) {
... }
public storeMenuTree(Menu menu) {
... }
}

 If I would not use the @Typed annotation at the MenuAdminService, then it would automatically satisfy all the types in it's whole class hierarchy - including the MenuService it extends. So if you would try to

private @Inject MenuService menuSvc; 

 then you would get an AmbiguousResolutionException (remember: both MenuService and MenuAdminService implement the MenuService type!). By adding 

@Typed(MenuAdminService.class)

it will only act as the type in the annotation and not automatically use all the parent types too! 


 

Rick Hightower replied on Thu, 2011/04/07 - 10:25am in response to: Mark Struberg

Thanks for adding this. Great info. Thanks again for taking the time to expand on this.

Mark Struberg replied on Thu, 2011/04/07 - 11:47am

Might as well add another tip: Beware of the Constructor!

Writing CDI bean initialisation code in Constructors is not a good thingy as you will see weird effects.

Remember - all beans you get injected (except  @Dependent ones) are always 'contextual references' = proxies for their underlying contextual instances! But CDI implementations do not use Java's own reflect proxy (because this one is only able to proxy interfaces) but must use instance proxies.

How goes? The CDI implementations I know (Weld, CanDI and of course OpenWebBeans) create proxies by dynamically subclassing the class which should get proxied. So whenever a proxy gets created, it will naturally also invoke the constructors up in the parent-class chain.

Thus writing the classic reference counter for your bean will give you wrong numbers:

@SessionScoped
public class MyBean implements Serializable {
  private static int refCounter = 0;

  public MyBean() {
    refCounter++;  // <- this will be called too often since the ct will also get called for proxies
  }
  ...
}

 Instead use the @PostConstruct or @Inject annotations on any method:

@SessionScoped
public class MyBean implements Serializable {
  private static int refCounter = 0;

  @Inject
  public void init() {
    refCounter++;  // <- this will be called too often since the ct will also get called for proxies
  }

  @PostConstruct
  pubic void anotherInit() {
  }
  ...
  @PreDestroy // ofc this works as well btw!
  public void cleanup() {
  }
}

 

Mark Struberg replied on Thu, 2011/04/07 - 12:19pm

btw, in your example above:
@Produces ATMTransport createTransport(InjectionPoint injectionPoint) {

please note that this would only work for producing @Dependent scoped beans! (which is exactly what you get if you don't explicitely specify another scope).

 

The reason why this only works with @Dependent scoped beans is that for all NormalScoped beans (@RequestScoped, @SessionScoped, @ApplicationScoped,...), you just don't know where they get injected into.

In fact, your contextual instance (your one-and-only instance of that bean in the given scope) will never get @Inject-ed directly, but instead you will always 'only' get a contextal reference (the proxy) injected into your injection point!

Ergo, this trick only works for @Dependent scoped beans...

 

Overally the JSR-299 spec is one of the best specs I've ever read. But as I already expressed above: I don't like the automatic pickup of not-explicitely scoped beans as @Dependent.

Reason is that this information is not really obvious and often gets forgotten.  If you forget about adding the scope annotation or per accident import e.g. javax.faces.beans.SessionScoped instead of javax.enterprise.context.SessionScoped - IDEs are damn good in picking up the wrong import ;) - you will end up with @Dependent. This mean you will be creating a new instance for each and every injection point and EL invocation - and loosing lots of data in between...

Rogerio Liesenfeld replied on Thu, 2011/04/07 - 12:44pm in response to: Rick Hightower

Indeed. If I have the time, I could implement a CDI portable extension that installs a java.lang.instrument.ClassFileTransformer at startup. The transformer would then modify bytecode in the constructors of classes containing injection points (while ignoring classes without such points). Such a modified constructor would end with a call to the InjectionTarget#inject(...) method.

Use of java.lang.instrument is much better and simpler than using AspectJ or compile-time instrumentation. And by using the Attach API, there is no need to have users manually add a "-javaagent:instrumentation.jar" JVM parameter when running an app/container. There is some runtime overhead, obviously, but I believe it should be small enough (based on my experience with the JMockit tools).

I wish CDISource the best of luck. CDI is great, and I am sure it will only improve.

Mark Struberg replied on Thu, 2011/04/07 - 12:58pm

@Rogerio:

 

I fear using -javaagent has one BIG problem: you will always hit the SystemClassLoader!

This is really a no-go  in EE server scenarios and any other situations where you need classpath isolation by using a ClassLoaders hierarchy. The whole javaagent approach is imo only useful if you have a standalone application.In all other situations you will most probably either break the application - or even worse - open a fat security hole without even notizing it!

All existing CDI implementations (Dan and Reza can confirm this for Weld and CanDI) are using a ClassLoader hack. So we are not using compile time instrumentation - in fact we don't change the original bean classes at all! We just additionally create proxies for them at runtime by 'dynamically' creating subclasses via in memory byte-code engineering.So you can still write code like

private MyBean myBean = new MyBean();

 

Mark Struberg replied on Thu, 2011/04/07 - 1:12pm in response to: Rogerio Liesenfeld

> support injection into non-managed beans

Stu (Stuart Douglas) of JBoss fame wrote a portable CDI Extension for exactly that [1]. This Extension basically will dynamically apply annotations to your classes - without having to write them in your code. It will give you the kind of XML config you might know from Spring 2.

 In general: if you are looking for CDI Extensions, then check JBoss Seam3 [2] and  Apache MyFaces CODI [3] first

 LieGrue,
strub

 

[1] http://docs.jboss.org/seam/3/xml-config/latest/reference/en-US/html_single/

[2] http://docs.jboss.org/seam/3/3.0.0.Final/reference/en-US/html/

[3] http://myfaces.apache.org/extensions/cdi/

PS: <shamelessselfadvert>the Seam3 doc is much better, but CODI is in some areas much more powerful ;) </shamelessselfadvert>

Rick Hightower replied on Thu, 2011/04/07 - 1:53pm in response to: Mark Struberg

RE: the Seam3 doc is much better, but CODI is in some areas much more powerful ;)


When I think of successful OS projects, they usually have on thing in common: good docs.

Facelets was well documented and easy to learn. Hibernate had excellent documents (not everyone agrees with this). Spring has excellent documents. etc. etc.

Docs are a good sign of the overall quality of a project. That being said. I am horrible at writing docs (see crank).

Reza Rahman replied on Thu, 2011/04/07 - 2:29pm

Yep - we do the same thing. In the early EJB 3.1 time-frame there was actually some talk of making EJB/JPA/CDI "new"-able. It was tabled because it gets hideously complicated for everyone involved - especially when you get beyond simple injection and there is a possibility the modified byte-code can be run in environments/class-loaders that you cannot entirely predict (e.g. taking a war that runs fine on Resin and try to run it on GlassFish). Basically it would require standardizing not just the API but how the API is actually implemented.

Rogerio Liesenfeld replied on Thu, 2011/04/07 - 3:26pm in response to: Mark Struberg

Hello Mark,

Actually, a ClassFileTransformer (registered through the java.lang.instrument.Instrumentation object provided by a "Java agent") can transform classes loaded by *any* classloader. In fact, the first parameter to the "transform" method is the ClassLoader which defined the class to be transformed. Therefore, the transformer implementation can decide whether to transform or not each class based on its defining classloader.

Also, it's not actually necessary to use the "-javaagent" parameter at all. A Java agent can be loaded programmaticaly (at least from JDK 1.6 on).

I have actually implemented this kind of stuff in a tool which uses multiple classloaders; it works.

Mark Struberg replied on Fri, 2011/04/08 - 3:59am in response to: Rogerio Liesenfeld

Hi Rogerio!

What I meant is that by using -javaagent alll the classes you need for the transformation will end up getting loaded via the SystemClassLoader. I'm not 100% sure yet about the 'transformed classes' butI think they also get loaded that way.

 You will see this effect if you use those libs with tomcat - they will just refuse to work. The reason is that tomcat implements a security feature to prevent people from overloading classes which got loaded via the SystemClassLoader. I stumbled accross this problem in OpenJPA:

https://issues.apache.org/jira/browse/OPENJPA-1454
https://issues.apache.org/jira/browse/OPENJPA-1410

I haven't yet used the programmatical way to use the javaagent in Java6 of course

Jessie Mear replied on Wed, 2011/09/07 - 6:40am

The latest release of the technology, JSR 314: JavaServer Faces 2.0, makes UI development for Java EE applications even easier through support for annotations and the addition of new features such as Facelets and composite components. java programmers

Rick Siskey replied on Thu, 2011/10/27 - 12:06am

CDI or Computer Design Integration understands what your business is like, its application, structure, the whole nine yards. After it gets the necessary information, it then evaluates what you require and finally, it formulates recommendations. - Rick Siskey

Krishna Bheeman... replied on Sun, 2012/06/24 - 9:49pm

Hi Rick is it posible to have @Inject @Any private Instance allTransports; and @Produces ATMTransport createTransport(InjectionPoint injectionPoint) { in same application? I am getting null for allTransports Thank you for your help

Pradeep Kumar replied on Wed, 2012/09/19 - 12:57pm

Thanks for adding this, great explanation, very useful for me..!!!!!!!!!!!

Mark Srikanth replied on Tue, 2014/05/20 - 8:07am

 great work really thank you for the info about sap .a free live demo on all sap courses.by Exp trainers  SAP BO Online Training .http://sapbusinessobjectstraining.com/

Vladislav Naydenov replied on Mon, 2014/06/30 - 9:06am

Hi, Rick.

I'm following your tutorial step-by-step and I can't make

 createTransport(InjectionPoint injectioPoint) 

to work only with @Produces annotation. I'm getting 

org.jboss.weld.exceptions.DeploymentException: WELD-001409Ambiguous dependencies for type [IATMTransport] with qualifiers [@Default] at injection point [[field] @Inject private com.automatedtellermachine.AutomatedTellerMachine.transport]. Possible dependencies [[Producer Method [IATMTransport] with qualifiers [@Default @Any] declared as [[method] @Produces public com.automatedtellermachine.transport.factory.TransportFactory.createTransport(InjectionPoint)], Managed Bean [class com.automatedtellermachine.transport.StandartATMTransport] with qualifiers [@Default @Any]]]

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.