Blog信息 |
blog名称: 日志总数:1304 评论数量:2242 留言数量:5 访问次数:7593774 建立时间:2006年5月29日 |

| |
[Java Open Source]Integrate Compass, Appfuse, DisplayTag Tutorial Part 5 软件技术
lhwork 发表于 2007/1/23 9:01:53 |
The goal of this tutorial is to use DiaplayTag to help layout easier.
Before we start, we have to understand some features of Compass and DisplayTag.
In CompassAction's ActionForward "list" method, we have a request parameter named "page".
The page value is used in Method performSearch in BaseCompassAction.java to determine where should the hits "detach" from.
In BaseCompassAction.java, we have a Integer named "pageSize".
The pageSize value is used in Method performSearch to determine the size that the hits should be "detach";
In other words, when we get a searchResults return, it is a segment of all hits.
For example, the hit length is 10000, we get 00010 ~ 00019 with page=1 and pageSize=10.
It's good news.
But for DisplayTag layout, it needs a list with a really length = 10000 to properly display the results with paging feature.
Another issue is DisplayTag's first page index is "1" instead of "0".
That means we need to do something to "cheat" DisplayTag ^^
The way is we create a list with length = 10000, but just set objects from 00010 ~ 00019.
Then set it to request scope for DisplayTag layout.
The original idea is from Xexex's blog, a post named "Pageable DisplayTag with Hibernate".
All codes could be download here.
I remove lib, extra, docs folders to reduce size.
Step 1. Prepare proper list for DisplayTag
1. Edit "CompassSearchResults.java" and append below codes
------ start here ------
private int totalCount; public int getTotalCount() { return totalCount; }
public void setTotalCount(int totalCount) { this.totalCount = totalCount; }
public CompassSearchResults(CompassHit[] hits, long searchTime, int totalCount) { this.hits = hits; this.searchTime = searchTime; this.totalCount = totalCount; }
------ End here ------ Default CompassSearchResults does not keep the length of the hits before detach. But we need the length. Here we add a Constructor to keep totalCount value. We will use it to determine the "lengh" of list that we have to create to cheat DisplayTag. 2. Edit Method performSearch in BaseCompassAction.java
Mark up this line : CompassSearchResults searchResults = new CompassSearchResults(detachedHits.getHits(), time);
Add this line : CompassSearchResults searchResults = new CompassSearchResults(detachedHits.getHits(), time, hits.length()); 3. Create class "CompassPageList.java" under src/web ... /util/ and add below codes
------ start here ------
package org.myapp.webapp.util;
import java.util.ArrayList;import java.util.Iterator;import java.util.List;
import org.compass.core.CompassHit;
public class CompassPageList {
private List cachedList; private int pageNo; private int pageSize; private int totalSize;
public CompassPageList(int pageSize, int totalSize) { this.pageSize = pageSize; this.totalSize = totalSize; // initialize full list with null; cachedList = new ArrayList(totalSize); for (int i = 0; i < totalSize; i++) { cachedList.add(null); }//for } public void loadPage(final int pageNo,CompassHit[] result) { // check cache has data: if (pageIsCached(pageNo)) { return; } int index = getPageStartRow(pageNo); for(int i=0; i<result.length; i++){ cachedList.set(index, result[i].getData()); index ++; } } private int getPageEndRow(int pageNo) { return Math.min(pageNo * pageSize, totalSize); } private int getPageStartRow(int pageNo) { return (pageNo - 1) * pageSize; }
private boolean pageIsCached(final int pageNo) { boolean pageIsCached = true; for (int i = getPageStartRow(pageNo);i < getPageEndRow(pageNo) && pageIsCached; i++) { if (cachedList.get(i) == null) { pageIsCached = false; } } return pageIsCached; } public Object get(int index) { return cachedList.get(index); } public boolean isEmpty() { return cachedList.isEmpty(); } public Iterator iterator() { return cachedList.iterator(); } public int size() { return cachedList.size(); } public int getPageSize() { return pageSize; }
public List list() { return cachedList; }}
------ End here ------ 4. Add new Method named "nextPage" in BaseCompassAction and add below codes
------ start here ------
protected int nextPage(HttpServletRequest request){ Enumeration names = request.getParameterNames(); int pageNo = 1; while (names.hasMoreElements()) { String parameter = (String) names.nextElement(); if (parameter.matches("d-[0-9]+-p")) { pageNo = Integer.parseInt(request.getParameter(parameter)); break; } } return pageNo; } ------ End here ------ Why we need this method? ^^ DisplayTag determines what page now the user requests to visit by a special request parameter. For example, when user clicks page 3 link , "d-12345-p=3" added as parameter and send back to server Here "12345" is random. So, by parsing "d-12345-p=3", we can know what the page the user now request. This help us identify what page the user clicks. Remember that DisplayTag's first page index is 1. ^^ 5. Edit "CompassAction.java"
Here we modify Method "list", copy and overwrite with below codes ------ start here ------ public ActionForward list(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("Enter list method"); String query = request.getParameter("query"); //String pageStr = request.getParameter("page"); //Integer page = Integer.parseInt(pageStr); int curPage = nextPage(request); final CompassSearchCommand searchCommand = new CompassSearchCommand(); int searchCommandPage = curPage - 1; searchCommand.setPage(searchCommandPage); searchCommand.setQuery(query); if (!StringUtils.hasText(searchCommand.getQuery())) { return mapping.findForward("list"); } Compass compass = (Compass) getBean("compass"); CompassSearchResults searchResults; searchResults = (CompassSearchResults) new CompassTemplate(compass).execute( CompassTransaction.TransactionIsolation.READ_ONLY_READ_COMMITTED, new CompassCallback() { public Object doInCompass(CompassSession session) throws CompassException { return performSearch(searchCommand, session); } }); CompassHit[] cha = searchResults.getHits(); CompassPageList list = new CompassPageList(Constants.PAGE_SIZE, searchResults.getTotalCount()); list.loadPage(curPage, cha); request.setAttribute("CompassHits", list.list()); return mapping.findForward("list"); }
------ End here ------ We mark up : String pageStr = request.getParameter("page"); and Integer page = Integer.parseInt(pageStr); Add : int curPage = nextPage(request); The "curPage" means the page number the user reauest. The "searchCommandPage" means the page number we want compass to detach from. A CompassPageList is created for DisplayTag use. 6. Edit "CompassListActionTest.java", copy and overwrite with below codes.
------ start here ------ package org.myapp.webapp.action;
import java.util.Date;import java.util.Iterator;import java.util.List;import java.util.Random;
import org.myapp.service.LookupManager;import org.compass.core.CompassHit;import org.myapp.model.Chapter;import org.myapp.model.Document;
public class CompassListActionTest extends BaseStrutsTestCase {
public CompassListActionTest(String name) { super(name); }
protected void setUp() throws Exception { super.setUp(); getMockRequest().setUserRole("admin"); } public void testAddData(){ Random rnd = new Random(); LookupManager mgr = (LookupManager) ctx.getBean("lookupManager"); for(int i=0; i<2; i++){ Document document = new Document(); document.setKeyWords("keyword"+ rnd.nextInt()); document.setTitle("title"+ rnd.nextInt()); //document.setPublishDate(new Date()); document.setVersion(1); for(int j=0; j<10; j++){ Chapter chapter = new Chapter(); chapter.setContent("content"+ rnd.nextInt()); chapter.setTitle("title"+ rnd.nextInt()); mgr.saveObject(chapter); document.getChapters().add(chapter); } mgr.saveObject(document); } } /* * jack Contain the term jack in the default search field * jack london (jack or london) Contains the term jack or london, or both, in the default search field * +jack +london (jack and london) Contains both jack and london in the default search field * name:jack Contains the term jack in the name property (meta-data) * * name:jack -city:london (name:jack and not city:london) Have jack in the name property and don't have london in the city property * * name:"jack london" Contains the exact phrase jack london in the name property * name:"jack london"~5 Contain the term jack and london within five positions of one another * jack* Contain terms that begin with jack * jack~ Contains terms that are close to the word jack * * birthday:[1870/01/01 TO 1920/01/01] Have the birthday values between the specified values. Note that it is a lexicography range */ public void testList_1() throws Exception { setRequestPathInfo("/compass"); addRequestParameter("method", "list"); addRequestParameter("query", "title*"); addRequestParameter("d-12345-p", "2"); actionPerform();
verifyForward("list"); verifyNoActionErrors(); List list = (List)request.getAttribute("CompassHits"); Iterator it = list.iterator(); while(it.hasNext()){ Object o = it.next(); System.out.println("o : "+ o); } } public void testList_2() throws Exception { setRequestPathInfo("/compass"); addRequestParameter("method", "list"); addRequestParameter("query", "title:title01"); addRequestParameter("d-12345-p", "1"); actionPerform();
verifyForward("list"); verifyNoActionErrors(); List list = (List)request.getAttribute("CompassHits"); Iterator it = list.iterator(); while(it.hasNext()){ Object o = it.next(); System.out.println("o : "+ o); } } public void testList_3() throws Exception { setRequestPathInfo("/compass"); addRequestParameter("method", "list"); addRequestParameter("query", "title001"); addRequestParameter("d-12345-p", "1"); actionPerform();
verifyForward("list"); verifyNoActionErrors(); List list = (List)request.getAttribute("CompassHits"); Iterator it = list.iterator(); while(it.hasNext()){ Object o = it.next(); System.out.println("o : "+ o); } } }
------ End here ------
Note:
In "testAddData", I create documents and chapters as preload data for test.
You will find that why I save chapter and document independently?
Here I have synchronous problem in Session.
If someone knows how to make compasee and hibernate synchronous correctly. Please let me know.
7. Run "ant create-lucene-index" and "ant CompassListActionTest" to see reults.
You will find that a list created to cheat DisplayTag Note:
Since ant task test-web depends on db-load, every time you run test, you have default data in db.
But the index is not cleared.
Watch out this ^^
Run ant task create-lucene-index if you need it.
Step 2. Deploy your application
1. Create jsp named "compassList.jsp" under \web\pages and add codes below
------ start here ------ <%@ include file="/common/taglibs.jsp"%>
<display:table name="CompassHits" id="CompassHits" pagesize="10" class="table" requestURI=""> <display:column property="id" title="ID" /> <display:column property="class.simpleName" title="Class Name"/> <display:column property="title" /> </display:table>
------ End here ------
2. Edit "build.xml" overwrite task "package-service" with below and script
------ start here ------
<target name="package-service" depends="compile-service"> <mkdir dir="${build.dir}/service/classes/META-INF"/> <copy tofile="${build.dir}/service/classes/META-INF/applicationContext-service.xml"> <fileset dir="src/service" includes="**/*-service.xml"/> </copy> <copy todir="${build.dir}/service/classes"> <fileset dir="src/service" includes="**/*.cpm.xml"/> </copy> <jar destfile="${dist.dir}/${webapp.name}-service.jar"> <manifest> <attribute name="Class-Path" value="${webapp.name}-dao.jar ${webapp.name}-service.jar"/> </manifest> <fileset dir="${build.dir}/service/classes" includes="**/*.class"/> <fileset dir="${build.dir}/service/classes" includes="**/*.cpm.xml"/> <metainf dir="${build.dir}/service/classes/META-INF"/> </jar> </target>
------ End here ------ Here I copy cpm file to classes and jar 3. Deploy and run it Browse to http://localhost:8080/myapp/compass.html?query=title*&d-5397244-p=1&method=list And see results ^^ You could check the screen here Summary
In the source code of "Document.java", I mark up attribute "publishDate" annotaion. The situation is the test case runs well, but always failed in container. Since I always got parse date failed and can not find ways what's wrong = =. If someone could help me fix it, I will be very appreciate. |
|
|