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.
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 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).
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
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
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
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
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.
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:
- 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>
- 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> - 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.
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.
Menubaris a ZK menubar component with two child ZKmenuitemcomponents with the IDsaddandexit. - The table listing the customers currently in the database. The
table in the example is a ZK
listboxcomponent whose ID iscustomerList. The table column headers are defined bylistheaderchild components of thecustomerListlistbox.
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.
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
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.
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
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.
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code for this article | zkManageCustomers.zip | 71 KB | HTTP |
Information about download methods
Learn
- "Rich Internet applications using ZK: An open source Ajax
framework" (developerWorks, Jan 2010) introduces you to ZK and
gives you a real-world example of its use running on Apache Tomcat and
connecting to a MySQL database.
- Get a tour, demo, and download of ZK Open source Ajax.
- Read the ZK Developer's
Guide for information about fundamentals, the ZUL component, and
ZK in depth.
- View live demos of applications using
ZK (ZK Explorer, ZK SpreadSheet, ZK Calendar, and more).
- Get all the ZK Studio
documentation: Getting Started, Developer's Guides, and Webinars.
- Learn all about JSR-299: Contexts and
Dependency Injection for the Java™ EE platform.
- Read Getting
started with ZK CDI for help setting up your development
environment.
- Read about Weld, the reference
implementation of the JSR-299 specification that defines CDI.
- Read "Dependency injection with Guice" (developerWorks, December 2008) to take a tour of Guice, Google's open source dependency injection framework for Java development.
- "Introduction to Spring 2 and JPA" (developerWorks, August 2006) is an older, but still useful, discussion on the Spring framework and DI concepts.
- "Java EE 5: Power and productivity with less complexity" (developerWorks, November 2007) gives you a quick-and-dirty tour of JEE 5, including DI details.
- To learn more about setting up Weld with
other application servers and environments, see "Community Documentation"
Chapter 18: Application servers and environments supported by
Weld.
Get products and technologies
- Download IBM product
evaluation versions or explore
the online trials in the IBM SOA Sandbox and get your hands on
application development tools and middleware products from DB2®,
Lotus®,
Rational®, Tivoli®, and WebSphere®.
Discuss
- developerWorks blogs: Check out developerWorks blogs and get
involved in the developerWorks community.






