Explore the CDI programming model in ZK

Implement a simple application

Java™ Specification Request (JSR) 299: Contexts and Dependency Injection (CDI) for the Java EE platform defines a powerful set of services. Services include type-safe dependency injection of Java EE components and an event notification model for allowing interaction between components, which simplifies access to Java EE services from the Java EE Web tier. Essentially, any third-party framework used in the Java EE Web tier can leverage CDI services using a CDI portable extensions mechanism. This article extends a sample application from the article "Rich Internet applications using ZK," and explains how to modify a real-world example using the ZK framework and its integration with powerful CDI services.

Share:

Mr. Sachin K Mahajan, Software Developer, WSO2 Inc

Photo of Sachin MahajanSachin graduated with a Masters degree from the University of Utah, Salt Lake City. He has worked in both small and large companies in the U.S. and India in various technical and managerial roles. Sachin currently works in the IBM Software Group Lotus division.


developerWorks Contributing author
        level

Ashish Dasnurkar, Engineer, Potix Corp.

Photo of Ashish DasnurkarAshish has a bachelor's degree from the University of Pune, India, and has worked with the Java EE platform for six years on several enterprise applications for multi-national companies. He currently works at the Potix Corporation, which is the driving force behind the ZK Ajax Framework.



25 May 2010

Also available in Chinese

Introduction

ZK, an open source Asynchronous JavaScript and XML (Ajax) framework written in Java code, lets you write a Web 2.0-enabled, rich Internet application without writing a single line of JavaScript code.

ZK is analogous to Ajax without JavaScript. It is a powerful framework composed of an Ajax-based event-driven engine, a rich set of XHTML and XUL components, and the ZUML mark-up language for creating feature-rich user interfaces.

In this article, learn about the Contexts and Dependency Injection for the Java EE platform (CDI) programming model in relation to the ZK framework. This article builds on the example application in "Rich Internet applications using ZK: An open source Ajax framework," an article that explores the power of ZK. Using ZK and CDI, you'll extend the detailed, real-life example application for customer management.

You can download the source code for the application in this article.

JSR-299 and CDI

Java Specification Request (JSR) 299, or Java CDI, is a Java standard for dependency injection and contextual lifecycle management. Through the standard, a set of services is defined that makes application development much easier and cleaner. The services provide:

  • Interaction of objects through an event notification mechanism
  • Type-safe dependency injection
  • Lifecycle methods for stateful objects bound to contexts
  • A "decorator" interceptor to bind the interceptor to objects
  • A service provider interface (SPI) to develop portable extensions

Because CDI emphasizes loose coupling and strong typing, the bean doesn't need to be aware of certain aspects, such as implementation, threading model, or lifecycle. These aspects can vary based on the deployment, thus not affecting the client at all. Loose coupling makes the code easy to maintain and extensible.

ZK and CDI

ZK CDI, which is provided by the ZK framework, gives seamless integration with CDI to expose CDI services within the ZK framework. It lets enterprise developers integrate CDI-driven applications, with a compressive and powerful Ajax front end supplied by ZK. Using CDI and ZK together lets you effortlessly bridge the gap between Java's EE Web tier and Java EE.

The ZK CDI extension allows seamless use of CDI features within the ZK programming model. In addition to built-in CDI features, ZK CDI extensions provide the following features to make development easy.

Custom variable resolver/EL resolver
Resolves CDI managed beans within <zscript />, an EL expression ( ${...}), and a ZK annotated data binding expression (@{…}) by their EL name.
ZK custom scopes
In addition to built-in CDI scopes (Session, Request, Application, and Conversation), this extension provides five more ZK scopes: Desktop, Page, Execution, IdSpace, and Component.
ZK components as managed beans
Lets you inject ZK components into managed beans, such as ZK composer.
UI event handlers using ZK custom annotation and CDI-provided event notification model
Lets you annotate any method with ZK custom annotation and turn it into an event handler method.

The ZK CDI extension is based on Weld, which is a reference implementation of the JSR-299 specification that defines CDI (see Resources for more information).


Using ZK with CDI

The following simple example shows how to access a CDI managed bean from a ZUL file. (The .zul file extension is for a ZK user interface file.) In CDI context, a managed bean, or a simple bean, is a Java EE component that can be injected into other components, associated with a context, or reached through EL expressions.

The simple HelloWorld example in Listing 1 shows how a managed bean is accessed through EL expression within a ZUL file. First you define a HelloWorld class with a single String field named text and the getter method getText(). According to the CDI specification, having a no-arg default constructor makes this class eligible to be a managed bean. To refer the HelloWorld managed bean through a unified EL expression, it needs to be annotated with the qualifier @javax.inject.Named. Any EL name can be given to the managed bean by providing a value member of the @Named qualifier. If it is not specified, the EL name defaults to the unqualified class name of the bean class after converting the first character to lower case. In the example, HelloWorld is defaulted to helloWorld EL.

Listing 1. Access a managed bean
@Named
@SessionScoped
public class HelloWorld implements Serializable {

	private String text = "HelloWorld";
	
	public String getText() {
		return text;
	}
}

The ZUL code in Listing 2 accesses the HelloWorld bean using its defaulted EL name, helloWorld.

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

ZK provides a variable-resolver directive <?variable-resolver ?> . You can use this to specify a resolver class to be used by the ZK EL evaluator (${...}), ZK annotated data binder (@{...}), and the <zscript> interpreter to resolve unknown variables.

One feature of the ZK CDI extension is a custom EL resolver called DelegatingVariableResolver, which can be used to resolve a HelloWorld managed bean by its EL name within a unified EL expression.

In the index.zul file in Listing 2, you define a simple window component and display a text field of the HelloWorld class using the ${helloWorld.text} EL expression. During the ZUL rendering phase, when the ZUL parser encounters this EL expression, it will use DelegatingVariableResolver specified in the <?variable-resolver ?> to resolve the helloWorld bean instance. Because HelloWorld is now a container managed bean, the container is supposed to provide a HelloWorld bean instance. Using the bean instance returned by the container, ${helloWorld.text} will be evaluated. The resulting evaluation of the getText() method returns the "Hello World" string, as shown in Figure 1.

Figure 1. HelloWorld
Screen showing the message My CDI-injected bean says HelloWorld.

Implementing a real-life application using ZK with CDI

This section explores more CDI features by implementing a real-life application. You'll build upon the Manage Customer application that was demonstrated in the "Rich Internet applications using ZK" article. The Manage Customer application lets users perform various operations, such as adding a new customer, editing the customer data, and soft deletions of the customer entries in the database. Figure 2 shows the main user interface screen of the Manage Customers application.

Figure 2. Manage Customers Dashboard
Screen showing customer names, ids, dates, and whether they've been deleted.

The customers registered within the application are listed. The list is a grid, with columns for an ID, name of the customer, active date, and the deleted flag. The data in the grid can be sorted (in either ascending or descending order) by clicking the button near the column names. Sorting has been enabled for the ID (int), Name (String), and active date (Date) column. Pagination has also been enabled for this application, as shown at the bottom of the screen. The page is enabled to show 5 records at a time. Users can move to the next page or directly to a specific page.

Figure 3 shows the top bar of the Manage Customer application.

Figure 3. Top menu bar
Drop-down menu showing where you can register a new customer.

The menu bar is implemented using the menubar widget of ZK. It includes an option to register a new customer and exit the application.


Setting up the development environment

Before diving into implementation details, let's review some details about creating a sample application to manage customers. The Eclipse IDE is used to create the application, but any IDE of your choice should work.

The basic idea is to create a dynamic Web application project and point it to the application server runtime, which in this case is the Apache Tomcat runtime. After setting up the new project and runtime, replicate the folder structure, as shown in Figure 4.

Figure 4. Directory structure
ZKmanagecustomers directory structure

The directory structure of the Manage Customer application follows the same pattern shown in Figure 4.

The core files for this application are contained in the WebContent folder, with the following subdirectories:

  • META-INF contains the database credential information for connecting to the MySQL database.
  • WEB-INF includes the library folder containing the ZK JAR files required for running the application. It also includes three configuration files.
    • beans.xml, which indicates the bean classes within this application that are available for injection.
    • web.xml, which describes the datasource, ZK servlets, and Weld configurations.
    • zk.xml, which may contain ZK-specific configurations.

All the other associated ZUL and HTML files are contained within the WebContent folder. These files serve as the view part of the application, providing the dynamic and static content to the Web application.

The sample file zkManageCustomer.zip (see Download) contains the zipped version of the application. It also includes the metadata files required by Eclipse so it can be imported directly into that IDE seamlessly.

The Java 2 Platform, Enterprise Edition (J2EE) perspective in Eclipse has a server tab, which when right-clicked displays an option to create a new server. This server can be used to manage the application server from the Eclipse IDE.

After the new server is configured, the newly created resources need to be configured on the server. This server configuration deploys the resources that are created during the course of development.

Configuring MySQL

The example program is configured to work with Tomcat and MySQL. However, you should have no trouble getting it to run in another CDI-enabled application server. Because the example uses JDBC, it should work with any supported SQL database, such as DB2 Express-C, with only minor changes to the connection code.

To connect Tomcat to the MySQL database you need to define a resource reference. This element specifies the name of a resource manager connection factory reference, as shown in Listing 3. In this case, it would be the database connection specified by jdbc/mysql, which is of javax.sql.DataSource type.

Listing 3. Define a resource reference
<resource-ref>
      <description>DB Connection</description>
      <res-ref-name>jdbc/mysql</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
</resource-ref>

You also need to define a connection resource in the context.xml file under the WebContent/META-INF folder. This file contains such properties as the driver name, jndi name, username, password, datatype, and the URL.

Listing 4. Define a connection resource
<Resource driverClassName="com.mysql.jdbc.Driver" 
	maxActive="4" maxIdle="2" maxWait="5000" auth="Container" 
	name="jdbc/mysql" password="as1008" type="javax.sql.DataSource" 
	url="jdbc:mysql://localhost:3306/customer" username="root"/>

The customer database has a single table that can be created by running the script in Listing 5.

Listing 5. Create a table
use customer;
CREATE TABLE 'customer' (
	  'ID' int(11) NOT NULL AUTO_INCREMENT,
	  'name' varchar(255) DEFAULT NULL,
	  'date' date DEFAULT NULL,
	  'deleted' tinyint(1) DEFAULT '0',
	  PRIMARY KEY ('ID')
	);

Enabling ZK CDI extension support

To leverage the ZK CDI extension, copy the zkcdi.jar file (in the binary distribution of the ZK CDI extension) into the WEB-INF/lib folder of your Web application project. The zkcdi.jar should be used as a Web application library.

Enabling Weld support for Tomcat

To enable Weld support for Tomcat:

  1. Specify the Weld servlet listener (used to boot Weld and control its interaction with requests) in the WEB-INF/web.xml file in the web root as:
    <listener>
       <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
    </listener>
    <listener>
       <listener-class>org.jboss.weld.el.WeldELContextListener</listener-class>
    </listener>
  2. Tomcat has a read-only JNDI, so Weld can't automatically bind the BeanManager extension SPI. To bind the BeanManager into JNDI, you should populate META-INF/context.xml in the web root with the following contents to make it available to your deployment as:
    <Resource name="BeanManager" auth="Container"
    		type="javax.enterprise.inject.spi.BeanManager" 
    		factory="org.jboss.weld.resources.ManagerObjectFactory" />
          <resource-env-ref>
          <description>Object factory for the CDI Bean Manager</description>
          <resource-env-ref-name>BeanManager</resource-env-ref-name>
          <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager
         </resource-env-ref-type>
    </resource-env-ref>
  3. Add an empty beans.xml file under the WEB-INF folder to enable dependency injection by CDI.

For similar instructions to set up Weld with other application servers and environments, see Resources.


Extending the application

The Manage Customers application provides the following functions:

  • A dashboard for user operations, including a view of all customers
  • Adding new customers
  • Editing existing customers
  • Deleting customers (soft delete)

Figure 2 shows the dashboard with a list of all the registered customers. The user can sort the data based on the ID or Name. The index.zul file has various attributes that define the look and feel of the application, such as borderlayout, menubar, menu, and menupopup.

Listing 6. Define the look
<?page id="manageCust" title="Manage Customers" cacheable="false" 
	language="xul/html" zscriptLanguage="Java" contentType="text/html;charset=UTF-8"?>
	<?variable-resolver class="org.zkoss.zkplus.cdi.DelegatingVariableResolver"?>
<zk>
  <window id="win" border="normal" width="810px" minheight="300"
   apply="${manageCustomer}">
      <caption label="Manage Customers"/>
         <borderlayout height="30px">
	     <north border="none">
	        <menubar id="menubar" width="800px">
		   <menu label="Manage Customers">
		      <menupopup>
		         <menuitem id="add" label="Register New Customer">
			  </menuitem>
			  <menuseparator />
			  <menuitem id="exit" label="Exit" onClick="win.detach()" />
		       </menupopup>
		    </menu>
		 </menubar>
	      </north>
	  </borderlayout>
      <listbox id="customerList" mold="paging" pageSize="5" multiple="true" width="800px">
	   <listhead sizable="true">
	      <listheader label="Id" sort="auto(id)"/>
	      <listheader label="Name" sort="auto(name)"/>
	      <listheader label="Active Date" sort="auto(date)"/>
	      <listheader label="Deleted?" />
	   </listhead>
	</listbox>
</window>
</zk>

Unlike the previous implementation of Manage Customer, this example used ZK's MVC approach to separate the view from the controller. The index.zul file contains only the view part of the application, while controller code is written separately in the composer class ManageCustomer. This lets you leverage ZK CDI features.

With the ZK MVC approach, a controller can be applied to a specific component using the apply attribute. The example uses this technique to apply ManageCustomer to the dashboard main window component. It also demonstrates the use of the variable-resolver directive for DelegatingVariableResolver and an EL expression to apply the ManageCustomer managed bean instance provided by the container.

There are two major components on the dashboard page.

  • The menu at the top that has two submenu items: Register New Customer and Exit.

    Menubar is a ZK menubar component with two child ZK menuitem components with the IDs add and exit.

  • The table listing the customers currently in the database. The table in the example is a ZK listbox component whose ID is customerList. The table column headers are defined by listheader child components of the customerList listbox.

So far, you've explored components that define the dashboard UI, but what about the data? When does the customerList listbox get populated? Look at the ManageCustomer controller class shown in Listing 7.

Listing 7. ManageCustomer
@Named
@SessionScoped
public class ManageCustomer extends GenericComposer implements Serializable {	
	@Inject @SessionScoped CustomerService custSvc;
	@Inject @ComponentId("customerList") private transient Listbox customerList;
	/**
	 * Set up list of all customers on the dashboard
	 * @param component
	 */
	public void doAfterCompose(Component component) throws Exception {
    	   super.doAfterCompose(component);
	   List myList = custSvc.getAllCustomers();
	   ListModelList lm = new ListModelList(myList);
	   customerList.setModel(lm);
	   customerList.setItemRenderer(new CustomersListboxRenderer());
	}
...

First, mark the ManageCustomer bean class with the @Named qualifier, enabling it to be accessed through unified EL expression. This was demonstrated in the window component of the index.zul file with the apply attribute value of "${manageCustomer}".

The next step is to use the CDI type-safe dependency injection feature to inject the CustomerService bean inside the ManageCustomer controller. The CustomerService bean is a utility class that implements methods to access the database for adding, updating, removing, and retrieving customer information. You can do this by specifying a CustomerService field inside the ManageCustomer class and annotating it with @javax.inject.Inject. Per the CDI specification, whenever the ManageCustomer bean instance is instantiated by a container, the CustomerService bean instance will be auto injected in it.

At this point, you need access to the customerList listbox to populate it with all customer data. The example uses the utility class GenericComposer that's provided by the ZK CDI extension. Extend the ManageCustomer class with org.zkoss.cdi.util.GenericComposer, thus enabling the auto injection of the child components of the window component to the ManageCustomer controller class. The auto injection of ZK components requires the use of the qualifier @org.zkoss.cdi.inject.ComponentId with the same component ID specified in the index.zul file. Also, per the ZK CDI extension specification, the ComponentId qualifier member value must match the field name. For example, for injecting the customerList list box in ManageCustomer, use:

@Inject @ComponentId("customerList") private transient Listbox customerList;

It's time to put some data inside this listbox. The data population needs to be done before the page is rendered in the client browser. The GenericComposer is of type Composer, and it needs the doAfterCompose() method. This is a callback method that's invoked after all components are composed. It enables the data population of the customer listbox to occur before rendering, but after all ZUL components are composed. You still must call doAfterCompose() of the GenericComposer before anything else in the overridden doAfterCompose() method.

Register a new customer

What happens when someone clicks "Register New Customer" (the Add menu item on the Manage Customer dashboard menu bar)? After it's clicked, it should present the Enter Customer Data window and save the input data as a new Customer record in the database. To do that, you first need to have an event handling method in the ManageCustomer controller for an "add" menuitem onClick event. You can do this easily by using the ZK CDI extension-defined @org.zkoss.cdi.event.Events qualifier and CDI-defined event notification model. Define a simple registerNewCustomer() method as shown in Listing 8.

Listing 8. Define a registerNewCustomer() method
public void registerNewCustomer(@Observes @Events("add.onClick")  
                MouseEvent evt) throws Exception {
   Window win1 = (Window)Executions.createComponents("addCustomer.zul", null, null);
   win1.doModal();
   win1.setTitle("Enter Customer Data");
   win1.setClosable(true);
   win1.setMaximizable(true);
}

The example uses the @javax.enterprise.event.Observes annotation for a method parameter of type MouseEvent, which is a ZK event type. This will make the registerNewCustomer() method an Observer method (a method that observes a type MouseEvent). Whenever an event of type MouseEvent is published by CDI, this method will be notified. There can be various events of type MouseEvent, so how can you differentiate among them? This is where the @Events qualifier comes to the rescue. The @Events qualifier can be provided with a member value to indicate what kind of event this method needs to be notified of. The @Events member value can be specified in the form of a component ID followed by the event name. For example, for the Add menuitem onClick event, the @Events qualifier can be supplied with an "add.OnClick" value, as shown in Listing 7.

What is the sequence of events when the "Register New Customer" menuitem is clicked? First, a ZK event of type MouseEvent is sent to the server. The ZK framework will receive this event. Then, the ZK CDI extension will publish/fire a CDI event with the appropriate @Events qualifier, along with a member value on MouseEvent type. CDI will notify any methods that are observing on the MouseEvent type with the exact @Events qualifier and member value.

Listing 9. Add customer
<?page title="Add Customer" contentType="text/html;charset=UTF-8"?>
<?variable-resolver class="org.zkoss.zkplus.cdi.DelegatingVariableResolver"?>
<zk>
   <window id="addCustomerWin" title="Register New Customer" border="normal"
       apply="${addCustomer}">
      <grid fixedLayout="true" width="450px">
         <rows>
	    <row>
	       <label value="Customer Name" />
		<textbox id="customerName" constraint="no empty" />
	    </row>
	    <row>
	        <label value="Date" />
		   <datebox id="date" constraint="no empty"/>
	      </row>
	      <row>
		 <button id="saveBtn" label="Save" />
		 <button id="cancelBtn" label="Cancel" onClick="addCustomerWin.detach()"/>
	       </row>
	     </rows>
       </grid>
   </window>
</zk>

After being invoked, the registerNewCustomer() method simply creates a new Window component using addCustomer.zul and makes it a modal dialog window. Figure 5 shows an example.

Figure 5. Register New Customer
Screen showing fields to enter a customer date and date as well as save and cancel buttons.

Similar to the index.zul page, the example uses the <?variable-resolver ?> directive and the DelegatingVariableResolver to apply the AddCustomer managed bean as a controller to the addCustomerWin window component. The Register Customer dialog is implemented as a grid with mandatory values for the Customer name and Date. The AddCustomer implementation is similar to the ManageCustomer class. The CustomerService bean is injected using the @Inject qualifier; the child components of addCustomerWin are also auto injected using the @Inject and @ComponentId qualifiers, as shown in Listing 10.

Listing 10. Add customer
@Named
@SessionScoped
public class AddCustomer extends GenericComposer implements Serializable {
   @Inject @SessionScoped CustomerService custSvc;
   @Inject @ComponentId("customerName") private transient Textbox customerName;
   @Inject @ComponentId("date") private transient Datebox date;
   @Inject @ComponentId("saveBtn") private transient Button saveBtn;
}

Like the registerNewCustomer() method in the ManageCustomer class, the example implements the saveNewCustomerDetails() observer method, as described in Listing 11.

Listing 11. Save new customer details
public void saveNewCustomerDetails(
   @Observes @Events("saveBtn.onClick") MouseEvent evt, @New Customer newCustomer)
      throws Exception {
   newCustomer.setName(customerName.getValue());
   java.util.Date utilDate = date.getValue();
   java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
   newCustomer.setDate(sqlDate);
   custSvc.addCustomer(newCustomer);
   Executions.getCurrent().sendRedirect("index.zul");
   evt.getTarget().getParent().detach();
}

Whenever the Save button (saveBtn) is clicked, the saveNewCustomerDetails() method is notified of the corresponding onClick MouseEvent. This method also has one additional parameter of type Customer. It is annotated with the @New qualifier because, per the CDI specification, a new instance of the Customer bean is auto injected in the parameter each time this method is observed.

Edit/(soft) Delete customer

The user can access the Edit Customer screen by selecting any customer row on the Manage Customer dashboard, as shown in Figure 6.

Figure 6. Edit Customer
Screen showing customer name, date, and the delete box checked.

Similar to the registerNewCustomer() method, the example defines the observer method editCustomer() that @Observes a ZK SelectEvent, which is sent whenever a listitem in listbox is selected.

Listing 12. Edit customer
public void editCustomer(
   @Observes @Events("customerList.onSelect") SelectEvent evt) throws Exception {
      Listitem selectedCustomer = customerList.getSelectedItem();
      String custId = ((Listcell) (selectedCustomer.getChildren().get(0))).getLabel();
      Map<String, String> args = new HashMap<String, String>();
      args.put("custId", custId);
      Window win2 = (Window) Executions.createComponents("editCustomer.zul", null, args);
      win2.doModal();
      win2.setTitle("Enter Customer Data");
      win2.setClosable(true);
      win2.setMaximizable(true);
}

Implementing the EditCustomer controller class for the editCustomerWin window component of editCustomer.zul is very similar to AddCustomer. To avoid repetition, it's not described in this article.

You can download the code for this application, the source of the EditCustomer class, and the editCustomer.zul file.


Summary

Join the Web development group on My developerWorks

Discuss topics and share resources with other developers about Web development in the My developerWorks Web development group.

Not a member of My developerWorks? Join now!

In this article, you explored ZK, an open source Ajax framework written in Java code, and JSR-299 Contexts and Dependency Injection. The ZK CDI extension has features that let you use CDI within the ZK programming model. You used a simple real-world example running on Apache Tomcat and connecting to a MySQL database.

The ZK framework has a rich set of components, a mark-up language, powerful development tools, and great documentation. It is an open source event-driven Ajax framework. CDI as defined by JSR-299 of Java EE platform 6 also provides a set of powerful features, such as type-safe dependency injection, an event notification model, and an SPI to develop portable extensions. The ZK CDI extension integrates the ZK programming model with CDI, allowing seamless development of Java EE 6 enterprise applications.


Download

DescriptionNameSize
Sample code for this articlezkManageCustomers.zip71 KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=491872
ArticleTitle=Explore the CDI programming model in ZK
publish-date=05252010