How to Integrate ZK with Seam
Dennis Chen, Engineer, Potix Corporation
Aug. 2, 2007
Applicable to zk-2.5.0-FL-2007-08-01 and later.
- Applicable to Seam 1.2.1.
Introduction
JBoss Seam is a powerful application framework for build web applications by unifying and integrating JSF, Ajax4jsf, EJB3 etc. Although Seam is powerful, but it is binding the presentation layout with JSF and Ajax4jsf.
Now, we provide a way to integrate ZK with Seam, so you can use ZK component to cooperate with Seam's context includes EJBs and JavaBeans. In this article, we will show you a "SayHello" example by Seam first, and then we will show you two examples which base "SayHello" but use ZK to replace JSF.
The examples in ZK are highly depend on ZK's Data Binding mechanism. You can learn more about Data Binding at ZK Developer's Reference: Data Binding and new feature of annotation for Data Binding in ZK 2.4 here
Demo Installation
We refer a simple CRUD(Create, Read, Update and Delete) example 'SayHello' which is contained in the book "Simplicity and Power Beyond Java? EE" for demonstration. You can download the original example code at here(tomcatejb3 folder in zip).
To run demos in this article, just download the zk-Seam-demo zip file, extract out the zkseam-demo\zkseam-demo.war and deploy it to tomcat.
There are 3 start link for the examples:
- http://localhost:8080/zkseam-demo/hello.seam, for Seam Example.
- http://localhost:8080/zkseam-demo/part2/hello.zul , for ZK's Example "Using bean context and navigation rule of Seam".
- http://localhost:8080/zkseam-Demo/part3/hello.zul , for ZK's Example "Using bean context of Seam only".
A CRUD Example "Say Hello" of Seam
This demo contains 3 JSF pages, hello.xhtml, fans.xhtml and person.xhtml. The page navigation flow likes below:
In hello.xhtml contains a input form for person data input and validation. You can submit your information and the page will navigate to fans.xhtml.
In fans.xhtml contains a data table to list all person, you can delete person directly in this page , and you can edit person by click edit link.
In person.xhtml you can edit and update the person information.
How to Integrate ZK with Seam
In this session, we show you how to config for integration, then we demonstrate two examples "Using bean context and navigation rule of Seam" and "Using bean context of Seam only".
Configuration
To integrate ZK with seam, you must download and install ZK (reference to ZK Tutorial ). After that, append following configuration on web.xml,zk.xml, navigation.xml and pages.xml in WEB-INF folder
- web.xml : register two filters to initiate Seam's context when zk loading and ui update.
<filter>
<filter-name>Seam Context Filter</filter-name>
<filter-class>org.jboss.seam.web.ContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Seam Context Filter</filter-name>
<servlet-name>zkLoader</servlet-name>
</filter-mapping>
filter-mapping>
<filter-name>Seam Context Filter</filter-name>
<servlet-name>auEngine</servlet-name>
</filter-mapping>
<filter>
<filter-name>ZK FacesContext Filter</filter-name>
<filter-class>org.zkoss.seam.HttpFacesContextFilter</filter-class>
<init-param>
<param-name>update-uri</param-name>
<param-value>/zkau</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ZK FacesContext Filter</filter-name>
<servlet-name>zkLoader</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>ZK FacesContext Filter</filter-name>
<servlet-name>auEngine</servlet-name>
</filter-mapping>
- zk.xml : add Thread Local Synchronizer to synchronize context of Seam.
<listener>
<description>ThreadLocal Variables Synchronizer</description>
<listener-class>org.zkoss.zkplus.util.ThreadLocalListener</listener-class>
</listener>
<preference>
<name>ThreadLocal</name>
<value>
org.jboss.seam.contexts.Contexts=applicationContext,methodContext,
eventContext,pageContext,sessionContext,conversationContext,businessProcessContext;
javax.faces.context.FacesContext=_currentInstance;
</value>
</preference>
- navigation.xml : add navigation rule (for first integration example only)
<navigation-rule>
<from-view-id>/part2/*</from-view-id>
<navigation-case>
<from-outcome>fans</from-outcome>
<to-view-id>/part2/fans.zul</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>hello</from-outcome>
<to-view-id>/part2/hello.zul</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
- pages.xml : add Param for person.zul page initiation (for first integration example only)
<page view-id="/part2/person.zul">
<param name="pid" value="#{manager.pid}" converterId="javax.faces.Long"/>
</page>
In Part 2, we replace the UI layer from JSF to ZK. There are 3 zul pages which reuse Seam's context beans and page navigation rule by some utility class. All functions are same as "Say Hello" example in Seam.
- part2/hello.zul
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?variable-resolver class="org.zkoss.seam.DelegatingVariableResolver"?>
<zk >
<window title="DataModel, Validation and Restful Page Demo" border="normal"
width="400px">
<zscript>import org.zkoss.seam.*</zscript>
<grid>
<rows>
<row>Your Name:
<textbox onBindingSave="ValidationUtil.validate(event)"
value="@{bind(value=person.name,save-when='btn.onClick')}"/>
</row>
....
</rows>
</grid>
<button label="Say Hello" id="btn" onClick="doSayHello()"
onBindingValidate="ValidationUtil.afterValidate(event)"/>
<button label="See Fans" href="fans.zul"/>
<zscript>
void doSayHello(){
NavigationUtil.navigate(manager.sayHello());
}
</zscript>
</window>
</zk>
1. Add <?variable-resolver class="org.zkoss.seam.DelegatingVariableResolver"?> to obtain variable from Seam's context. Such as variable "person" will be obtained from Seam's context.
2. Add onBindingSave = "ValidationUtil.validate(event)" in component to handle validation and add onBindingValidate = "ValidationUtil.afterValidate(event)" in trigger button to handle the logic after validation.
3. Call manager.sayHello() to insert data. "manager" is obtain by resolver from Seam's context, too.
4. Use NavigationUtil.navigate(pageid:String) to process navigation rules of JSF.
- part2/fans.zul
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?variable-resolver class="org.zkoss.seam.DelegatingVariableResolver"?>
<zk >
<window title="The ZK and Seam Fans" border="normal"
width="600px">
<zscript>import org.zkoss.seam.*;import org.jboss.seam.*;</zscript>
The following persons have said "hello" to ZK and Seam:
<grid model="@{bind(fans)}" id="g1" >
....
<rows>
<row self="@{bind(each='rperson')}" value="@{bind(rperson)}">
<label value="@{bind(rperson.name)}"/>
....
<button label="edit" onClick="doEdit(self.parent.value)"/>
<button label="delete" onClick="doDelete()" />
</row>
</rows>
</grid>
<separator/>
<button label="Goto Hello" onClick="gotoHello()"/>
<zscript>
void gotoHello(){
NavigationUtil.navigate("hello");
}
void doDelete(){
if(DataModelUtil.select("fans","selectedFan",self.parent.value)){
NavigationUtil.navigate(manager.delete());
g1.model = fans;
}else{
alert("some thing wrong!!");
}
}
void doEdit(selected){
selectedFan = selected;
Executions.getCurrent().sendRedirect("person.zul?pid="+selectedFan.id);
}
</zscript>
</window>
</zk>
1. Bind model of grid by variable "fans" from Seam's context.
2. Use DataModelUtil.select(dataModel:String,dataModelSelection:String,selected:Object) to inject selected person into Seam's context. After that, We call manager.delete() to delete selected person.
3. Update the model of grid(g1) since a new model is created after deletion.
- part2/person.zul
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?variable-resolver class="org.zkoss.seam.DelegatingVariableResolver"?>
<zk >
<window title="Edit " border="normal"
width="400px">
<zscript>import org.zkoss.seam.*</zscript>
<textbox visible="false" value="@{bind(value=person.id,save-when='btn.onClick')}"/>
<grid>
<rows>
<row>Your Name:
<textbox onBindingSave="ValidationUtil.validate(event)"
value="@{bind(value=person.name,save-when='btn.onClick',converter='org.zkoss.seam.Validator')}"/>
</row>
....
</rows>
</grid>
<button label="Save" id="btn" onClick="doUpdate()"
onBindingValidate="ValidationUtil.afterValidate(event)"/>
<zscript>
void doUpdate(){
NavigationUtil.navigate(manager.update());
}
</zscript>
</window>
</zk>
1. Because we assign a page initial action of Seam in pages.xml, so when a request to "/part2/person.zul" with request parameter "pid" will cause event invoking manager.setPid(). After this action, we can get "person" variable which is prepared by manager.setPid() from Seam's context.
2. We must bind person.id to a hidden field(use <textbox visible="false"> to instead) to retrieve data(id) back.
3. When call to manager.update(), a variable "person" will be update into database.
Live Demo
Using bean context of Seam only
In Part 3, we not only replace the UI layer but also eliminate the navigation rule. Without tied with the navigation rule we can put all CRUD operation in just one page.
- part3/hello.zul
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?variable-resolver class="org.zkoss.seam.DelegatingVariableResolver"?>
<zk >
<hbox onCreate="refreshW1()">
<zscript>import org.zkoss.seam.*;import org.jboss.seam.*;</zscript>
<zscript>
Person selectedPerson;
</zscript>
<window border="normal" width="300px" id="w1">
<textbox visible="false" value="@{bind(value=person.id,save-when='triggerBtn.onClick'}"/>
<grid>
<rows>
<row>Your Name:
<textbox onBindingSave="ValidationUtil.validate(event)"
value="@{bind(value=person.name,save-when='triggerBtn.onClick')}"/></row>
....
</rows>
</grid>
<button label="Say Hello" id="btn" onClick="Events.sendEvent(new Event(Events.ON_CLICK,triggerBtn));doSayHello()" />
<button label="Update" id="updateBtn" onClick="Events.sendEvent(new Event(Events.ON_CLICK,triggerBtn));doUpdate()" />
<button label="Delete" id="deleteBtn" onClick="Events.sendEvent(new Event(Events.ON_CLICK,triggerBtn));doDelete()" />
<button label="Cancel" id="cancelBtn" onClick="doClear()"/>
<button visible="false" id="triggerBtn" onBindingValidate="ValidationUtil.afterValidate(event)"/>
</window>
<window title="The following persons have said 'hello' to Zk and Seam:" border="normal" width="550px" id="w2">
<listbox id="g1" onSelect="listSelected(g1.selectedItem)"
model="@{bind(fans)}" selectedItem="@{bind(selectedPerson)}">
....
<listitem self="@{bind(each='lperson')}">
<listcell label="@{bind(lperson.name)}"/>
....
</listitem>
</listbox>
</window>
<zscript>
void listSelected(li){
ContextUtil.updateToContext("person",selectedPerson);
binder.loadComponent(w1);
refreshW1();
}
void doSayHello(){
manager.sayHello();
binder.loadComponent(w2);
doClear();
}
void doUpdate(){
manager.update();
binder.loadComponent(w2);
doClear();
}
void doDelete(){
if(DataModelUtil.select("fans","selectedFan",person)){
manager.delete();
binder.loadComponent(w2);
doClear();
}else{
alert("some thing wrong!!");
}
}
void doClear(){
ContextUtil.updateToContext("person",new Person());
selectedPerson = null;
binder.loadComponent(w1);
refreshW1();
}
void refreshW1(){
if(person.id==0){
w1.getFellow("btn").setVisible(true);
....
}else{
w1.getFellow("btn").setVisible(false);
....
}
}
</zscript>
</hbox>
</zk>
- We use a tirggerBtn to proxy save-when event to multiple buttons(btn, updateBtn and deleteBtn). And those buttons must send a onClick Event to tirggerBtn to notify Data Binding to process.
- When listSelected(), we put selectedPerson into to Seam's context by ContextUtil.updateToContext(name:String,bean:Object). After that, we can reload the window by Data Binding.
- When "Say Hello" button is clicked, Data Binding is triggered by the Events.sendEvent(new Event(Events.ON_CLICK,triggerBtn)) and will bind data into person which is obtained from Seam's context. After that, doSayHello() just call manager.sayHello() to insert data
- When "Update" button is clicked, Data Binding is triggered by the Events.sendEvent(new Event(Events.ON_CLICK,triggerBtn)) and will bind data into person which is obtained from Seam's context. After that, doUpdate() just call manager.update() to update data.
- When "Delete" button is clicked, Data Binding is triggered by the Events.sendEvent(new Event(Events.ON_CLICK,triggerBtn)) and will bind data into person which is obtained from Seam's context. After that, in doDelete() we use DataModelUtil.select(dataModel:String,dataModelSelection:String,selected:Object) to inject person into Seam's context for DataModelSelection, and then just call manager.delete() to delete data.
- When doClear(), we put a new Person() into to Seam's context by ContextUtil.updateToContext(name:String,bean:Object). After that, We can reload the window by Data Binding.
Live Demo
Download
Summary
JBoss Seam unify serevel technologies to provide a powerful application framework on web. In this article, we demonstrate you how to integrate ZK with Seam by some utilities of ZK. You now have more choices on developing your application by ZK or Seam. We hope you enjoy this new feature and give us feedbacks so we can do things better.
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |