用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
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();
}
}
具体实现类就不说了,不复杂,看源码应该看得懂。
写完Service,要做一个RpcServer,其实是个Servlet
RpcServer.java
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() {
}
}
二是将Service放到XmlRpcServer中。使客户端通过xml客户远程调用。
然后就是把这个Servlet注册到web.xml中
<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 display-name>
<welcome-file-list>
<welcome-file>index.jsp welcome-file>
welcome-file-list>
<servlet>
<servlet-name>RpcServer servlet-name>
<servlet-class>org.mstar.appletrpc.rpcserver.RpcServer servlet-class>
<init-param>
<param-name>user-data param-name>
<param-value>/WEB-INF/user.xml param-value>
init-param>
<load-on-startup>1 load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>RpcServer servlet-name>
<url-pattern>/xmlrpc url-pattern>
servlet-mapping>
web-app>
还要有个用户数据库user.xml
<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"/>
user-database>
< init-param >
< param-name > user-data param-name>
<param-value>/WEB-INF/user.xml param-value>
init-param>
接下来就是用Applet调用这个Servlet了。
太长了,就不写在页面上了,源码中有,这里写一个例子,登陆的actionPerformed方法
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能不能打开啊。