用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
display-name>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
<servlet>
<servlet-name>RpcServerservlet-name>
<servlet-class>org.mstar.appletrpc.rpcserver.RpcServerservlet-class>
<init-param>
<param-name>user-dataparam-name>
<param-value>/WEB-INF/user.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>RpcServerservlet-name>
<url-pattern>/xmlrpcurl-pattern>
servlet-mapping>
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"
/>
user-database>
注意user.xml的路径是在web.xml中设定的。
<
init-param
>
<
param-name
>
user-data
param-name>
<param-value>/WEB-INF/user.xmlparam-value>
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能不能打开啊。