用applet + xml-rpc + hsqldb + xml實現(xiàn)一個聊天室
昨天公司前輩看我沒什么事就托我給他做個小聊天室,估計是別人托他做的L,某個在校學(xué)生的作業(yè)(其實我也剛從學(xué)校里出來,呵呵)。沒辦法,做吧,其實前輩給了我一個網(wǎng)上下的源程序讓我改一下。我看了看,其中有些錯誤,改了以后,前輩又說要加些功能,可是原來的那個源碼使用socket做的,要實現(xiàn)這樣的功能,有些麻煩,功能其實很簡單,不如自己做個玩玩。于是想到了,以前看到的一些技術(shù)和類庫,就當(dāng)練練手吧。
首先要解決的問題是applet如何與server進行通信。發(fā)放有很多,但是對于這樣的小東西,最好用一個輕量級的實現(xiàn)起來簡單的技術(shù),于是我想到了xml-rpc,以前做個用javascript通過xml-rpc與服務(wù)端通信(我以前的文章中寫過),現(xiàn)在客戶端也是Java實現(xiàn)起來就容易多了。Xml-rpc是個技術(shù)規(guī)范,有很多實現(xiàn),現(xiàn)在客戶端和服務(wù)器都用Java,當(dāng)然要用apache的實現(xiàn)。
接下來要解決的問題是用戶數(shù)據(jù)庫如何實現(xiàn),這樣一個小程序就不用精通mysql或sqlserver的“大駕”了。本想用hsqldb一起實現(xiàn)的了,但是想了想,不易管理,用戶要想更改用戶,hsqldb沒有一個很好的工具,而且,用戶數(shù)據(jù)庫,其實就是一張用戶表,有沒有大的訪問量,完全可以用xml來儲存。于是我還是決定用dom4j來操作揖個user.xml來實現(xiàn)。
下面是聊天信息的中轉(zhuǎn),和在線狀態(tài)的維護。其實這個自己寫一個類來實現(xiàn)也可以,無非就是維護一個map,但是想到自己還是沒有那么高的水平一定能把這個做好,莫不如用內(nèi)存數(shù)據(jù)庫來實現(xiàn),效率也不錯,操作也方便。這時hsqldb就派上用場了。其實hsqldb的用途還是很大的。
好了下面是總體框架

對了,還有一個monitor,是用來維護用戶在線狀態(tài)的,因為用戶很有可能不是正常退出,所以這個monitor作為一個單獨的線程對超時用戶進行處理。
Service(ChatService.java)是主業(yè)務(wù)類,其實它也只是一個代理類,實際業(yè)務(wù)是由ChatEngine.java(hsql)和XmlHelper.java(xml)完成的。
先看一下截圖吧

下面詳細(xì)的介紹一下,每部分的實現(xiàn)
首先是業(yè)務(wù)邏輯類ChatService.java
package org.mstar.appletrpc.rpcserver;
import java.util.*;
public class ChatService {
private XmlHelper helper;
private ChatEngine engine;
public ChatService(XmlHelper helper,ChatEngine engine){
this.helper = helper;
this.engine = engine;
}
public boolean validateUser(String username,String password){
User user = helper.getUser(username);
if(user!=null&&user.getPassword()!=null&&user.getPassword().equals(password)){
return true;
} else {
return false;
}
}
public boolean existUser(String username){
return helper.existUser(username);
}
public boolean addUser(String username,String password,String nickname){
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setNickname(nickname);
return helper.addUser(user);
}
public boolean addMessage(String sender, String receiver, String content) {
return engine.addMessage(sender,receiver,content);
}
public Map getMessage(String receiver) {
return engine.getMessage(receiver);
}
public boolean logon(String user){
return engine.logon(user);
}
public Vector getOnline(){
return engine.getOnline();
}
public boolean logoff(String user){
return engine.logoff(user);
}
/**
* checkOnline
*/
public void checkOnline() {
engine.checkOnline();
}
}
這個類是個很普通的類,為底層的業(yè)務(wù)邏輯實現(xiàn)者做個代理。這里要注意的是由于xml-rpc支持的Type很有限,所以,很多是由返回值得類型要注意一下,數(shù)組要用Vector,對象要用HashTable或Map,不能直接返回一個復(fù)雜對象類型,如User之類的。
具體實現(xiàn)類就不說了,不復(fù)雜,看源碼應(yīng)該看得懂。
寫完Service,要做一個RpcServer,其實是個Servlet
RpcServer.java
package org.mstar.appletrpc.rpcserver;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import org.apache.xmlrpc.XmlRpcServer;
import org.dom4j.*;
import java.sql.SQLException;
public class RpcServer extends HttpServlet {
private ServletConfig servletConfig;
private XmlHelper xmlHelper;
private ChatEngine engine;
private XmlRpcServer xmlrpc;
//Initialize global variables
public void init(ServletConfig servletConfig) throws ServletException {
this.servletConfig = servletConfig;
String userdata = servletConfig.getInitParameter("user-data");
File file = new File(servletConfig.getServletContext().getRealPath(userdata));
try {
xmlHelper = new XmlHelper(file);
engine = new ChatEngine();
ChatService service = new ChatService(xmlHelper,engine);
xmlrpc = new XmlRpcServer();
xmlrpc.addHandler("ChatService",service);
new OnlineMonitor(service).start();
} catch (DocumentException ex) {
ex.printStackTrace();
} catch (SQLException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
//Process the HTTP Post request
public void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
byte[] result = xmlrpc.execute(request.getInputStream());
response.setContentType("text/xml");
response.setContentLength(result.length);
OutputStream out = response.getOutputStream();
out.write(result);
out.flush();
}
//Clean up resources
public void destroy() {
}
}
這個類主要做兩件事,一是初始化一些業(yè)務(wù)對象如ChatService和XmlHelper,ChatEngine,啟動monitor。
二是將Service放到XmlRpcServer中。使客戶端通過xml客戶遠(yuǎn)程調(diào)用。
然后就是把這個Servlet注冊到web.xml中
xml version="1.0" encoding="UTF-8"?>
<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>AppRpc< SPAN>display-name>
<welcome-file-list>
<welcome-file>index.jsp< SPAN>welcome-file>
< SPAN>welcome-file-list>
<servlet>
<servlet-name>RpcServer< SPAN>servlet-name>
<servlet-class>org.mstar.appletrpc.rpcserver.RpcServer< SPAN>servlet-class>
<init-param>
<param-name>user-data< SPAN>param-name>
<param-value>/WEB-INF/user.xml< SPAN>param-value>
< SPAN>init-param>
<load-on-startup>1< SPAN>load-on-startup>
< SPAN>servlet>
<servlet-mapping>
<servlet-name>RpcServer< SPAN>servlet-name>
<url-pattern>/xmlrpc< SPAN>url-pattern>
< SPAN>servlet-mapping>
< SPAN>web-app>
還要有個用戶數(shù)據(jù)庫user.xml
xml version="1.0" encoding="GBK"?>
<user-database>
<user id="mty1" password="123" nickname="馬天一1"/>
<user id="mty2" password="123" nickname="馬天一2"/>
<user id="mty3" password="123" nickname="馬天一3"/>
<user id="mty4" password="123" nickname="馬天一4"/>
< SPAN>user-database>
注意user.xml的路徑是在web.xml中設(shè)定的。
<init-param>
<param-name>user-data< SPAN>param-name>
<param-value>/WEB-INF/user.xml< SPAN>param-value>
< SPAN>init-param>
接下來就是用Applet調(diào)用這個Servlet了。
太長了,就不寫在頁面上了,源碼中有,這里寫一個例子,登陸的actionPerformed方法
public void actionPerformed(ActionEvent e) {
Vector params = new Vector();
params.addElement(useridTextField.getText());
params.addElement(new String(pwdField.getPassword()));
Boolean result = new Boolean(false);
try {
XmlRpcClient xmlrpc = new XmlRpcClient(getCodeBase().toString() +
"xmlrpc");
result = (Boolean) xmlrpc.execute("ChatService.validateUser",
params);
if (result.booleanValue()) {
params = new Vector();
params.addElement(useridTextField.getText());
result = (Boolean) xmlrpc.execute("ChatService.logon",
params);
if (result.booleanValue()) {
userid = useridTextField.getText();
appendMessage(userid + ": 您已經(jīng)登陸成功");
resetUserList();
new Timer(2000, new RefreshMessageActionListener()).
start();
new Timer(5000, new RefreshListActionListener()).
start();
}
} else {
appendMessage("用戶名密碼錯誤,登陸失敗");
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (XmlRpcException ex) {
ex.printStackTrace();
}
}
xmlrpc.execute("ChatService.validateUser",params);
execute方法是xml-rpc client執(zhí)行遠(yuǎn)程調(diào)用的方法。
第一個參數(shù)是方法名,第二個是這個遠(yuǎn)程方法的參數(shù),用Vector傳遞。
基本上就是這樣一個流程。一點都不復(fù)雜,有什么不明白的就給我發(fā)信,歡迎討論。
源碼下載:http://www.tkk7.com/Files/mstar/AppletRPC.rar
項目是用JBuilder2006做的,不知道以前版本的JBuilder能不能打開啊。