ZK Spring intergated Configurations

Configurations

Now lets start walking through the configurations:

/WEB-INF/zk.xml file: the ThreadLocal issue (IMPORTANT!)

The Spring Web Flow engine holds in the servlet thread serveral ThreadLocal variables for each request so the engine can refer it from time to time. These ThreadLocal variables contains important work flow related information and shall be accessiable any time. However, ZK by default spawns a new event thread for each event handling job. That is, the ZK event thread will not have such important ThreadLocal variables and the original assumption is broken.

Therefore, remember always to disable the ZK event thread mechanism entirly when use ZK with Spring. This tells the ZK framework NOT to spawn a new event thread for event handling and everyting shall back to it track. That is, configure the /WEB-INF/zk.xml file as follows:


<zk>
...
<system-config>
<disable-event-thread/>
</system-config>
...
</zk>

/WEB-INF/web.xml

web.xml is the standard servlet configuration file. To make ZK and Spring works, you have to configure this file.


/WEB-INF/web.xml
<web-app ...>
...
<!--
- Spring configurations
-->
<!-- The master configuration file -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/web-application-config.xml
</param-value>
</context-param>

<!-- Enables Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Loads the Spring web application context -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Serves static resource content from .jar files -->
<servlet>
<servlet-name>Resources Servlet</servlet-name>
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Resources Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>

<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/spring/*</url-pattern>
</servlet-mapping>

<!--
- ZK configurations
-->
<listener>
<description>
Used to cleanup when a session is destroyed</description>
<display-name>
ZK Session Cleaner</display-name>
<listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
</listener>

<!-- ZK loder -->
<servlet>
<description>
ZK loader for ZUML pages</description>
<servlet-name>zkLoader</servlet-name>
<servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
<init-param>
<param-name>update-uri</param-name>
<param-value>/zkau</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zul</url-pattern>
</servlet-mapping>

<!-- ZK update engine -->
<servlet>
<description>
The asynchronous update engine for ZK</description>
<servlet-name>auEngine</servlet-name>
<servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>auEngine</servlet-name>
<url-pattern>/zkau/*</url-pattern>
</servlet-mapping>

<!-- Default welcome files -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
  • Spring configuration:
    1. The <context-param> tells Spring Framework context loader where to find the context parameter files.
    2. The <listener> ContextLoaderListener defines the Spring Framework context loader that will load and parse the context parameter files.
    3. The <filter> springSecurityFilterChain defines the entry servlet filter for Spring Secuirty filter chains.
    4. The <servlet> Resources Servlet defines the spring resource servlet which can be used to retrieve resource defined in library .jar files.
    5. The <servlet> Spring MVC Dispatcher Servlet defines the entry servlet for Spring Web Flow. Any URL starts with /spring/* will go through the Spring Web Flow controller

    The most important configuration might be the Spring MVC Dispatcher Servlet. It tell the servlet container that all URL that starts with /spring/* shall be served by this servlet. It is a dispatcher servlet so we will need extra configurations for Spring MVC and Spring Web Flow to bridge the work flow requests to real Spring Web Flow controller.

  • ZK Ajax framework configuration:
    1. The <listener> HttpSessionListener is used to cleanup the session when it is destroyed.
    2. The <servlet> zkLoader servlet is used to load a ZK page
    3. The <servlet> auEngine servlet is used to update a ZK page (Ajax update)

    This is a typical ZK application configuration. Nothing very special.

/WEB-INF/config/webmvc-config.xml

webmvc-config.xml is the additional configuration file specific to Spring MVC.


/WEB-INF/config/webmvc-config.xml
<beans ...>
<!-- Maps request URIs to controllers -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/main=flowController
/booking=flowController
</value>
</property>
<property name="defaultHandler">
<!-- Selects view names to render based on the request URI: e.g. /main selects "main" -->
<bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
</property>
</bean>

<!-- Maps logical view names to real page URL (e.g. 'search' to '/WEB-INF/search.zul' -->
<bean id="viewResolver" class="org.zkoss.spring.web.servlet.view.ZkResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".zul"/>
</bean>
</beans>
  1. The <bean> SimpleUrlHandlerMapping works with Spring MVC will maps the request URI to real Controller. Here all the request URIs in the pattern /main and /booking would be dispatched to flowController(which is defined in webflow-config.xml configuration file. We will discuss it later).
  2. The <bean> viewResolver maps the logical view name returned by controller to real page URL. Note here the name viewResolver has to be given as is.

/WEB-INF/config/webflow-config.xml

webflow-config.xml is the configuration file specific to Spring Web Flow.


/WEB-INF/config/webflow-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:zksp="http://www.zkoss.org/2008/zkspring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
http://www.zkoss.org/2008/zkspring
http://www.zkoss.org/2008/zkspring/zkspring.xsd">

<!-- Executes flows: the central entry point into the Spring Web Flow system -->
<webflow:flow-executor id="flowExecutor">
<webflow:flow-execution-listeners>
<webflow:listener ref="jpaFlowExecutionListener" />
<webflow:listener ref="securityFlowExecutionListener" />
</webflow:flow-execution-listeners>
</webflow:flow-executor>
<!-- Installs a listener that manages JPA persistence contexts for flows that require them -->
<bean id="jpaFlowExecutionListener"
class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
<constructor-arg ref="entityManagerFactory" />
<constructor-arg ref="transactionManager" />
</bean>
<!-- Installs a listener to apply Spring Security authorities -->
<bean id="securityFlowExecutionListener"
class="org.springframework.webflow.security.SecurityFlowExecutionListener" />

<!-- The registry of executable flow definitions -->
<webflow:flow-registry id="flowRegistry"
flow-builder-services="zkFlowBuilderServices">
<webflow:flow-location path="/WEB-INF/flows/main/main.xml" />
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>


<!-- Configures the ZK Spring Web Flow integration -->
<zksp:flow-controller id="flowController" flow-executor="flowExecutor"/>
<zksp:flow-builder-services id="zkFlowBuilderServices" />
</beans>
  • Spring <webflow:> configuration(in red):
    1. The flowExecutor bean is the central entry point into the Spring Web Flow system. It adds two work flow listeners to handle the JPA data persistence and web flow security protection.
    2. The flowRegistry bean is the registry of work flow definitions. It tells the Spring Web Flow system where to find the flow definition file. Also, it specifies which flow-builder-service to use for these work flow definitions. Note here it uses zkFlowBuilderServices.
  • ZK Spring <zksp:> configuration(in blue):
    1. The flowController bean is the bridge between Spring MVC and ZK Spring Web Flow (flowExecutor). Note the name flowController has to be given as is.
    2. The zkFlowBuilderServices bean is where those real web flow services are (expression parsing, view resolving, type conversion, etc.)

    ZK adopts the Spring namespace configuration mechanism so all you need to do to enable the ZK Spring Web Flow Integration is by specifying these two beans.

Define a work flow

After finishing application configuration, it is time to define the work flow.


/WEB-INF/flows/main/main.xml
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">

<var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" />

<view-state id="enterSearchCriteria">
<on-render>
<evaluate expression="bookingService.findBookings(currentUser.name)" result="viewScope.bookings" />
</on-render>
<transition on="search" to="reviewHotels">
<evaluate expression="searchCriteria.resetPage()" />
</transition>
<transition on="cancelBooking">
<evaluate expression="bookingService.cancelBooking(componentScope.booking)" />
<render fragments="bookingsFragment" />
</transition>
</view-state>

<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>
<transition on="sort">
<set name="searchCriteria.sortBy" value="requestParameters.sortBy" />
<render fragments="searchResultsFragment" />
</transition>
<transition on="previous">
<evaluate expression="searchCriteria.previousPage()" />
<render fragments="searchResultsFragment" />
</transition>
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
<render fragments="searchResultsFragment" />
</transition>
<transition on="select" to="reviewHotel">
<set name="flowScope.hotel" value="self.attributes.hotel" />
</transition>
<transition on="changeSearch" to="changeSearchCriteria" />
</view-state>

<view-state id="reviewHotel">
<transition on="book" to="bookHotel" />
<transition on="cancel" to="enterSearchCriteria" />
</view-state>

<subflow-state id="bookHotel" subflow="booking">
<input name="hotelId" value="hotel.id" />
<transition on="bookingConfirmed" to="finish" />
<transition on="bookingCancelled" to="enterSearchCriteria" />
</subflow-state>

<view-state id="changeSearchCriteria" view="enterSearchCriteria" popup="true">
<on-entry>
<render fragments="hotelSearchFragment" />
</on-entry>
<transition on="search" to="reviewHotels">
<evaluate expression="searchCriteria.resetPage()"/>
</transition>
</view-state>

<end-state id="finish" />

</flow>

A work flow is defined in a separate XML configuration file. A work flow basically is composed of states. Inside each state, we can defined transition to tell the flow engine which state to go on some predefined action event. Also, you can do some evaluate in the life cycle of the work flow. However, I am not going to explain every tag used in this work flow definition file. I will focus on those tags that related to the mapping web pages. If you need to know more details, you can download the spring-webflow-reference.pdf from the Spring website.

  • <view-state>: define the view state. Each view-state is mapped to a real web page. The view resolver configured in zkFlowBuilderServices bean(configured in webflow-config.xml) is used to resolve the view-state id into a Spring MVC view name
  • <transition>: define the transition condition. The attribute on="action-event" tells when to trigger a flow transition, and the optional attribute to="target-state" tells which target state to go when the specified transition action event is fired. If attribute to is omitted, flow engine will keep in this state. In such case, there shall be generally at least an <evaluate> tag is specified. As for how to fire a transition action event in the zul page, I will explain later with an example zul page.
  • <evaluate:>: define the evaluation expression. Each <evaluate> tag request to evaluate the specified expression as defined in attribute expression. These expression can be evaluated as the Unified Epression Language(EL). Note that you can use all ZK implicit objects and accessible variables here including self, event, desktop, page, and so on. That is, you can write ZK page controll code in the flow definition file if you like.

Define an associated view-state page

Here we discuss the associatation between a zul page and a view state.


/WEB-INF/flows/main/enterSearchCriteria.zul
<?page title="ZK Spring: Hotel Booking Sample Application" complete="true" ?>
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/WEB-INF/layouts/standard.zul"?>
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" arg0="./hotelSearch"?>
<?variable-resolver class="org.zkoss.spring.DelegatingVariableResolver"?>
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" ?>
<zk:zk xmlns="http://www.zkoss.org/2005/zk/native"
xmlns:zul="http://www.zkoss.org/2005/zul"
xmlns:zk="http://www.zkoss.org/2005/zk">
<zul:div id="hotelSearch" class="section" self="@{define(content) fragment(hotelSearchFragment)}">
<span class="errors">
<!-- <h:messages globalOnly="true" /> -->
</span>
<h2>Search Hotels</h2>
<form id="mainForm">
<fieldset>
<div class="field">
<div class="label">
Search String:
</div>
<div class="input">
<zul:textbox id="searchString" value="@{searchCriteria.searchString}"
tooltiptext="Search hotels by name, address, city, or zip."/>
</div>
</div>
<div class="field">
<div class="label">
Maximum results:
</div>
<div class="input">
<zul:listbox id="pageSize" rows="1" mold="select" selectedItem="@{searchCriteria.pageSize}">
<zul:listitem forEach="" value="0" label=""/>
</zul:listbox>
</div>
</div>
<div class="buttonGroup">
<zul:button self="@{action(search)}" label="Find Hotels" onClick=""/>
</div>
</fieldset>
</form>
</zul:div>
...
</zk:zk>

Per the file name, you might have guessed this example view state page is associated to the view state enterSearchCriteria. Spring Work Flow maps a view state to a view state page URL by name convention. It is a combination of the view state id and the URL path of the work flow definition file.

Following is some important notes regarding a ZK view state page:

  • The <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ...?> is required. ZK Spring Web Flow Integration assumes it. Basically, any settable work flow variables shall be specified as annotated data binding. E.g. the @{searchCriteria.searchString} and the @{searchCriteria.pageSize} in this example page.
  • The <?variable-resolver class="org.zkoss.spring.DelegatingVariableResolver"?> is a must. ZK rendering engine and data binder use this resolver to resove work flow variables defined in the work flow configuration file.
  • The <zul:button self="@{action(search)}" .../> is how you trigger a view state transition. Simply specify a component action annotation in the form of @{action(action-event)} and you are done; where the action-event is the transition action event name. That is, in this example, when an end user click this button, it tells Spring Web Flow engine that a transition action event search is fired for the current view state. Per the transition definition, <transition on="search" to="reviewHotels">, the Spring Web Flow engine will transit view state page from current enterSearchCriteria view state to the reviewHotels view state.

Summary

The Spring Web Flow apparently is designed with page based navigation in mind. It provides some minor Ajax effects (e.g. Popup and Fragment) and that is all. Then what role can a rich Ajax framework like ZK play? A good practice might be that doing fine grain Ajax operations in a page with ZK event programming whilst doing coarse work flow page transitions with Spring Web Flow definition. However, you can also define <transition> without the to attribute and make it doing <evalute> only so it would work like a pure ZK event listener. It is all up to you and your applications' requirement.

We now have made Spring Web Flow work with ZK. The next step shall be focused on enabling injection of ZK components into Spring beans. Integrating different frameworks together has not been an easy job and I might miss something. We welcome your feedback and suggestions so we can make ZK Spring Integration better.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值