The JSR-299 passed its final release stage on December 10, 2009 and programming by dependency injection has become a standard part of the Java EE 6 platform. It is time to test integrating ZK with JSR-299 or CDI — Context and Dependency Injection for Java EE platform.

And here is the result, the Hello World of  ZK + CDI.

Hello World

zkcdi_helloworld

— index.zul —

<?variable-resolver class="org.zkoss.zkplus.cdi.DelegatingVariableResolver"?>
<window title="ZK + CDI: Hello World" width="300px">
    My weld-injected bean says: ${helloWorld.text}
</window>

— HelloWorld.java —

@Named
@SessionScoped
public class HelloWorld implements Serializable {
    private final String text = "Hello World";
    public String getText() {
        return text;
    }
}

ZK provides a variable-resolver directive <?variable-resolver?> that application developers can specify the resolver class that will be used by the ZK EL evaluator(${…}), ZK annotated data binder(@{…}),  and <zscript> interpreter to resolve unknown variables. Based on such mechanism, if we can implements a variable resolver class that would resolve the CDI managed beans by their EL names, it would be a perfect integration point between the two frameworks.

And it turns out to be quite easy.

Each CDI framework by specification needs to implement an ELResolver for resolving a CDI managed bean with EL name. So what I have to do is just to bridge ZK variable resolver request to CDI ELResolver and done.

Number Guess

Loading a static “Hello World” page might be just too simple. How about some real Ajax interactions with CDI? I use the  Weld JSR-299 RI developed by JBoss plus Tomcat 6 as my CDI framework so I also borrow the “numberguess” example from the Weld download package as my testing base. I rewrite the example(mainly only the view page) with ZK annotated data binding mechanism and it works just perfect.

This is a game that generates a random target number between 0 to 100 and the game player gets 10 opportunities to guess what the number is. For each guessing, the application will response message to hint the player what the next guess shall be higher or lower and the new valid number range.

Get Adobe Flash player

— index.zul —

<?variable-resolver class="org.zkoss.zkplus.cdi.DelegatingVariableResolver"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit"?>
<window title="Guess a number..." border normal" width="300px">
  <vbox>
    <label value="@{game.message}" style="color:red"/>
    <span>
I'm thinking of a number between <label value="@{game.smallest}" style="color:blue"/>
and <label value="@{game.biggest}" style="color:blue"/>. You have
<label value="@{game.remainingGuesses}" style="color:blue"/> guesses remaining.
    </span>
    <span>
Your guess:
<intbox value="@{game.guess, save-when:'guessButton.onClick'}" disabled="@{game.noMoreGuess}"/>
    </span>
    <span>
      <button id="guessButton" label="Guess" disabled="@{game.noMoreGuess}"/>
      <button label="Reset" onClick="game.reset();binder.loadAll();"/>
    </span>
  <vbox>
</window>

The CDI managed bean Game(with EL name “game”) is the class that  keep all game states.

A typical program flow is like this:

  1. Players enter the guessing number into the ZK <intbox> component and press Guess button to make the guess.
  2. ZK data binder takes over and call game.setGuess() with the entered value as specified in <intbox value="@{game.guess, save-when:'guessButton.onClick'}"/>.
  3. In the game.setGuess() method, the program compares the guess number and the target number and then changes the game states such as message, biggest, smallest number, remainingGuesses, etc. accordingly.
  4. ZK data binder’s “load-on-save” mechanism then updates those ZK UI components with @{game.xxx} expression automatically and players see the response message and updated states for this guessing on the screen.

Note that these are Ajax based. Only ZK UI components with @{game.xxx} expression is updated.

This “number guess” example also tests the ZK zscript interpreter with the CDI. When players press the Reset button, zscript interpreter will call game.reset();binder.loadAll(); that reset game states and generate a new target number, and then ask ZK data binder to update all ZK UI components.

Because zscript interpreter supports all major scripting languages such as groovy, ruby, javascript, and python, you can actually access CDI managed beans by your favorite scripting language.

More?

I expect to test more integrations between ZK and CDI besides the EL resolving. Some ideas have popped  into my mind. Is it possible to inject ZK components as CDI manged beans? Can we implement more CDI contexts for ZK scopes such as Desktop, Page, IdSpace, Component, and Execution? Can we inject a controller bean to a ZK component by CDI annotation? Can we bind ZK event and CDI event automatically? A lot of things to do.

Steps to setup Tomcat 6 + Weld

  1. Copy weld-servlet.jar to your application’s WEB-INF/lib folder. You can find the jar in Weld 1.0 SP1
  2. Add in your application WEB-INF/web.xml the following listener. This makes Weld bind with Tomcat.
    <listener>
        <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
    </listener>
    
  3. In your application META-INF folder, new a context.xml file with following contents. This provides a JNDI reference java:comp/env/BeanManager for the accessing to the Weld bean manager. The ZK CDI variable resolver will need this.
    <Context>
        <Resource name="BeanManager" auth="Container"
            type="javax.enterprise.inject.spi.BeanManager"
            factory="org.jboss.weld.resources.ManagerObjectFactory"/>
    </Context>

References

ZK QuickStart Guide

Weld – JSR-299 Reference Implementation

Download (source in war file)

helloworld.war

numberguess.war

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

19 Responses to “Integrate ZK and JSR-299(Weld)”

  1. robbiecheng says:

    cool, CDI is one important feature of Java EE, now ZK developers have more choice other than Sping. 😉

  2. I found this entry in Gavin King is blog.

    This is real cool to have CDI ready in ZK.

    Regards

  3. Thomas Scherm says:

    Hi Henri,

    since a couple of days, I´m Working on a integration of ZK && Weld. First i worte a Variable Resolver, then i implementated an conversation context bound to zk´s desktop lifecycle & staying stable on desktop recreation on browser refresh.

    No i am ready for test with my implementation of a long running conversation propagation by wrapping and extending Execution instances.

    In other words: i m pleased to see, you think of the same concerns and like weld / cdi. I think it´s cool.

    Interested in a code donnation?

    cheers

    Thomas

  4. Hello and many thanks for a enlightening article. I appreciate what you said.

  5. Nana Welle says:

    Like your blog 🙂 I’ll check your site later again.

  6. Reuben E. says:

    I glorify the valuable info you provide in your articles. I will bookmark your blog and have my kids check up here oftentimes. I am quite sure they will study lots of new stuff here than anybody else!

  7. Ricky says:

    I tried to download the war numberguess.war, put in my glassfish v3 and i get the following error:
    org.zkoss.xel.XelException: Cannot locate the BeanManager for JavaEE 6.

  8. Marcel says:

    Hi Henri,
    I tried your CDI example in Tomcat and it works great. I am also currently working on creating a Portlet for Liferay with the same example you give on this page. Unfortunately that does not seem to work. For the HelloWorld example I see the ZK Window incl. the content, but the injected “Hello World” String is missing. There are no errors in the server logs, therefore I am not sure what is happening. Do you have any idea why this would not work in Liferay?
    Thanks,
    Marcel

  9. henrichen says:

    @Marcel,

    Since this involve at least 4 parties(ZK, Weld, Tomcat, Liferay), I think the first step is to make sure Weld + Tomcat + Liferay works :). Can I ask what version of ZK, Tomcat, and Liferay you tested on?

  10. Marcel says:

    Hi Henri,
    I am using ZK 5.0.0, Weld 1.0 and the Liferay 5.2.3 bundle (that includes Tomcat 6.0). I will try Weld+Liferay without ZK and let you know how that works for me.
    Thanks,
    Marcel

  11. Marcel says:

    Hi Henri,

    I resolved my problem. I was missing the empty beans.xml in my portlet. After adding it to WEB-INF folder I got it to work. (http://seamframework.org/Community/WeldAndGlassfishV3)

    I also needed to change the scope from @SessionScoped to @ApplicationScoped in the HelloWorld example.

    Thanks for your help,
    Marcel

  12. Took me time to read all the comments, but I really enjoyed the article. It proved to be Very helpful to me and I am sure to all the commenters here! It’s always nice when you can not only be informed, but also entertained! I’m sure you had fun writing this article.

  13. Excellent work there. I must appreciate author as well.

  14. Marcel says:

    Hi Henri,

    it looks like the problem with the session scope is caused by ThreadLocal variables. Do you have an idea/suggestion how to resolve this?

    I found a similar Problem with other frameworks and there is an example saying to use a ThreadLocalListener. I tried that but it did not solve my problem, see http://www.zkoss.org/smalltalks/zk2.4.1/zk2.4.1.dsp

    Thank you,
    Marcel

    Example for Hibernate:

    ThreadLocal Variables Synchronizer
    org.zkoss.zkplus.util.ThreadLocalListener

    ThreadLocal

    org.hibernate.context.ThreadLocalSessionContext=context;

  15. Henri Chen says:

    @Marcel,

    I think the best way to handle this ThreadLocal issue is to “disable” the ZK Event thread mechanism entirely.

    in zk.xml

    <system-config>
    <disable-event-thread>true</disable-event-thread>
    </system-config>

  16. […] view part of the application while controller code is written separately in a composer class. In previous article Henri showed us how to use DelegatingVariableResolver to resolve CDI managed beans by their EL […]

  17. Hello and many thanks for article. Excellent work there.

  18. That is some inspiring stuff. Never knew that belief could be this diverse.

  19. Nick says:

    Guys have you tried installing the war in JBoss 6.0 AS or in Glassfish 3.0.1 which are fully j2ee 6 supported app servers?

Leave a Reply