Integration with Other Frameworks
editSpring
edit1. Add Spring framework jar and all its dependencies to the classpath of your server. (See documentation at http://www.springframework.org for details.)
2. Add the following to your web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
3. Put the following applicationContext.xml file into your WEB-INF directory:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="myBusinessServiceSpringBean" class="test.BusinessServiceImpl"/> </beans>
4. Example of test.BusinessServiceImpl and test.BusinessService:
package test; import java.util.*; public class BusinessServiceImpl implements BusinessService { public List getElementsList() { // some logic here return Arrays.asList(new String[] {"abc", "def"}); } }
package test; public interface BusinessService { java.util.List getElementsList(); }
5. Compile and deploy.
6. And finally a ZUL file example which calls methods of Spring-managed BusinessService:
<?xml version="1.0" encoding="windows-1251"?> <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?> <window> <grid> <rows> <row forEach="${myBusinessServiceSpringBean.elementsList}"> <label value="${each}"/> </row> </rows> </grid> </window>
where the forEach loop is looping over the collection returned by a call to the spring bean method and the ${each} attribute causes toString to be called on each object in the collection. You can also use zscript to directly work with your spring bean. This code does the same as the above
<?xml version="1.0" encoding="windows-1251"?> <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?> <zscript> List someData = myBusinessServiceSpringBean.getElementsList(); </zscript <window> <grid> <rows> <row forEach="${someData}"> <label value="${each}"/> </row> </rows> </grid> </window>
If you choose to use the MVC pattern (see Composer and GenericAutowireComposer examples in this wiki) then you can have Spring create your model and controller beans. Consider the following applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="myModel" scope="session" class="com.acme.MyModelImpl" /> <bean id="myController" scope="prototype" class="com.acme.MyControllerImpl"> <constructor-arg ref="myModel" /> </bean> </beans>
Here we tell spring to create a single com.acme.MyModelImpl object for a given user HttpSession but to create a new com.acme.MyControllerImpl bean for every application look-up. Each "myController" bean is created using a constructor that is passed the "myModel" bean scoped to the HttpSession. We need to activate the RequestContextListener in the web.xml for session scoping to work:
<listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener>
Now within any given page we can cause ZK to ask Spring for a new myController bean to use as the controller backing a given window object:
<?xml version="1.0" encoding="UTF-8"?> <zk xmlns="http://www.zkoss.org/2005/zul"> <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?> <window id="mainWindow" apply="${myController}"> <textbox forward="onChange=mainWindow.onUserEntersDiscountCouponNumber"/> </window>
The above code uses the DelegatingVariableResolver. It resolves the myController reference in the "apply" attribute via spring (see the MVC and 'apply' attribute documentation). Spring creates a new object on demand wired to the session scoped myModel bean. The "forward" attribute will delegate the textbox onChange event to the mainWindow's custom "onUserEntersDiscountCouponNumber" event handler. There is no such event handler on the Window component class however the object specified in the "apply" attribute can add one to the window object that it is applied to. The simplest way to do this is for the com.acme.MyControllerImpl controller class to subclass GenericAutowireComposer and provide an event handler "public void onUserEntersDiscountCouponNumber(Event event) throws Exception". The logic in the superclass will then automatically add a event handler to the mainWindow object that calls the method you provide. See the MVC examples her on this wiki for further details and the MVC SmallTalks published at http://www.zkoss.org
Struts + Tiles + JSP (+ Spring)
editN.B. A lighter approach than what follows here could be to turn on the ZKFilter and have it post process the URLs of your actions. That works only if you JSPs output pure XHTML (use jTidy -asxml to convert html to xhtml then put CDATA sections around any script source code within the page).
N.B. With ZK3.0 consider using the ZK custom tag lib that lets you write ZK components into the page using a tag lib.
ZK provides a very rich set of features. You can use it to build an entire single page Ajax driven rich web application without using any web programming at all. Many companies however have significant investments in existing J2EE web technologies like Struts, Tiles and JSP. These companies may wish to leverage their existing investments by starting out with ZK XUL within a central tile on those pages that would benefit from XUL components. This section outlines one way of doing this. You need to be familiar with the support for Struts within Spring to be able to follow this example.
As this How-To covers ZK, Struts and Spring you need to specify them in your web.xml (please note that the zkFilter is not loaded as Tiles will cause zkLoader to render pure zul):
<!-- SECTION FOR SPRING --> <listener> <display-name>Spring Context Loader</display-name> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- SECTION FOR ZK --> <listener> <description>Used to cleanup when a session isdestroyed</description> <display-name>ZK Session Cleaner</display-name> <listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class> </listener> <servlet> <description>ZK loader for evaluating ZK pages</description> <servlet-name>zkLoader</servlet-name> <servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class> <!-- Specifies URI of the update engine(DHtmlUpdateServlet). --> <init-param> <param-name>update-uri</param-name> <param-value>/zkau</param-value> </init-param> <load-on-startup>1</load-on-startup><!-- MUST --> </servlet> <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>*.zul</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>*.zhtml</url-pattern> </servlet-mapping> <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> <!-- SECTION FOR STRUTS --> <servlet> <servlet-name>The Struts Engine</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts/struts-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
Create your struts configuration file struts-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <form-beans> <!-- This Form Bean will hold the HTTP POST data sent from our /search.jsp page --> <form-bean name="SearchStuff" type="com.yourdomain.web.StuffSearchForm"></form-bean> </form-beans> <action-mappings> <!-- This Action Mapping delegates to Spring to find a Bean called SearchStuff --> <action path="/SearchStuff" name="SearchStuff" type="org.springframework.web.struts.DelegatingActionProxy" validate="false" scope="request"> <forward name="success" path="/search-results-tile.jsp"/> <forward name="errors" path="/search-errors-tile.jsp"/> </action> </action-mappings> <controller processorClass="org.apache.struts.action.RequestProcessor"></controller> <!-- Message Resources --> <message-resources parameter="ApplicationResources" /> <!-- Spring support for Struts --> <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/spring/applicationContext.xml" /> </plug-in> </struts-config>
Then in your Spring configuration file applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- Struts will delegate onto this custom struts Action Class --> <bean name="/SearchStuff" class="com.yourdomain.web.StuffSearchAction"> <property name="searchDelegate"> <ref bean="searchDelegate" /> </property> </bean> <!-- The Action Class needs a Business Delegate --> <bean id="searchDelegate" class="com.yourdomain.business.SearchDelegateImpl"> <constructor-arg><ref bean="pojoSessionFacade"/></constructor-arg> </bean> <!-- The Business Delegate needs a Pojo Facade that implements a package of use cases --> <bean id="pojoSessionFacade" class="com.yourdomain.business.PojoSessionFacadeImpl"> <constructor-arg>...</constructor-arg> <constructor-arg>...</constructor-arg> </bean> <!-- The pojo sesion facade should be wrapped in a spring transaction interceptor. Working open source code examples are at this link http://www.manning.com/crichardson/. The code is Apache licienced and will compile, build and unit test a working pojo Session Facade using either JDO, Hibernate or iBATIS sql maps. See his applicationContext.xml for details. --> ... </beans>
Note that in the Struts configuration we have two action mappings for "success" or "errors". Success is mapped to search-results-tile.jsp which contains
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <tiles:insert page="site-layout.jsp" flush="true"> <tiles:put name="body-content" value="/search-results.zul" /> </tiles:insert>
This basically says that this page looks like site-layout but with search-results.zul in the middle of the page. This is because in site-layout.jsp there is:
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles"%> <html> <head>...</head> <body> <div class="main"> <div class="banner"> <!-- lots of html --> .... </div> <div class="left-menu"> <!-- The site menu could be a JSP using JSTL and custom tags --> <tiles:insert attribute="site-menu" /> </div> <div class="main-panel"> <!-- The main panel could be either a .jsp or a .zul--> <tiles:insert attribute="body-content" /> </div> ... </div> </body> </html>
With that information we see that in the specific search-results-tile.jsp we stated that it is based on site-layout but with search-results.zul in the body-content position.
Next we need to ensure that the Struts Action is passing results via the HTTP Session to search-results.zul. To do this the Struts Action can do something like:
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { StuffSearchForm stuffSearchForm = (StuffSearchForm)form; ActionErrors errors = stuffSearchForm.validate(mapping, request); if(!errors.isEmpty()){ saveErrors(request, errors); return mapping.findForward("errors"); } // Business logic. The protected field this.searchDelegate is an interface type. The concrete // instance was configured by Spring and injected when this object was constructed. List searchResultsList = this.searchDelegate.searchStuff(stuffSearchForm.getStuffSearchCriteria()); // Put the list where ZUL can get at it in the web session request.getSession().setAttribute("searchResults", searchResultsList); return mapping.findForward("success"); }
Note that this.searchDelegate will be set by Spring as defined within the applicationContext.xml
So we now know that our Struts class will put search results list the Http Session where our search-results.zul can locate it:
<window> <zscript><![CDATA[ /** * Here we pick up the search results that were set by our Struts Action class. */ List iterateOverMe = sessionScope.get("searchResults"); ]]></zscript> <tabbox> <tabs> <!-- Iterate over our List once to draw the tabpanel tabs. --> <tab forEach="${iterateOverMe}" label="${each.searchResultLabel}"/> </tabs> <tabpanels> <!-- Iterate over our Collection again to draw the tabpanel panels. --> <tabpanel forEach="${iterateOverMe}"> <!-- Here you put whatever ZUL that you need to render your search result items using ${each}. See other examples in this wiki. --> ... </tabpanel> </tabpanels> </tabbox> </window>
You should be aware however that letting Struts do the search for us our server is doing a lot more stressful page rebuild work than is necessary. A more AJAX centric approach is to write a single ZUL page that first presents the user with a search input form. Then in zscript event handlers run the search and update the ZK destop with the search results. Only the html for the search results will then be sent back to the browser on the AJAX response. An pattern to achieve this is to move the code that was originally within a subclass of Struts Action into a subclass of org.zkoss.zul.Window then render the search results within the zscript event handler:
<?page id="main-page"?> <!-- NOTE THAT WE SPECIFY OUR OWN CUSTOM BUSINESS DELEGATE SUBCLASS OF WINDOW HERE! --> <window use="com.yourdomain.zul.SearchWindow" id="SearchFacadeDelegate"> <zscript> void doSearch(){ businessFacadeDelegate = Path.getComponent('/SearchFacadeDelegate'); businessFacadeDelegate.setSearchNameCriteria(name_criteria.value); searchResultsPlaceHolder.src = "render-search-results.zul"; // The file render-search-results.zul should have the delegate run // the actual search and render the results } </zscript> <label value="Search By Name:"/><textbox name="name_criteria"/> <button label="Search"> <attribute name="onClick"> doSearch(); </attribute> </button> <include id="searchResultsPlaceHolder" src=""/> </window>
Note - using Tiles the ZUL page might note notice that it is being included and it might write <html> and <head> tags into the page. To avoid this rather than having tiles include a raw zul page include a jsp page that simply JSP includes the zul page which will behave well.
JasperReport
editsee this Small Talk for an introduction.
Hibernate
editZK Hibernate support without Spring: Hibernate Session handling. Preliminary!
The ZK team has provided since version 2.1.1 a set of classes to support the Hibernate without Spring case. The details is explained by Henri Chen in his Hibernate + ZK article.
Acegi Security for Spring
editsee this Small Talk for an introduction
There is also another small talk written by Hari Gangadharan - Making Acegi work with ZK
ZK 2.x.x + Spring 2.x.x + Hibernate 3.x + JUnit Test
editby Marcos de Sousa (Maputo - Mozambique)
01/06/2007
The motivation behind this entry is to provide a simple step-by-step guide for integrate ZK Framework 2.x.x + Spring Framework 2.x.x + Hibernate 3.x. I will show you how things is done in real j2ee world application.
A. Initial Setup:
1. Download the ZK Framework JARs and add to your classpath: zk.jar, zul.jar, zhtml.jar, zkplus.jar, zweb.jar, zcommon.jar, bsh.jar, jcommon.jar, commons-el.jar, commons-fileupload.jar, commons-io.jar, Filters.jar
2. Download Spring Framework and add to your classpath: spring.jar, spring-mock.jar
3. Download Hibernate 3.x.x and Hibernate Annotation 3.x.x then add to your classpath: antlr-2.x.x.jar, asm.jar, asm-attrs.jar, cglib-2.x.x.jar, commons-collections 2.x.x.jar, commons-logging-1.x.x.jar, dom4j-1.x.x.jar, ejb3-persistence.jar, hibernate3.jar, hibernate-annotations.jar, jdbc2_0-stdext.jar, jta.jar, xerces-2.x.x, xml-apis.jar
4. Add the JAR containing your database driver (I am using SQL Server sqljdbc.jar in the example, but only minor changes are necessary to adapt for another database).
5. Add the JAR junit-3.x.x.jar for Unit Test.
B. Code - Domain Model:
This example will be based on a purposefully simple domain model of just 2 classes. Notice the use of annotations. One may choose to use either annotations or an XML file to specify the object-relational mapping metadata - or even a combination of both approaches. Here, I have chosen to use solely annotations - for which brief descriptions will be provided immediately following the domain model code listings.
First, the Employee class:
package com.wikibook.persistence; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @SuppressWarnings("serial") @Entity public class Employee implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String fullname; private String email; private String username; private String password; @ManyToOne @JoinColumn(name = "role") private Role role; public Employee() { } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFullname() { return fullname; } public void setFullname(String fullname) { this.fullname = fullname; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Role getRole() { return role; } public void setRole(Role role) { this.role = role; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
Second, the Role class:
package com.wikibook.persistence; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; @SuppressWarnings("serial") @Entity public class Role implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @OneToMany(cascade = CascadeType.ALL, mappedBy = "role") private Set<Employee> employees = new HashSet<Employee>(); public Role() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Employee> getEmployees() { return employees; } public void setEmployees(Set<Employee> employees) { this.employees = employees; } public void addEmployee(Employee employee) { this.employees.add(employee); } }
In the Employee class, there is a @ManyToOne annotation to describe the relationship to an Role. Look cascade rules. For example, when a Role is deleted, the associated Employees will also be deleted.
C. Code - Data Access Object Layer:
For accessing instances of the domain model, it is best to create a generic interface that hides all details about the underlying persistence mechanism. That way, if switching later to something other than Hibernate Annotation, there will be no impact on the architecture, with minor changes are necessary to adapt to migrate to JPA. This also makes it easier to test the service layer, since it enables the creation of stub implementations of this data access interface - or even dynamic mock implementations.
Here is the interface. Notice that there are no dependencies on any Hibernate Annotation or Spring classes. In fact, the only dependencies here that are not core Java classes are the classes of my domain model.
package com.wikibook.persistence.dao; import java.util.List; import com.wikibook.persistence.Employee; public interface EmployeeDao { void save(Employee employee); void update(Employee employee); void delete(Employee employee); Employee get(Long id); List<Employee> getAll(); Employee getByUserName(String username); List<Employee> getAllByRole(Integer role); }
package com.wikibook.persistence.dao; import java.util.List; import com.wikibook.persistence.Role; public interface RoleDao { void save(Role role); void update(Role role); void delete(Role role); Role get(Long id); List<Role> getAll(); Role getByName(String name); }
For the implementation of this interface, I am going to extend Spring’s HibernateDaoSupport class. This provides a convenience method for retrieving the HibernateTemplate. If you have used Spring with JDBC or other ORM technologies, then you will probably be quite familiar with this approach.
It should be noted that use of the HibernateDaoSupport is optional.
Here is the implementation:
package com.wikibook.persistence.dao; import java.util.List; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import com.wikibook.persistence.Employee; public class EmployeeDaoImpl extends HibernateDaoSupport implements EmployeeDao { public void save(Employee employee) { getHibernateTemplate().save(employee); } public void update(Employee employee) { getHibernateTemplate().update(employee); } public void delete(Employee employee) { getHibernateTemplate().delete(employee); } public Employee get(Long id) { return (Employee)getHibernateTemplate().get(Employee.class, id); } @SuppressWarnings("unchecked") public List<Employee> getAll() { return getHibernateTemplate().loadAll(Employee.class); } @SuppressWarnings("unchecked") public Employee getByUserName(String username) { List<Employee> lista = getHibernateTemplate().find("FROM Employee WHERE username = ?", username); if (!lista.isEmpty()) { return lista.get(0); } return null; } @SuppressWarnings("unchecked") public List<Employee> getAllByRole(Integer role) { return getHibernateTemplate().find( "FROM Employee WHERE role = ?", role); } }
package com.wikibook.persistence.dao; import java.util.List; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import com.wikibook.persistence.Role; public class RoleDaoImpl extends HibernateDaoSupport implements RoleDao { public void save(Role role) { getHibernateTemplate().save(role); } public void update(Role role) { getHibernateTemplate().update(role); } public void delete(Role role) { getHibernateTemplate().delete(role); } public Role get(Long id) { return (Role)getHibernateTemplate().get(Role.class, id); } @SuppressWarnings("unchecked") public List<Role> getAll() { return getHibernateTemplate().loadAll(Role.class); } @SuppressWarnings("unchecked") public Role getByName(String name) { List<Role> lista = getHibernateTemplate().find("FROM Role WHERE name = ?", name); if (!lista.isEmpty()) { return lista.get(0); } return null; } }
D. The Service Layer:
The service layer play a critical role in the system architecture. It would be the point where transactions are demarcated - and typically, they would be demarcated declaratively in the Spring configuration. In the next step, when you have a look at the configuration, you will notice that I have provided a transactionManager bean. It would wrap service layer methods with transactions. The main point to take away is that there is NO transaction-related code in the data-access tier. Using the Spring HibernateTemplate ensures that the same EntityManager is shared across all DAOs. Therefore transaction propagation occurs automatically - as dictated by the service layer. In other words, it will actually behave exactly the same as other persistence mechanisms configured within the Spring framework.
Here is the interface and the implementation:
package com.wikibook.persistence.manager; import java.util.List; import com.wikibook.persistence.Employee; public interface EmployeeManager { void save(Employee employee); void update(Employee employee); void delete(Employee employee); Employee get(Long id); List<Employee> getAll(); Employee getByUserName(String username); List<Employee> getAllByRole(Integer role); }
package com.wikibook.persistence.manager; import java.util.List; import com.wikibook.persistence.Role; public interface RoleManager { void save(Role role); void update(Role role); void delete(Role role); Role get(Long id); List<Role> getAll(); Role getByName(String name); }
package com.wikibook.persistence.manager; import java.util.List; import com.wikibook.persistence.Employee; import com.wikibook.persistence.dao.EmployeeDao; public class EmployeeManagerImpl implements EmployeeManager { private EmployeeDao employeeDao; public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } public void save(Employee employee) { this.employeeDao.save(employee); } public void update(Employee employee) { this.employeeDao.update(employee); } public void delete(Employee employee) { this.employeeDao.delete(employee); } public Employee get(Long id) { return this.employeeDao.get(id); } public List<Employee> getAll() { return this.employeeDao.getAll(); } public Employee getByUserName(String username) { return this.employeeDao.getByUserName(username); } public List<Employee> getAllByRole(Integer role) { return this.employeeDao.getAllByRole(role); } }
package com.wikibook.persistence.manager; import java.util.List; import com.wikibook.persistence.Role; import com.wikibook.persistence.dao.RoleDao; public class RoleManagerImpl implements RoleManager { private RoleDao roleDao; public void setRoleDao(RoleDao roleDao) { this.roleDao = roleDao; } public void save(Role role) { this.roleDao.save(role); } public void update(Role role) { this.roleDao.update(role); } public void delete(Role role) { this.roleDao.delete(role); } public Role get(Long id) { return this.roleDao.get(id); } @SuppressWarnings("unchecked") public List<Role> getAll() { return this.roleDao.getAll(); } public Role getByName(String name) { return this.roleDao.getByName(name); } }
E. Configuration:
Since I opted for the annotation-based mappings, you have actually already seen most of the Hibernate Annotation-specific configuration when the domain classes were presented. As mentioned above, it also would have been possible to configure those mappings via XML. The only other required configuration is in META-INF/context.xml, the spring application context spring-sql.xml (must be in classpath or where packages start) and web.xml.
Here is the context.xml:
<?xml version="1.0" encoding="UTF-8"?> <Context crossContext="true" reloadable="true"> <Resource name="DefaultDS" auth="Container" type="javax.sql.DataSource" factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" url="jdbc:sqlserver://COMPUTERNAME:1433;DatabaseName=WIKIBOOK" username="USERNAME" password="PASSWORD" maxActive="10" maxIdle="10" maxWait="-1" /> </Context>
Here is the spring-sql.xml (must be in the classpath root):
<?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:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"> <!-- JNDI DataSource for J2EE environments --> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/DefaultDS" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="annotatedClasses"> <list> <value>com.wikibook.persistence.Role</value> <value>com.wikibook.persistence.Employee</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.dialect"> org.hibernate.dialect.SQLServerDialect </prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="daoTxTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="roleDao" class="com.wikibook.persistence.dao.RoleDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="roleManager" parent="daoTxTemplate"> <property name="proxyInterfaces"> <list> <value> com.wikibook.persistence.manager.RoleManager </value> </list> </property> <property name="target"> <bean class="com.wikibook.persistence.manager.RoleManagerImpl"> <property name="roleDao" ref="roleDao" /> </bean> </property> </bean> <bean id="employeeDao" class="com.wikibook.persistence.dao.EmployeeDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="employeeManager" parent="daoTxTemplate"> <property name="proxyInterfaces"> <list> <value> com.wikibook.persistence.manager.EmployeeManager </value> </list> </property> <property name="target"> <bean class="com.wikibook.persistence.manager.EmployeeManagerImpl"> <property name="employeeDao" ref="employeeDao" /> </bean> </property> </bean> </beans>
Here is the web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <description> <![CDATA[WikiBook]]> </description> <display-name>WikiBook</display-name> <!-- Spring ApplicationContext --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/spring-*.xml</param-value> </context-param> <!-- INI ZK --> <!-- INI DSP --> <servlet> <description> <![CDATA[The servlet loads the DSP pages.]]> </description> <servlet-name>dspLoader</servlet-name> <servlet-class> org.zkoss.web.servlet.dsp.InterpreterServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>dspLoader</servlet-name> <url-pattern>*.dsp</url-pattern> </servlet-mapping> <!-- END DSP --> <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> <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>*.zhtml</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>/zk/*</url-pattern> </servlet-mapping> <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> <!-- END ZK --> <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> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> <!-- INI filter --> <!-- Hibernate OpenSession Filter --> <filter> <filter-name>lazyLoadingFilter</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> </filter> <filter-mapping> <filter-name>lazyLoadingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- END filter --> <!-- Miscellaneous --> <session-config> <session-timeout>10</session-timeout> </session-config> <welcome-file-list> <welcome-file>index.zul</welcome-file> </welcome-file-list> <!-- INI resource-ref --> <resource-ref> <description>DefaultDS</description> <res-ref-name>jdbc/DefaultDS</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <!-- END resource-ref --> <!-- INI MIME mapping --> <!-- MIME mapping OMMITED--> <!-- END MIME mapping --> </web-app>
F. Integration Testing:
Perhaps the best way to learn a new API is to write a bunch of testcases. The TestEmployeeManager class provides some basic tests. In order to learn more about Hibernate Annotation, modify the code and/or configuration and observe the impact on these tests.
Here is the TestManager class:
package com.wikibook.test; import javax.sql.DataSource; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.springframework.test.AbstractTransactionalSpringContextTests; public class TestManager extends AbstractTransactionalSpringContextTests { public TestManager() { try { if (SimpleNamingContextBuilder.getCurrentContextBuilder() == null) { SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder(); DataSource ds = new DriverManagerDataSource("com.microsoft.sqlserver.jdbc.SQLServerDriver", "jdbc:sqlserver://COMPUTERNAME:1433;DatabaseName=WIKIBOOK", "USERNAME", "PASSWORD"); builder.bind("java:comp/env/DefaultDS", ds); builder.activate(); } } catch (Exception e) { e.printStackTrace(); } } @Override protected String[] getConfigLocations() { return new String[] { "spring-sql.xml" }; } }
Here is the TestEmployeeManager class:
package com.wikibook.test; import com.wikibook.persistence.Employee; import com.wikibook.persistence.manager.EmployeeManager; public class TestEmployeeManager extends TestManager { private EmployeeManager employeeManager; public TestEmployeeManager() { } @Override protected void onSetUpBeforeTransaction() throws Exception { employeeManager = (EmployeeManager) applicationContext.getBean("employeeManager"); } public void testInsert() { Employee employee = new Employee(); employee.setFullname("Marcos de Sousa"); employee.setUsername("marcos.sousa"); employee.setPassword("password1"); employeeManager.save(employee); employee = new Employee(); employee.setFullname("Tom Yeh"); employee.setUsername("tom.yeh1"); employee.setPassword("password2"); employeeManager.save(employee); employee = new Employee(); employee.setFullname("Henri Chen"); employee.setUsername("henri.chen"); employee.setPassword("password3"); employeeManager.save(employee); setComplete(); // Commit the transactions, and don't rollback } public void testUpdate() { Employee employee = employeeManager.get(Long.valueOf(2)); assertEquals("Must be tom.yeh1", "tom.yeh1", employee.getUsername()); employee.setUsername("tom.yeh"); employeeManager.update(employee); employee = employeeManager.get(Long.valueOf(2)); assertEquals("Must be tom.yeh", "tom.yeh", employee.getUsername()); setComplete(); } public void testDelete() { assertEquals("Before must be 3", 3, employeeManager.getAll().size()); Employee employee = employeeManager.get(Long.valueOf(3)); employeeManager.delete(employee); assertEquals("After must be 2", 2, employeeManager.getAll().size()); setComplete(); } }
Here is the AllTests class:
package com.wikibook.test; import junit.framework.Test; import junit.framework.TestSuite; public class AllTests { public static Test suite() { TestSuite suite = new TestSuite("Test for com.wikibook.test"); //$JUnit-BEGIN$ suite.addTestSuite(TestEmployeeManager.class); // TODO: ADD MORE TEST SUITE HERE. For Example TestRoleManager //$JUnit-END$ return suite; } }
Until here, if you run your Unit Test every thing will work.
// TODO: Tomorrow I will continue...
// TODO: Create Database, Table, Show Integration with ZK Framework as Front End, Conclusions and Tips and Tricks, etc.
// TODO: Maybe show Acegi Security? OSWORKFLOW? IBM AS400? Ant? Maven 2? SSL/HTTPS? CSS? Eclipse? in action.