Jive
是一個比較豐富的知識寶藏,從中可以學習到很多新的實戰技巧和具體功能實現方式。前面基本介紹了
Jive
中的一些主要架構技術,通過這些技術可以基本上掌握
Jive
論壇系統。
Jive
中還有很多非常實用的組件技術和工具庫,分析學習可重用技術,可以在自己具體的項目重復使用,大大提高了新系統的開發速度和效率。
Jive
的管理功能中提供了將
Jive
數據庫數據導出到
XML
文件的管理工具,在這個工具功能實現中,使用了樹形結構的遍歷技術。
Jive
將
ForumThread
中的第一個
ForumMessage
作為
root ForumMessage
,以這個
ForumMessage
為根節點,每個
ForumThread
中包含了一套樹形結構。
TreeWalker
是樹形結構的一個抽象接口,代碼如下:
public interface TreeWalker {
??? //
根節點
??? public ForumMessage getRoot();
??? //
獲得父節點
??? public ForumMessage getParent(ForumMessage child)
?????????? ?throws ForumMessageNotFoundException;
??? //
獲得子節點
??? public ForumMessage getChild(ForumMessage parent, int index)
??????????? throws ForumMessageNotFoundException;
??? //
獲得所有子節點
??? public Iterator children(ForumMessage parent);
??? //
獲得所有的子節點,包括子節點的子節點
…
??? public Iterator recursiveChildren(ForumMessage parent);
??? //
獲得一個節點的深度,相對根節點而言
??? public int getMessageDepth(ForumMessage message);
???
??? public int getChildCount(ForumMessage parent);
??? public int getRecursiveChildCount(ForumMessage parent);
?
??? /**
???? *
返回相對父節點的子節點索引。例如
???? * <pre>
???? *?? 4
???? *?? |-- 2
???? *?? |-- |-- 1
???? *?? |-- |-- 6
???? *?? |-- |-- 8
???? *?? |-- 5
???? * </pre>
???? * getIndexOfChild(4, 2)
將返回
0
???? * getIndexOfChild(4, 5)
將返回
1
???? * getIndexOfChild(2, 1)
將返回
0
???? * getIndexOfChild(2, 6)
將返回
1
???? * getIndexOfChild(2, 8)
將返回
2
???? */
??? public int getIndexOfChild(ForumMessage parent, ForumMessage child);
??? //
一個節點是否是葉,葉相對枝來說,葉沒有子節點了
??? public boolean isLeaf(ForumMessage node);
}
DbTreeWalker
是
TreeWalker
的一個實現,它是將一個
ForumThread
下所有帖子的
ID
從數據庫中裝入
LongTree
中。一句
LongTree
的樹形結構遍歷核心技術實現
ForumThread
中的帖子結構的遍歷。
LongTree
類似之前的
Cache
類,封裝了樹形結構遍歷的核心算法,在
LongTree
中建立了
3
個數組
long [] keys
、
char [] leftChildren
和
char [] rightSiblings
。
一個節點有兩個特性:它有子節點;它有兄弟節點。
leftChildren
保存的是這個節點的子節點的索引;而
rightSiblings
保存的是這個節點兄弟節點的索引。例如:
??1000
?? |-- 3000
?? |-- |--4000
?? |-- |--6000
?? |-- |--7000
?? |-- 5000
1000
是個根節點,
1000
下有兩個子節點
3000
和
5000
,而
3000
則有
3
個子節點
4000
、
6000
和
7000
,
3000
還有一個兄弟節點
5000
,使用上述
3
個數組是這樣保持信息的:
keys[0] = 1000
keys[1] = 3000
keys[2] = 4000
keys[3] = 5000
keys[4] = 6000
keys[5] = 7000
keys
數組中保存的是各個節點的數值,而
leftChildren
和
rightSiblings
數組保存的是
keys
數組的
index
,即
0
、
1
、
2
、
3
、
4
等數字。
1000
節點有兩個子節點,那么其對應的
leftChildren
和
rightSiblings
分別是:
leftChildren[0] = 1
leftChildren[0]
中的索引
0
表示當前索引,
keus[0]
是
1000
,說明現在節點是
1000
;
1
也表示
keys
數組的索引,
keys[1]
的值是
3000
,所以上一句表示
1000
的子節點是
3000
。
1000
節點沒有兄弟節點:
rightSiblings[0] =
-
1
再看看
3000
節點,其
keys
的索引
Index
是
1
,其子節點是
4000
、
6000
和
7000
,取最近一個
4000
的索引
index
放入數組:
leftChildren[1] = 2
這表示
1000
節點的子節點是
4000
,那么另外一個
6000
節點如何表示?這是以
4000
節點的兄弟節點表現出來的。
4000
節點的
keys
的索引
index
是
2
,通過下列表示:
rightSiblings[2] = 4
其中,
4
表示
6000
在
keys
中的索引
Index
。同樣,第
3
個子節點
7000
表示如下:
rightSiblings[4] = 5
這樣,
3000
節點有
3
個子節點
4000
、
6000
和
7000
(
4000
、
6000
和
7000
是兄弟節點)通過上述簡單兩句就表現出來了。
總結一個父子關系樹的表示方法:在父節點中,使用
leftChildren
保存最靠近父節點的一個子節點(父節點的第一個兒子,叫長子)的索引,其他子節點則是通過
rightSiblings
表明與長子是兄弟關系。
看看
LongTress
的初始化構造方法,
keys
數組的值保存的是
ForumMessage
的
ID
,如下:
public LongTree(long rootKey, int size) {
??? keys = new long[size+1];?? ???????????????????? //
初始化
??? leftChildren = new char[size+1]; ????????????? //
初始化
??? rightSiblings = new char[size+1]; ???????????? //
初始化
?
??? //
在
keys[1]
中保存的是
rootMessage
的
ID
??? keys[1] = rootKey;
??? leftChildren[1] = 0;?? ????????????????????????????? //
無子節點
??? rightSiblings[1] = 0;? ?????????????????????????????? //
無兄弟姐妹
}
當加入一個節點時,其方法如下:
public void addChild(long parentKey, long newKey) {
??? //
根據
parentKey
找出其對應的
keys
索引
index
??? char parentIndex = findKey(parentKey, (char)1);
??? if (parentIndex == 0) {
??????????? throw new IllegalArgumentException("Parent key " + parentKey +
??????????????????? " not found when adding child " + newKey + ".");
??? }
?
??? //
為
newKey
創建節點
??? keys[nextIndex] = newKey;
??? leftChildren[nextIndex] = 0;
??? rightSiblings[nextIndex] = 0;
?
??? //
將新建節點標志為父節點的子節點
??? if (leftChildren[parentIndex] == 0) {
??????? //
如果父節點原來沒有子節點,那就將新建節點作為其子節點
??????? leftChildren[parentIndex] = nextIndex;
??? }else {
???? ???//
如果父節點有子節點,尋找最后一個子節點
??????? long siblingIndex = leftChildren[parentIndex];
?????? //
在
siblingIndex
中循環查找,直至值為
0
??????? while (rightSiblings[new Long(siblingIndex).intValue()] != 0) {
???????????? siblingIndex = rightSiblings[new Long(siblingIndex).intValue()];
??????? }
??????? //
將新建節點作為最后一個字節點加入
??????? rightSiblings[new Long(siblingIndex).intValue()] = nextIndex;
??? }
??? //
最后,自動增加
nextIndex
以便下一個等待插入
??? nextIndex++;
}
Jive
將數據導出到
XML
文件時,就是根據某個
ForumMessage
的
ID
,通過
TreeWalker
找出它的所有子節點
ForumMessage
的
ID
,然后將其內容導出。
XML
稱為可擴充標記語言,是類似
HTML
定義文檔標記語言的一個框架。
XML
以結構嚴謹著稱,因此用來保存數據是非常適合的,這樣在數據庫之外,又多了一個持久化保存數據的方式。
在
Java
中,
XML
更多時是作為配置文件數據存儲體形式出現,在之前一般是使用
properties
來保存系統的配置文件,如下:
cache.maxsize=1024
cache.minsize=2
這兩句分別設置
cache
的最大值和最小值,那么在
Java
中通過下列語句讀取:
Properties p = new Properties();
InputStream fin = new FileInputStream("Config.properties");
p.load(fin);
String maxSize = p.getProperty("cache.maxsize ");
String minSize = p.getProperty("cache.minsize ");
這樣就可以獲得配置文件中的兩個值。
這種配置文件使用方法簡單直接,但是只適合配置文件不很復雜的情況。在復雜的配置情況下,
properties
就不是很合適,例如設置系統的可選屬性,一個系統安裝在不同應用場合,客戶的要求總有些不一樣,有些功能是可選的,那么需要在配置文件中配置一些可選的功能,以
Tomcat
的
server.xml
為例,如下:
<Context path="/register" docBase="D:/javasource/SimpleRegister/defaultroot" debug="1"
???????????????? reloadable="true" crossContext="true">
…
</Context>
<Context path="/examples" docBase="examples" debug="0"
???? ????????????reloadable="true" crossContext="true">
????????? <Logger className="org.apache.catalina.logger.FileLogger"
???????????????????? prefix="localhost_examples_log." suffix=".txt"
??????? ??? ??timestamp="true"/>
…
</Context>
在一個配置中有很多
Context
,每個
Contexr
都包含
Logger
等具體配置,
XML
格式本身是一種樹形結構的數據格式。在實際應用中,很多復雜的表示都可以使用樹形結構來分解代表。因此,使用
XML
來表示這種樹形結構的數據無疑是非常合適的。
在
Jive
中,
jive_config.xml
是
Jive
系統的配置文件。這個配置文件是在
Jive
系統安裝時,按照用戶的選擇動態生成的,其中包含數據庫連接參數、界面顯示顏色、電子郵件配置以及緩沖配置、搜索配置和文件或圖片上傳配置。
分析讀取
XML
數據有很多工具,如
DOM
(
http://www.worg/DOM/
)和
SAX
(
http://www.saxproject.org/
)。這兩種是標準的
XML
分析器,可以使用任何語言來實現,
DOM
分析
XML
數據時,是將整個文檔一下子讀入內存,如果文檔很大,性能就發生影響,而
SAX
則是動態地對每一行分析,無需全部讀入,因此在分析大文檔時速度比較快。
但是這兩種分析方法都是圍繞
XML
樹形結構展開的,在編制這兩種分析器時,會涉及到大量
XML
概念的
API
,需要一定的
XML
基礎和知識,使用起來有一定難度。
JDOM
(
http://www.jdom.org
)封裝了
DOM/SAX
的具體使用技術,以非常符合
Java
編程方式的形式來分析
XML
,因此使用起來非常方便。
在分析速度方面,
JDOM
比
DOM
要快,比
SAX
慢一點。但用在分析配置文件上,速度不是主要的,因為可以使用
lazy initialization
。這類似緩存機制,在第一次讀取后就保存在內存中,以后每次直接從內存中獲取。
在
Jive
中,
JDOM
操作基本是由
JiveGlobals
完成的。
public class JiveGlobals {
private static final String JIVE_CONFIG_FILENAME = "jive_config.xml";
private static XMLProperties properties = null;
?
...
//
從配置文件獲取配置
public static String getJiveProperty(String name) {
loadProperties();
return properties.getProperty(name);
}
? //
用
JDOM
載入配置文件
private synchronized static void loadProperties() {
if (properties == null) {
properties = new XMLProperties(jiveHome + File.separator +
JIVE_CONFIG_FILENAME);
}
}
?//
將配置保存到配置文件中
?public static void setJiveProperty(String name, String value) {
?? loadProperties();
?? properties.setProperty(name, value);
?}
}
從上面代碼看出,對
XML
文件讀寫非常方便,使用
properties.getProperty(name)
就可以獲得
name
的配置值,而
properties.setProperty(name, value)
一句就可以將
name
和其值
value
保存到
XML
文件中,非常類似
Hashtable
的讀取和存入。
XMLProperties
是
JDOM
的一個屬性文件輔助包,它主要是對屬性名進行分解和合成,例如
XML
如下:
<jive>
<email>
<fromName>Jive_Administrator</fromName>
<fromEmail>webmaster@example.com</fromEmail>
<subject>Your thread was updated!</subject>
<body>Hello {name}! The thread {threadName} was updated!</body>
</email>
<jive>
jive/email/fromName
的值是
Jive_Administrator
,那么如何讀取
Jive_Administrator
?使用
properties.getProperty("email.fromName")
就可以。注意到,這里
Key
的名字組合是
email.fromName
,這種特定的寫法就是
XMLProperties
可以支持的,在對
XML
文件保存細節中,由
XMLProperties
將這種屬性名稱寫法具體轉換成
XML
文檔操作。具體內部代碼如下:
? public String getProperty(String name) {
??????? if (propertyCache.containsKey(name)) {? //
從緩存中獲取
??????????? return (String)propertyCache.get(name);
??????? }
???????? //
將
email.fromName
轉變為
String
數組
???????? //
例如
propName[0] = jive; propName[1] = email …
??????? String[] propName = parsePropertyName(name);
??????? //
通過
propName
數組循環,遍歷
XML
的樹形結構層次,尋找出對應的屬性值
??????? Element element = doc.getRootElement();
??????? for (int i = 0; i < propName.length; i++) {
??????????? element = element.getChild(propName[i]);
??????????? if (element == null) {
??????????????? return null;
?????? ?????}
??????? }
??????? //
尋找到
element
后,獲得其內容
??????? String value = element.getText();
??????? if ("".equals(value)) {??????????? return null;??????? }
??????? else {
??????????? //
保存到緩存中
??????????? value = value.trim();
??????????? propertyCache.put(name, value);
??????????? return value;
??????? }
??? }
以上只是分析了
JDOM
的
XMLProperties
包是如何做屬性配置提取的,正是因為
JDOM
內部做了很多基礎支持性的細節工作,才使得使用
JDOM
變得非常方便。
總結使用
JDOM
對配置文件讀寫操作語法如下:
·?????????
獲得配置(查詢):
getProperty(name)
。
·?????????
新增和修改:
properties.setProperty(name, value)
。
·?????????
刪除:
properties.deleteProperty(name)
。
name
的格式是
xxx.xxx.xxx
,例如:
<jive>
?? …
<upload>
??????? <dir>/home/jdon/jive/upload/</dir>
??????? <relurl>upload/</relurl>
??? </upload>
??? …
</jive>
要獲得
/home/jdon/jive/upload/
,
name
的格式是
upload.dir
;要獲得
upload/
,
name
的格式是
upload.relurl
。
注意,如果要在系統中支持上述功能,必須下載
JDOM
包,還要有
DataFormatFilter. java
、
DataUnformatFilter.java
、
XMLFilterBase.java
和
XMLProperties.java
支持。這幾個類不包含在
JDOM
標準包中,作為一個應用包含在其
Sample
中。當然也可以直接從
Jive
中復制出來使用。
5.3?
全文檢索和
Lucene
Jive
中支持全文檢索,這個功能主要核心依賴另外一個開放源代碼項目
Lucene
(
http://jakarta.apache.org/lucene/docs/index.html
)。
Jakarta Lucene
是一個高性能全文搜索引擎,可以跨平臺應用于任何搜索應用。
使用
Lucene
作為搜索引擎,應用系統需要做兩件事情:
(
1
)建立索引文件。將
Jive
數據庫中的數據內容建立索引文件,這是通過
SearchManager
來完成。
SearchManager
代碼如下:
public interface SearchManager {
??? public boolean isSearchEnabled();
??? public void setSearchEnabled(boolean searchEnabled);
??? /**
??? //
如果
SearchManage
正在工作,返回真
??? public boolean isBusy();
??? //
返回索引完成率
??? public int getPercentComplete();
??? //
是否自動建立索引
??? //
通過
TaskEngine.scheduleTask
方法實現定期自動索引
??? public boolean isAutoIndexEnabled();
??? public void setAutoIndexEnabled(boolean value);
??? //
自動索引間隔的分鐘數
??? public int getAutoIndexInterval();
??? public void setAutoIndexInterval(int minutes);
??? //
獲得上次建立索引的時間
??? public Date getLastIndexedDate();
//
在實時建立索引時,將當前帖子加入索引
??? public void addToIndex(ForumMessage message);
??? public void removeFromIndex(ForumMessage message);
??? //
手動更新自上次建立索引后的新內容
??? public void updateIndex();
??? //
手動重新建立全部的索引
??? public void rebuildIndex();
??? //
優化
??? public void optimize();
}
·?????????
SearchManager
定義了建立索引的一些屬性,建立索引有兩種方式:當有新帖子加入時,通過調用
indexMessage()
方法實時索引;或者通過
TaskEngine.scheduleTask
方法每隔一定時間建立索引。
·?????????
DbSearchManager
作為
SearchManager
的子類實現,又是一個線程類,它是建立索引的主要功能類。在
DbSearchManager
中主要使用了
Lucene
的
IndexWriter
、
Analyzer
、
Document
和
Field
等功能類來建立索引。
·?????????
IndexWriter
用戶建立新的索引,當然也可以將文檔加入已經存在的索引。
在文本被索引之前,它必須通過一個分析器
Analyzer
。分析器
Analyzer
負責從文本中分離出索引關鍵字。
Lucene
有幾種不同類型的分析器:
·?????????
SimpleAnalyzer
是將英文轉換為小寫字母,按空格和標點符號切分出英文單詞,
如
I am Java
這一句,使用
SimpleAnalyzer
切詞就會切分出下列詞語:
token1=I
token2=am
token3=Java
·?????????
StandardAnalyzer
是對英文進行了較為復雜的處理。除了按詞語建立索引關鍵字(
token
)外,還能夠為特殊名稱、郵件地址、縮寫格式等建立索引單元,而且對“
and
”、“
the
”等詞語做了過濾。
·?????????
ChineseAnalyzer
是專門用來分析中文的索引的。關于中文分析器,有很多嘗試,如車東的
http://sourceforge.net/projects/weblucene/
;
zhoujun
的
http://www. jdon.com/jive/thread.jsp? forum=61&thread=8400
等,該問題將在后面章節繼續討論。
一個索引是由一系列
Document
組成,每個
Document
是由一個或多個
Field
組成,每個
Field
都有一個名字和值,可以把
Document
作為關系數據庫中一條記錄,而
Field
則是記錄中某列字段。一般建立索引如下:
//
指定將在哪個目錄建立索引
String indexDir = "/home/jdon/jive/WEB-INF/jiveHome";?
//
指定將要建立索引的文本
String text = "welcom here, I am Java,";?????
Analyzer analyzer = new StandardAnalyzer();?? //
使用
StandardAnalyzer
//
建立一個
IndexWriter
IndexWriter writer = new IndexWriter(indexDir, analyzer, true);
//
建立
Document
Document document? = new Document();
//
進行切詞、索引
document.add(Field.Text("fieldname", text));
//
加入索引中
writer.addDocument(document);
writer.close();
其中,
Field
根據具體要求有不同用法,
Lucene
提供
4
種類型的
Field: Keyword
、
UnIndexed
、
UnStored
和
Text
。
·?????????
Keyword
不實現切詞,逐字地保存在索引中,這種類型適合一些如
URL
、日期、個人姓名、社會安全號碼、電話號碼等需要原封不動保留的詞語。
·?????????
UnIndexed
既不實現切詞也不索引,但是其值是一個詞一個詞地保存在索引中,這不適合很大很長的詞語,適合于顯示一些不經過直接搜索的結果值。
·?????????
UnStored
與
UnIndexed
正好相反,將被切詞和索引,但是不保存在索引中,這適合巨大文本,如帖子內容、頁面內容等。
·?????????
Text
是實現切詞、索引,并且保存在索引中。
在
Jive
中,索引的建立以
DbSearchManager
中加入帖子索引方法為例:
protected final void addMessageToIndex(long messageID, long userID,
??????????? long threadID, long forumID, String subject, String body,
??????????? java.util.Date creationDate, IndexWriter writer) throws IOException
{
??? //
建立一個
? Document
??? Document doc = new Document();
??? doc.add(Field.Keyword("messageID",Long.toString(messageID)));
??? doc.add(new Field("userID", Long.toString(userID), false, true, false));
?? doc.add(new Field("threadID", Long.toString(threadID), false, true, false));
??? doc.add(new Field("forumID", Long.toString(forumID), false, true, false));
??? doc.add(Field.UnStored("subject", subject));
??? doc.add(Field.UnStored("body", body));
??? doc.add(new Field("creationDate", DateField.dateToString(creationDate),
??????????????? false, true, false));
??? //
將該
Document
加入當前索引中
??? writer.addDocument(doc);
}
在
DbSearchManager
中同時也實現了自動建立索引的過程,通過在構造方法中生成
TimeTask
實例:
timerTask = TaskEngine.scheduleTask(
??????????????????? this,autoIndexInterval*JiveGlobals.MINUTE,
??????????????????? autoIndexInterval*JiveGlobals.MINUTE);
因為
DbSearchManager
是線程類,它在
run
方法中實現索引任務自動運行:
TaskEngine.addTask(new IndexTask(false));
(
2
)建立完成后,就可以直接搜索特定的詞語了。搜索語句一般代碼如下:
Searcher searcher = new IndexSearcher((indexDir);? //
創建一個搜索器
//
使用和索引同樣的語言分析器
Query query = QueryParser.parse(queryString, "body", new StandardAnalyzer());
//
搜索結果使用
Hits
存儲
Hits hits = searcher.search(query);
//
通過
hits
得到相應字段的數據和查詢的匹配度
for (int i=0; i<hits.length(); i++) {
????? System.out.println(hits.doc(i).get("fieldname "));
};
Jive
實現搜索就復雜得多,它為搜索專門建立了一個
Query
接口:
public interface Query {
??? //
需要搜索的字符串
??? public String getQueryString();
??? public void setQueryString(String queryString);
?
??? public Date getBeforeDate();
??? public void setBeforeDate(Date beforeDate);
?
??? public Date getAfterDate();
??? public void setAfterDate(Date afterDate);
?
??? public User getFilteredUser();
??? public void filterOnUser(User user);
?
??? public ForumThread getFilteredThread();
??? public void filterOnThread(ForumThread thread);
?
??? public int resultCount();
??? public Iterator results();
??? public Iterator results(int startIndex, int numResults);
}
Query
接口中主要定義了和搜索相關的一些參數,可以根據具體要求定制,直接使用
Query
就可以達到搜索的目的,如需要搜索
Java is cool
,那么使用下列代碼:
ForumFactory forumFactory = ForumFactory.getInstance();
Query query = forumFactory.createQuery(forums);
query.setQueryString("Jive is cool");
Iterator iter = query.results();
while (iter.hasNext()) {
???? ForumMessage message = (ForumMessage)iter.nextElement();
???? //
輸出結果
}
追查代碼會發現,上面
forumFactory.createQuery(forums)
方法實際內容是
new DbQuery(forums, this)
。
DbQuery
作為
Query
的一個子類,它的搜索語句通過
executeQuery()
方法中下列語句實現:
private void executeQuery() {
??? try {
?????? Searcher searcher = getSearcher();? //
創建一個搜索器
?????? …
?????? //
使用分析器獲得
Query
對象
?????? org.apache.lucene.search.Query bodyQuery =
??????????????? QueryParser.parse(queryString, "body", DbSearchManager.analyzer);
????? org.apache.lucene.search.Query subjectQuery =
??????????????? QueryParser.parse(queryString, "subject", DbSearchManager.analyzer);
??????? //
將兩個
Query
對象加入
BooleanQuery
??????? BooleanQuery comboQuery = new BooleanQuery();
??????? comboQuery.add(subjectQuery,false,false);
??????? comboQuery.add(bodyQuery,false,false);
??????? //Jive
自己的搜索結果過濾器
??????? MultiFilter multiFilter = new MultiFilter(3);
??????? int filterCount = 0;
????
??????? if (factory.getForumCount() != forums.length) {
???????????? //
將其他論壇內容搜索結果過濾掉
??????????? String[] forumIDs = new String[forums.length];
???????????? for (int i=0; i<forumIDs.length; i++) {
??????????????? forumIDs[i] = Long.toString(forums[i].getID());
???????????? }
???????????? multiFilter.add(new FieldFilter("forumID", forumIDs));
???????????? filterCount++;
??????? }
?
???????? //
日期過濾器
?
如只查詢某日期以后的內容
??????? if (beforeDate != null || afterDate != null) {
??????????? if (beforeDate != null && afterDate != null) {
??????????????? multiFilter.add(new DateFilter("creationDate", beforeDate, afterDate));
??????????????? filterCount++;
?? ??????????}else if (beforeDate == null) {
??????????????? multiFilter.add(DateFilter.After("creationDate", afterDate));
???????????????? filterCount++;
???????????? }else {
???????????????? multiFilter.add(DateFilter.Before("creationDate", beforeDate));
? ???????????????filterCount++;
???????????? }
??????? }
??????? //
過濾用戶
??????? if (user != null) {
???????????? String userID = Long.toString(user.getID());
???????????? multiFilter.add(new FieldFilter("userID", userID));
???????????? filterCount++;
??????? }
??????? //
主題過濾
??????? if (thread != null) {
???????????? String threadID = Long.toString(thread.getID());
????????? ???multiFilter.add(new FieldFilter("threadID", threadID));
???????????? filterCount++;
??????? }
??????? if (filterCount > 0) {//
實現搜索
???????????? hits = searcher.search(comboQuery, multiFilter);
??? ????} else {
???????????? hits = searcher.search(comboQuery);
??????? }
??????? //
搜索結果不要超過最大大小
??????? int numResults = hits.length() < MAX_RESULTS_SIZE ?
??????????????????? hits.length() : MAX_RESULTS_SIZE;
??????? long [] messages = new long[numResults];
??????? for (int i=0; i<numResults; i++) {
?????????? messages[i]= Long.parseLong( ((Document)hits.doc(i)).get("messageID") );
?????? }
??????? results = messages;
????? } catch (Exception e) {
??????? e.printStackTrace();
??????? results = new long[0];
????? }
}
Jive
的搜索使用了過濾器,以便過濾掉不想出現的結果,然后還對搜索結果進行了限制轉換,這些在實際使用中都是必需的。
Jive
默認的字符集編碼方式是
ISO8859_1
,即
Latin-1
字符集,這是國際標準化組織用來表示
Latin
等西方語言使用的字符集。
ISO8859_1
字符集非常類似常見的
ASCII
字符集。由于
ISO8859_1
是使用單字節來表示,而漢字是采取雙字節來表示一個漢字,我國制定了一套專門用來表示漢字
GB2312
和
GBK
編碼字符集。
在
Java
內部運算中,涉及到的所有字符串都會被轉化為
UTF-8
編碼來進行運算。那么,在被
Java
轉化之前,字符串是什么樣的字符集?
Java
總是根據操作系統的默認編碼字符集來決定字符串的初始編碼,而且
Java
系統的輸入和輸出的都是采取操作系統的默認編碼。
因此,如果能統一
Java
系統的輸入、輸出和操作系統
3
者的編碼字符集合,將能夠使
Java
系統正確處理和顯示漢字。這是處理
Java
系統漢字的一個原則,但是在實際項目中,能夠正確抓住和控制住
Java
系統的輸入和輸出部分是比較難的。
Jive
是運行在
Web
容器中的一個
Servlet/JSP
系統。在這個系統中,輸入途徑有很多種:一種是通過頁面表單打包成請求(
request
)發往服務器的;第二種是通過數據庫讀入;還有第
3
種輸入比較復雜,
JSP
在第一次運行時總是被編譯成
Servlet
,
JSP
中常常包含中文字符,那么編譯使用
javac
時,
Java
將根據默認的操作系統編碼作為初始編碼。除非特別指定,如在
Jbuilder
中可以指定默認的字符集。
輸出途徑也有幾種:第一種是
JSP
頁面的輸出。由于
JSP
頁面已經被編譯成
Servlet
,那么在輸出時,也將根據操作系統的默認編碼來選擇輸出編碼,除非指定輸出編碼方式;還有輸出途徑是數據庫,將字符串輸出到數據庫。
由此看來,一個
J2EE
系統的輸入輸出是非常復雜,而且是動態變化的,而
Java
是跨平臺運行的,在實際編譯和運行中,都可能涉及到不同的操作系統,如果任由
Java
自由根據操作系統來決定輸入輸出的編碼字符集,這將不可控制地出現亂碼。
正是由于
Java
的跨平臺特性,使得字符集問題必須由具體系統來統一解決,所以在一個
Java
應用系統中,解決中文亂碼的根本辦法是明確指定整個應用系統統一字符集。
在
Jive
中如果指定默認字符集為某個字符集,那么就要在所有的輸入輸出環節都要標識為這個字符集。但是,前面已經提到,要完全在編碼時做到還是有一定難度,必須對
Web
程序有相當地掌握和理解,而且步驟較繁瑣。
有一種相對省事的做法,例如統一指定為
ISO8859_1
,因為目前大多數軟件都是西方人編制的,他們默認的字符集就是
ISO8859_1
,包括操作系統
Linux
和數據庫
MySQL
等。這樣,如果指定
Jive
統一編碼為
ISO8859_1
,那么就有下面
3
個環節必須把握:
·?????????
開發和編譯代碼時指定字符集為
ISO8859_1
。
·?????????
運行操作系統的默認編碼必須是
ISO8859_1
,如
Linux
。
·?????????
在
JSP
頭部聲明:
<%@ page contentType="text/html;charset=ISO8859_1" %>
。
如果統一指定為
GBK
中文字符集,上述
3
個環節同樣需要做到,不同的是只能運行在默認編碼為
GBK
的操作系統,如中文
Windows
。
所以統一編碼為
ISO8859_1
和
GBK
雖然帶來編制代碼的方便,但是也破壞了
Java
跨平臺運行的優越性,只在一定范圍內行得通。
很多情況下,程序員大都是在中文
Windows
下開發調試
Java
系統,然后直接部署到
Linux
等系統上真正運行。而且其中可能涉及到
XML
文件讀寫。
XML
是對編碼方式要求嚴格的數據存儲體,
XML
又可以隨著代碼移動。因此,在進行真正大規模
Java
系統開發運行時,上述臨時簡單的變通方式就沒有效果了。
要從根本上解決
Java
的中文問題,只要將
Java
系統的統一編碼定義為
UTF-8
。
UTF-8
編碼是一種兼容所有語言的編碼方式,惟一比較麻煩的就是要找到應用系統的所有出入口,然后使用
UTF-8
去“結扎”它。
Jive
默認的字符集編碼方式是
ISO8859_1
,如果都統一為
UTF-8
,那么也需要做下列幾步工作:
·?????????
開發和編譯代碼時指定字符集為
UTF-8
。
·?????????
使用過濾器,將所有請求(
request
)轉換為
UTF-8
;針對不同應用過濾器有兩種。
·?????????
如果所有請求都經過一個
Servlet
控制分配器,那么使用
Servlet
的
filter
執行語句。
·?????????
request.setCharacterEncoding("UTF-8")
。
·?????????
如果不經過
Servlet
,而直接是
JSP
,那么每個
JSP
頭部設置上述語句。
·?????????
在
JSP
頭部聲明:
<%@ page contentType="text/html;charset= UTF-8" %>
。
·?????????
設定數據庫連接方式是
UTF-8
。
以上討論了
Jive
以及通用
Java
的中文問題。如果整個應用系統是從開始進行開發,那么統一指定編碼為
UTF-8
就非常容易做到。如果是在英文源代碼基礎上二次開發,那么首先要將原來的源代碼轉換為統一編碼
UTF-8
,那么這種轉換工作會帶來一定的麻煩。