用applet + xml-rpc + hsqldb + xml实现一个聊天室
昨天公司前辈看我没什么事就托我给他做个小聊天室,估计是别人托他做的L,某个在校学生的作业(其实我也刚从学校里出来,呵呵)。没办法,做吧,其实前辈给了我一个网上下的源程序让我改一下。我看了看,其中有些错误,改了以后,前辈又说要加些功能,可是原来的那个源码使用socket做的,要实现这样的功能,有些麻烦,功能其实很简单,不如自己做个玩玩。于是想到了,以前看到的一些技术和类库,就当练练手吧。
首先要解决的问题是applet如何与server进行通信。发放有很多,但是对于这样的小东西,最好用一个轻量级的实现起来简单的技术,于是我想到了xml-rpc,以前做个用javascript通过xml-rpc与服务端通信(我以前的文章中写过),现在客户端也是Java实现起来就容易多了。Xml-rpc是个技术规范,有很多实现,现在客户端和服务器都用Java,当然要用apache的实现。
接下来要解决的问题是用户数据库如何实现,这样一个小程序就不用精通mysql或sqlserver的“大驾”了。本想用hsqldb一起实现的了,但是想了想,不易管理,用户要想更改用户,hsqldb没有一个很好的工具,而且,用户数据库,其实就是一张用户表,有没有大的访问量,完全可以用xml来储存。于是我还是决定用dom4j来操作揖个user.xml来实现。
下面是聊天信息的中转,和在线状态的维护。其实这个自己写一个类来实现也可以,无非就是维护一个map,但是想到自己还是没有那么高的水平一定能把这个做好,莫不如用内存数据库来实现,效率也不错,操作也方便。这时hsqldb就派上用场了。其实hsqldb的用途还是很大的。
好了下面是总体框架
对了,还有一个monitor,是用来维护用户在线状态的,因为用户很有可能不是正常退出,所以这个monitor作为一个单独的线程对超时用户进行处理。
Service(ChatService.java)是主业务类,其实它也只是一个代理类,实际业务是由ChatEngine.java(hsql)和XmlHelper.java(xml)完成的。
先看一下截图吧
下面详细的介绍一下,每部分的实现
首先是业务逻辑类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();
}
}
这个类是个很普通的类,为底层的业务逻辑实现者做个代理。这里要注意的是由于xml-rpc支持的Type很有限,所以,很多是由返回值得类型要注意一下,数组要用Vector,对象要用HashTable或Map,不能直接返回一个复杂对象类型,如User之类的。
具体实现类就不说了,不复杂,看源码应该看得懂。
写完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() {
}
}
这个类主要做两件事,一是初始化一些业务对象如ChatService和XmlHelper,ChatEngine,启动monitor。
二是将Service放到XmlRpcServer中。使客户端通过xml客户远程调用。
然后就是把这个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>
还要有个用户数据库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中设定的。
<init-param>
<param-name>user-data< SPAN>param-name>
<param-value>/WEB-INF/user.xml< SPAN>param-value>
< SPAN>init-param>
接下来就是用Applet调用这个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 + ": 您已经登陆成功");
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执行远程调用的方法。
第一个参数是方法名,第二个是这个远程方法的参数,用Vector传递。
基本上就是这样一个流程。一点都不复杂,有什么不明白的就给我发信,欢迎讨论。
源码下载:http://www.blogjava.net/Files/mstar/AppletRPC.rar
项目是用JBuilder2006做的,不知道以前版本的JBuilder能不能打开啊。