星期四, 6月 28, 2007

Spring Compass 備忘記

以往實作搜尋引擎, 首選 Lucene, 實作起來並不輕鬆,
Opensymphony Compass 是 Lucene 的開源框架,
如果對 Hibernate 有基本認識, 搭配 Spring framework, 使用 Compass 就會特別輕鬆,
但現時的 Compass 只能對單個 entity 作操作.

這編備忘記主要是以簡單的 Spring Web MVC 作為開始,
配合 Compass, 以新增及搜尋 User 作為簡單例子.

開始備忘記:
[1] 安裝 JDK 6u1
[2] 安裝 Eclipse WTP:
[3] 安裝 tomcat
[4] 下載 spring 及 OpenSymphony Compass
[5] Eclipse 實作


[1] 安裝 JDK 6u1:
下載 jdk-6u1-windows-i586-p.exe
http://java.sun.com/javase/downloads/index.jsp
安裝至 C:\jdk1.6.0_01\
新增環境變數 JAVA_HOME=C:\jdk1.6.0_01
C:\jdk1.6.0_01\bin 加入至 PATH 中
C:\jdk1.6.0_01\lib\dt.jar 及 C:\jdk1.6.0_01\lib\tools.jar 加入至 CLASSPATH 中
執行 C:\>java -version
輸出 java version "1.6.0_01" 即安裝成功
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)


[2] 安裝 Eclipse WTP:
下載 wtp-all-in-one-sdk-R-1.5.4-win32.zip
http://download.eclipse.org/webtools/downloads/drops/R1.5/R-1.5.4-200705021353/
解壓縮至 C:\wtp-all-in-one-sdk-R-1.5.4-win32


執行 C:\wtp-all-in-one-sdk-R-1.5.4-win32\eclipse\eclipse.exe 啟動 eclipse
如下圖所示:


[3] 安裝 tomcat:
下載 apache-tomcat-5.5.23.zip
http://tomcat.apache.org/download-55.cgi
解壓縮至 C:\apache-tomcat-5.5.23


設定 Eclipse 使用 tomcat 為 web server:
Eclipse: Window -> Show View -> Servers
右鍵點擊 Servers panel -> New -> Server ->> Apache -> Tomcat v5.5 Server -> Next
Tomcat installation directory 選擇 C:\apache-tomcat-5.5.23
JRE 選擇 jre1.6.0_01
按 Finish 完成, 如下圖所示


[4] 下載 spring 及 OpenSymphony Compass:
下載 spring-framework-2.0.6-with-dependencies.zip
http://sourceforge.net/project/showfiles.php?group_id=73357&package_id=173644


下載 compass-1.1.zip
http://www.opensymphony.com/compass/download.action


[5] Eclipse 實作:
Eclipse: File -> New -> Other ->> Web -> Dynamic Web Project -> Next
Project Name: FirstCompass
按 Finish 完成.


複製以下檔案至 Project FirstCompass/WebContent/WEB-INF/lib
spring-framework-2.0.6/dist/spring.jar
C:\apache-tomcat-5.5.23\webapps\jsp-examples\WEB-INF\lib\jstl.jar
C:\apache-tomcat-5.5.23\webapps\jsp-examples\WEB-INF\lib\standard.jar
compass-1.1.1\dist\commons-logging.jar
compass-1.1.1\dist\compass.jar
compass-1.1.1\dist\lucene\lucene-core.jar



<!-------------------- web.xml ------------------->
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app 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"
version="2.4">


<display-name>First Compass</display-name>


<description>spring compass sample application</description>


<context-param>
<param-name>webAppRootKey</param-name>
<param-value>FirstCompass.root</param-value>
</context-param>


<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-config.xml,/WEB-INF/beans-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>


<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>


<session-config>
<session-timeout>10</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
<!-------------------- web.xml ------------------->



<!-------------------- mvc-config.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="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/AddUser.do">addUserController</prop>
<prop key="/SearchUser.do">searchUserController</prop>
</props>
</property>
</bean>

<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>
org.springframework.web.servlet.view.JstlView
</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

<bean id="addUserController"
class="test.joeyta.AddUserController">
<property name="successView">
<value>searchUser</value>
</property>
<property name="formView">
<value>addUser</value>
</property>
</bean>


<bean id="searchUserController"
class="test.joeyta.SearchUserController">
<property name="nextView">
<value>listUser</value>
</property>
<property name="previousView">
<value>searchUser</value>
</property>
</bean>


</beans>
<!-------------------- mvc-config.xml ------------------->



<!-------------------- beans-config.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="compass"
class="org.compass.spring.LocalCompassBean">

<property name="resourceLocations">
<list>
<value>classpath:test/joeyta/library.cmd.xml</value>
<value>classpath:test/joeyta/User.cpm.xml</value>
</list>
</property>
<property name="compassSettings">
<props>
<prop key="compass.engine.connection">
target/testindex
</prop>
<prop key="compass.transaction.factory">
org.compass.core.transaction.LocalTransactionFactory
</prop>
</props>
</property>
</bean>


<bean id="userCompassDao" class="test.joeyta.UserCompassDao">
<property name="compass">
<ref local="compass" />
</property>
</bean>


</beans>
<!-------------------- beans-config.xml ------------------->
上面的 compass.engine.connection 使用了記憶體儲存 index 及 entity.
這裡可簡單的改成 filesystem 或 database, 詳情可參考 documentation.


<!-------------------- library.cmd.xml ------------------->
<?xml version="1.0"?>
<!DOCTYPE compass-core-meta-data PUBLIC
"-//Compass/Compass Core Meta Data DTD 1.0//EN"
"
http://www.opensymphony.com/compass/dtd/compass-core-meta-data.dtd">


<compass-core-meta-data>


<meta-data-group id="library" displayName="Library Meta Data">

<description>Library Meta Data</description>

<alias id="user" displayName="User">
<description>User alias</description>
<name>user</name>
</alias>

<meta-data id="name" displayName="Name">
<description>Name associated with an entity</description>
<name>name</name>
</meta-data>


<meta-data id="tel" displayName="Tel">
<description>Tel associated with an entity</description>
<name>tel</name>
</meta-data>

<meta-data id="address" displayName="Address">
<description>Address associated with an entity</description>
<name>address</name>
</meta-data>

</meta-data-group>

</compass-core-meta-data>
<!-------------------- library.cmd.xml ------------------->



<!-------------------- User.cpm.xml ------------------->
<?xml version="1.0"?>
<!DOCTYPE compass-core-mapping PUBLIC
"-//Compass/Compass Core Mapping DTD 1.0//EN"
"
http://www.opensymphony.com/compass/dtd/compass-core-mapping.dtd">


<compass-core-mapping package="test.joeyta">


<class name="User" alias="${library.user}">

<id name="id" />


<property name="name">
<meta-data>${library.name}</meta-data>
</property>

<property name="tel">
<meta-data>${library.tel}</meta-data>
</property>


<property name="address">
<meta-data>${library.address}</meta-data>
</property>


</class>

</compass-core-mapping>
<!-------------------- User.cpm.xml ------------------->
上例中使用 name, tel, address 作為 index.



/****************** User.java ********************/
package test.joeyta;


public class User {
private String id;


private String name;


private String tel;


private String address;


public String getAddress() {
return address;
}


public void setAddress(String address) {
this.address = address;
}


public String getId() {
return id;
}


public void setId(String id) {
this.id = id;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


public String getTel() {
return tel;
}


public void setTel(String tel) {
this.tel = tel;
}


}
/****************** User.java ********************/



/************** IUserCompassDao.java **************/
package test.joeyta;


import java.util.List;


public interface IUserCompassDao {
public void save(User user);

public List find(String sKeyword);

}
/************** IUserCompassDao.java **************/



/************** UserCompassDao.java **************/
package test.joeyta;


import java.util.ArrayList;
import java.util.List;


import org.compass.core.CompassCallback;
import org.compass.core.CompassException;
import org.compass.core.CompassHit;
import org.compass.core.CompassHits;
import org.compass.core.CompassSession;
import org.compass.spring.CompassDaoSupport;


public class UserCompassDao extends CompassDaoSupport implements
IUserCompassDao {


public void save(User user) {
getCompassTemplate().save(user);
}


public List find(final String sKeyword) {
return (List) getCompassTemplate().execute(new CompassCallback() {
public Object doInCompass(CompassSession session)
throws CompassException {
CompassHits hits = session.find(sKeyword);
List users = new ArrayList();
for (int i = 0; i < hits.length(); i++) {
CompassHit hit = hits.hit(i);
if (hit.getAlias().equals("user")) {
users.add(hit.getData());
}
}
return users;
}
});


}


}
/************** UserCompassDao.java **************/



/************** AddUserController.java **************/
package test.joeyta;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;


public class AddUserController extends SimpleFormController {
public AddUserController() {
setCommandClass(User.class);
}


protected ModelAndView onSubmit(Object command) throws Exception {
User user = (User) command;


try {
IUserCompassDao dao = (IUserCompassDao)getApplicationContext().getBean("userCompassDao");
dao.save(user);
return new ModelAndView(getSuccessView());
} catch (Exception e) {


}
return new ModelAndView(getFormView());
}
}
/************** AddUserController.java **************/



/************** SearchUserController.java **************/
package test.joeyta;


import java.util.List;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;


public class SearchUserController extends AbstractController {


String previousView;


String nextView;


protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (request.getParameter("keyword") != null && !request.getParameter("keyword").equals("")) {
String sKeyword = request.getParameter("keyword");
IUserCompassDao dao = (IUserCompassDao) getApplicationContext().getBean("userCompassDao");
List users = dao.find(sKeyword);
return new ModelAndView(getNextView(), "users", users);
}
return new ModelAndView(getPreviousView());
}


public String getNextView() {
return nextView;
}


public void setNextView(String nextView) {
this.nextView = nextView;
}


public String getPreviousView() {
return previousView;
}


public void setPreviousView(String previousView) {
this.previousView = previousView;
}


}
/************** SearchUserController.java **************/



<!---------------- index.jsp -------------->
<%@ page session="false"%>


<%
request.getRequestDispatcher("/WEB-INF/jsp/addUser.jsp").forward(request,response);
%>
<!---------------- index.jsp -------------->



<!---------------- addUser.jsp -------------->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Add User</title>
</head>
<body>


<form name="addUserForm" action="<%=request.getContextPath()%>/AddUser.do" method="POST">
Add user to compass <br/>
<table border="0">
<tr>
<td>ID</td><td><input type="text" name="id"/></td>
</tr>
<tr>
<td>NAME</td><td><input type="text" name="name"/></td>
</tr>
<tr>
<td>TEL</td><td><input type="text" name="tel"/></td>
</tr>
<tr>
<td>ADDRESS</td><td><input type="text" name="address"/></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Add"/></td>
</tr>
</table>
</form>


</body>
</html>
<!---------------- addUser.jsp -------------->



<!---------------- searchUser.jsp -------------->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Add User</title>
</head>
<body>


<form name="searchUserForm" action="<%=request.getContextPath()%>/SearchUser.do" method="POST">
Search user from compass <br/>
<table border="0">
<tr>
<td><input type="text" name="keyword"/></td>
</tr>
<tr>
<td><input type="submit" value="Search"/></td>
</tr>
</table>
</form>


</body>
</html>
<!---------------- searchUser.jsp -------------->



<!---------------- listUser.jsp -------------->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="
http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Add User</title>
</head>
<body>
<a href="<%=request.getContextPath()%>/index.jsp">back to index</a> <br/>
<table border="1">
<tr><td>ID</td><td>NAME</td><td>TEL</td><td>ADDRESS</td></tr>
<c:forEach var="user" items="${users}">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.tel}</td>
<td>${user.address}</td>
</tr>
</c:forEach>
</table>


</body>
</html>
<!---------------- listUser.jsp -------------->



Eclipse project 下載位置:
http://blog.matrix.org.cn/joeyta/resource/FirstCompass.zip
由於 spring.jar 及 compass.jar 太大了, 故不包含於 project 中, 請自行加入.


項目結構如下所示:


測試頁面如下圖所示:





參考資料:
http://www.opensymphony.com/compass/
http://www.opensymphony.com/compass/content/documentation.html





沒有留言: