今年大家都在炒作Web2.0,其中的一門技術(shù)Ajax也是跟著火了起來(lái),因此前面我寫了一篇名為《忽悠一下AJAX》的文章,簡(jiǎn)單地分析了一下Ajax的技術(shù)的實(shí)質(zhì)。雖然筆者不太喜歡跟風(fēng),但Ajax有一些地方還是比較有用的。前段時(shí)間做了EasyJF開(kāi)源團(tuán)隊(duì)的網(wǎng)上會(huì)議系統(tǒng),就用到了Ajax技術(shù),下面把設(shè)計(jì)思路發(fā)出來(lái)跟大家分享一下。
1、服務(wù)器端核心代碼
在EasyJF開(kāi)源團(tuán)隊(duì)的會(huì)議系統(tǒng)中,由于是以EasyJF官網(wǎng)的論壇系統(tǒng)、后臺(tái)管理等是集成一起的。服務(wù)器ChatService與ChatRoom共同合并到了一個(gè)ChatService.java類中,實(shí)現(xiàn)會(huì)議室管理及會(huì)議服務(wù)功能。ChatService類的部分主要代碼如下:
package com.easyjf.chat.business;
在EasyJF的會(huì)議系統(tǒng)中,由于使用EasyJWeb作為MVC框架,因此處理Ajax比較簡(jiǎn)單,下面是會(huì)議室系統(tǒng)的核心Action主要代碼。
package com.easyjf.chat.action;
public class ChatAction extends AbstractCmdAction {
?private ChatService chatRoom;
?public Object doBefore(WebForm form, Module module) {
?// TODO Auto-generated method stub
??if(chatRoom==null)chatRoom=ChatService.get((String)form.get("cid"));
??return super.doBefore(form, module);
?}
?public Page doInit(WebForm form, Module module) {
??// TODO Auto-generated method stub??
??return doMain(form,module);
?}?
?//用戶登錄進(jìn)入會(huì)議室
?public Page doMain(WebForm form, Module module) {???
??if(chatRoom!=null){
??ChatUser user=getChatUser();??
??if(!chatRoom.join(user))form.addResult("msg","不能加入房間,可能是權(quán)限不夠!");
??form.addResult("chatRoom",chatRoom);
??form.addResult("user",user);
??}
??else
??{
???form.addResult("msg","會(huì)議未啟動(dòng)或者會(huì)議室不存在!");
??}??
??return module.findPage("main");
?}?
?//處理用戶發(fā)言信息
?public Page doSend(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤
??Chat chat=(Chat)form.toPo(Chat.class);
??chat.setCid(chatRoom.geneId());
??chatRoom.talk(chat);
??return doRecive(form,module);
?}?
?//用戶接收發(fā)言信息
?public Page doRecive(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤
??String lastReadId=CommUtil.null2String(form.get("lastReadId"));
??//System.out.println(lastReadId);
??form.addResult("list", chatRoom.getNewestMsg(getChatUser(),lastReadId));??
??return module.findPage("msgList");
?}
?//用戶刷新會(huì)議狀態(tài)信息
?public Page doLoadConfig(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤??
??form.addResult("userList", chatRoom.getUsers());
??form.addResult("talkerList", chatRoom.getTalkers());
??return module.findPage("config");
?}
?//用戶退出
?public Page doExit(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤
??chatRoom.exit(getChatUser());
??form.addResult("msg","退出成功");
??ActionContext.getContext().getSession().removeAttribute("chatUser");
??return new Page("msg","/chat/xmlMsg.xml",Globals.PAGE_TEMPLATE_TYPE);
?}
EasyJF會(huì)議系統(tǒng)中,服務(wù)器發(fā)送給客戶端的都是格式化的xml文檔數(shù)據(jù)。下面是核心的AJAX函數(shù)及發(fā)送接收會(huì)議信息的客戶端代碼。
function newXMLHttpRequest() {
? var xmlreq = false;
? if (window.XMLHttpRequest) {??
??? xmlreq = new XMLHttpRequest();
? } else if (window.ActiveXObject) {???
??? try {?????
????? xmlreq = new ActiveXObject("Msxml2.XMLHTTP");
??? } catch (e1) {?????
????? try {??????
??????? xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
????? } catch (e2) {?????
????? }
??? }
? }
? return xmlreq;
}
//處理返回信息
//xmlHttp返回值,
//method:方法名 方法必須帶一個(gè)參數(shù)如doRecive(xNode);
function handleAjaxResult(req,method) {
? return function () {?
??? if (req.readyState == 4) {????
????? if (req.status == 200) {
????? // 將載有響應(yīng)信息的XML傳遞到處理函數(shù)
??? var objXMLDoc=new ActiveXObject("Microsoft.XMLDOM");
?????? objXMLDoc.loadXML(req.responseText);?? ??
?????? eval("if(objXMLDoc.firstChild)"+method+"(objXMLDoc.firstChild.nextSibling);");
????? } else {??????
??????? //alert("HTTP error: "+req.status);
????? }
??? }
? }
}
//執(zhí)行客戶端Ajax命令
//url 數(shù)據(jù)post地址
//postData 發(fā)送的數(shù)據(jù)包
//handleMethod 處理返回的方法
function executeAjaxCommand(url,postData,handleMethod)
{
?? var req = newXMLHttpRequest();
?? req.onreadystatechange =handleAjaxResult(req,handleMethod);???
?? req.open("POST", url, true);
?? req.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
?? req.setRequestHeader("charset","utf-8");?
?? req.send(postData);
}
//用戶發(fā)言
unction doSend()
{
?
?? if(!check())return false;
?? var msg=EditForm.content.value;
?? var reciver=EditForm.reciver.value;??
?? var url="/chat.ejf?easyJWebCommand=send&cid="+roomId+"&lastReadId="+lastReadId;
?? var postData="sender="+myName+"&reciver="+reciver+"&content="+msg;
?? clearTimeout(reciveTime);
?? executeAjaxCommand(url,postData,"recive");
?? EditForm.content.value="";
}
//接收發(fā)言信息
function doRecive()
{??
?? var reciver=EditForm.reciver.value;??
?? var url="/chat.ejf?easyJWebCommand=recive&cid="+roomId+"&lastReadId="+lastReadId;
?? executeAjaxCommand(url,"","recive");?
}
//處理接收到的發(fā)言信息
function recive(list)
{
??? var id="";??
??? for(var oNode=list.firstChild;oNode;oNode=oNode.nextSibling)?// 依次分析每個(gè)節(jié)點(diǎn)
?{
??? chatContent.innerHTML+=showMsg(oNode);
??? id=oNode.getAttribute("cid");
?}
??? if(id!="") lastReadId=id;
??? chatContent.scrollTop=chatContent.scrollHeight;
??? reciveTime=setTimeout("doRecive();",5000);?
}
六、系統(tǒng)演示
結(jié)束語(yǔ)
Ajax從技術(shù)上講主要就是javascript、dhtml、css、xmldom、xmlhttp等一些我們很早就接觸了的技術(shù)。而xmldom及xmlhttp也沒(méi)有什么東西,寫程序的時(shí)候把參考文檔打開(kāi)Copy就OK,dhtml及javascript涉及的東西就多了,不能只是看參考文檔,需要把他真正消化,并能靈活動(dòng)用,這就需要大家都練習(xí)了。筆者建議大家不要濫用Ajax。對(duì)于高手建議多研究一些業(yè)務(wù)及系統(tǒng)級(jí)算法設(shè)計(jì)等,對(duì)于新手嘛,把基本的技術(shù)(客戶端的包括dhtml、css、javascript、xml等,J2EE服務(wù)器端的設(shè)計(jì)模式、UML建模、Servlet、JDBC或ORM系統(tǒng)、XML、EJB及一些框架、工具等)學(xué)好才是硬道理。
?
一、系統(tǒng)實(shí)現(xiàn)的功能
?
本會(huì)議室系統(tǒng)主要用于EasyJF開(kāi)源團(tuán)隊(duì)的成員網(wǎng)上會(huì)議使用,會(huì)議系統(tǒng)模擬傳統(tǒng)的會(huì)議形式,可以同時(shí)開(kāi)設(shè)多個(gè)不同主題的會(huì)議室,每個(gè)會(huì)議室需要提供訪問(wèn)權(quán)限控制功能,會(huì)議中能夠指定會(huì)議發(fā)言模式(分為排隊(duì)發(fā)言、自由發(fā)言兩種),系統(tǒng)能自動(dòng)記錄每個(gè)會(huì)議室的發(fā)言信息,可以供參會(huì)人員長(zhǎng)期查閱。
會(huì)議系統(tǒng)的用戶支持游客帳號(hào)參加會(huì)議,同時(shí)也提供跟其它用戶系統(tǒng)的接口,比如EasyJF官網(wǎng)中的開(kāi)源論壇系統(tǒng)。
會(huì)議系統(tǒng)暫時(shí)使用文字聊天的方式,并提供語(yǔ)音及視頻的接口。
會(huì)議系統(tǒng)的用戶支持游客帳號(hào)參加會(huì)議,同時(shí)也提供跟其它用戶系統(tǒng)的接口,比如EasyJF官網(wǎng)中的開(kāi)源論壇系統(tǒng)。
會(huì)議系統(tǒng)暫時(shí)使用文字聊天的方式,并提供語(yǔ)音及視頻的接口。
?
二、技術(shù)體系
?
服務(wù)器端使用Java語(yǔ)言,MVC使用EasyJWeb框架;
客戶端使用AJAX技術(shù)與服務(wù)器端交互數(shù)據(jù);
會(huì)議歷史信息儲(chǔ)存格式使用文本格式,方便系統(tǒng)安裝運(yùn)行,也便于管理。
客戶端使用AJAX技術(shù)與服務(wù)器端交互數(shù)據(jù);
會(huì)議歷史信息儲(chǔ)存格式使用文本格式,方便系統(tǒng)安裝運(yùn)行,也便于管理。
?
三、會(huì)議室服務(wù)器端設(shè)計(jì)
?
會(huì)議室服務(wù)器端是整個(gè)會(huì)議系統(tǒng)的核心部分,服務(wù)器端程序設(shè)計(jì)的好壞影響到整個(gè)系統(tǒng)的質(zhì)量。
首先,根據(jù)會(huì)議室要實(shí)現(xiàn)的功能進(jìn)行抽象分析。一個(gè)會(huì)議室對(duì)象,應(yīng)該包括會(huì)議主題、會(huì)議簡(jiǎn)介、參會(huì)人數(shù)限制、公告、會(huì)議室類型、訪問(wèn)權(quán)限設(shè)定、房間密碼、當(dāng)前參會(huì)的人員、當(dāng)前發(fā)言的人員、排隊(duì)等待發(fā)言的人員等參數(shù)信息。我們把他封裝一個(gè)Java對(duì)象當(dāng)中。如下面的ChatRoom代碼所示:
public class ChatRoom{
?private String cid;//主鍵
?private String title;//會(huì)議室主題
?private String intro;//會(huì)議室簡(jiǎn)介
?private String announce;//會(huì)議室公告
?private String owner;//會(huì)議室創(chuàng)建人
?private Integer maxUser;//最大在線人數(shù)
?private Integer intervals;//最大刷新時(shí)間間隔
?private String vrtype;//訪問(wèn)權(quán)限
?private String vrvalue;//訪問(wèn)值
?private Integer status;//會(huì)議室狀態(tài)
?private Date inputTime;
}
首先,根據(jù)會(huì)議室要實(shí)現(xiàn)的功能進(jìn)行抽象分析。一個(gè)會(huì)議室對(duì)象,應(yīng)該包括會(huì)議主題、會(huì)議簡(jiǎn)介、參會(huì)人數(shù)限制、公告、會(huì)議室類型、訪問(wèn)權(quán)限設(shè)定、房間密碼、當(dāng)前參會(huì)的人員、當(dāng)前發(fā)言的人員、排隊(duì)等待發(fā)言的人員等參數(shù)信息。我們把他封裝一個(gè)Java對(duì)象當(dāng)中。如下面的ChatRoom代碼所示:
public class ChatRoom{
?private String cid;//主鍵
?private String title;//會(huì)議室主題
?private String intro;//會(huì)議室簡(jiǎn)介
?private String announce;//會(huì)議室公告
?private String owner;//會(huì)議室創(chuàng)建人
?private Integer maxUser;//最大在線人數(shù)
?private Integer intervals;//最大刷新時(shí)間間隔
?private String vrtype;//訪問(wèn)權(quán)限
?private String vrvalue;//訪問(wèn)值
?private Integer status;//會(huì)議室狀態(tài)
?private Date inputTime;
}
?
需要一個(gè)管理會(huì)議室的類,與會(huì)議有關(guān)的操作(如啟動(dòng)會(huì)議、關(guān)閉會(huì)議)等都直接找他。該類還應(yīng)該即有自動(dòng)定時(shí)檢測(cè)用戶在線情況(防止用戶意外退出)、把內(nèi)存中的會(huì)議歷史發(fā)言信息保存到文本文件中等功能。這里可以考慮使用一個(gè)ChatService類提供這些功能:
public class ChatService implements Runnable {
private static final Map service=new HashMap();//會(huì)議室服務(wù),系統(tǒng)中的當(dāng)前會(huì)議室存放到該表集合中
private static final int maxServices=10;//可以同時(shí)開(kāi)的最大會(huì)議室數(shù)
private static final SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
private final List msgs;//會(huì)議發(fā)言信息Chat
private final List users;//在線用戶,ChatUser
private final List talkers;//排隊(duì)發(fā)言人數(shù)Talker
private final List manager;//會(huì)議室管理員
private Talker currentTalker;//當(dāng)前發(fā)言人
public ChatService()
{
?this.msgs=new ArrayList();
?this.users=new ArrayList();
?this.talkers=new ArrayList();
?this.manager=new ArrayList();
?this.maxUser=1000;//最大1000人同時(shí)
?this.interval=1000*60*5;//5分鐘以前的信息
}
}
public class ChatService implements Runnable {
private static final Map service=new HashMap();//會(huì)議室服務(wù),系統(tǒng)中的當(dāng)前會(huì)議室存放到該表集合中
private static final int maxServices=10;//可以同時(shí)開(kāi)的最大會(huì)議室數(shù)
private static final SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
private final List msgs;//會(huì)議發(fā)言信息Chat
private final List users;//在線用戶,ChatUser
private final List talkers;//排隊(duì)發(fā)言人數(shù)Talker
private final List manager;//會(huì)議室管理員
private Talker currentTalker;//當(dāng)前發(fā)言人
public ChatService()
{
?this.msgs=new ArrayList();
?this.users=new ArrayList();
?this.talkers=new ArrayList();
?this.manager=new ArrayList();
?this.maxUser=1000;//最大1000人同時(shí)
?this.interval=1000*60*5;//5分鐘以前的信息
}
}
?
會(huì)議發(fā)言信息也需要封裝成一個(gè)類,表示發(fā)言人、接收人、內(nèi)容、發(fā)言時(shí)間、類型等,大致如下面的Chat類:
public class Chat? {
private String cid;
private String sender;
private String reciver;
private String content;
private Date vdate;
private Integer types;
private Integer status;
}
public class Chat? {
private String cid;
private String sender;
private String reciver;
private String content;
private Date vdate;
private Integer types;
private Integer status;
}
?
還有表示參加會(huì)議的人的信息,包括參會(huì)人名稱、IP地址、狀態(tài)等,如下面的ChatUser類所示:
public class ChatUser {
private String ip;
private String port;
private String userName;
private Date lastAccessTime;
private Integer status;
}
public class ChatUser {
private String ip;
private String port;
private String userName;
private Date lastAccessTime;
private Integer status;
}
?
另外還需要一個(gè)表示當(dāng)前發(fā)言人的Talker類,表示當(dāng)前的發(fā)言人,發(fā)言開(kāi)始時(shí)間,發(fā)言預(yù)計(jì)結(jié)束時(shí)間等。
在服務(wù)器端的設(shè)計(jì)中,會(huì)議室信息服務(wù)器應(yīng)該能以多線程的方式運(yùn)行,即啟動(dòng)一個(gè)會(huì)議就新開(kāi)一個(gè)線程,每個(gè)會(huì)議線程維護(hù)自己的會(huì)議狀態(tài),如參會(huì)人、發(fā)言人,保存會(huì)議歷史發(fā)言信息以及清空內(nèi)存中的數(shù)據(jù)等操作。
?
?
四、客戶端設(shè)計(jì)
?
會(huì)議室客戶端包括兩個(gè)部分,一個(gè)部分是會(huì)議室的管理界面,主要包會(huì)議室的“添刪改查”及“啟動(dòng)”或“關(guān)閉”會(huì)議服務(wù)的操作。這部分我們直接使用EasyJWeb Tools中的添刪改查業(yè)務(wù)引擎AbstractCrudAction可以快速實(shí)現(xiàn)。界面也比較簡(jiǎn)單,直接使用EasyJWeb Tools代碼生成工具引擎生成即可。會(huì)議室管理的客戶端是傳統(tǒng)的Java Web技術(shù),因此沒(méi)有什么要考慮的。
客戶端的第二個(gè)部分也即會(huì)議系統(tǒng)的主要部分,該部分主要有兩個(gè)界面,第一個(gè)頁(yè)面是會(huì)議室進(jìn)入的選擇頁(yè)面。也即把已經(jīng)啟動(dòng)的會(huì)議室列出來(lái),用戶選擇一個(gè)會(huì)議室進(jìn)入,這個(gè)頁(yè)面也是使用傳統(tǒng)的Java Web技術(shù)。第二個(gè)頁(yè)面是進(jìn)入會(huì)議室后的主界面,這個(gè)界面是整個(gè)會(huì)議系統(tǒng)的主要界面,所有參與會(huì)議的操作都在這里運(yùn)行的。這個(gè)界面需要不斷的與服務(wù)器端交互傳輸數(shù)據(jù),傳輸?shù)膬?nèi)容包括用戶的發(fā)言、其它人給用戶的發(fā)言、會(huì)議室的狀態(tài)等。有的傳輸信息需要即時(shí)響應(yīng)(如用戶發(fā)言),有的信息可以設(shè)置成定時(shí)響應(yīng)(如會(huì)議室狀態(tài))。
Java Web程序中與服務(wù)器端交互數(shù)據(jù)主要有兩種方式,一種是直接刷新頁(yè)面,另外一種是使用Socket直接跟Web服務(wù)器端口通訊。由于Socket編程相對(duì)復(fù)雜,我們選擇第一種直接刷新頁(yè)面的方式,這種方式又可以分為幾種,包括傳統(tǒng)的Form提交,傳統(tǒng)的自動(dòng)刷新網(wǎng)頁(yè)取得數(shù)據(jù)以及使用ActiveXObject對(duì)象(如xmlhttp)直接與服務(wù)器交互數(shù)據(jù),也即AJAX方式。由于使用AJAX方式用戶感覺(jué)不到頁(yè)面在刷新,表現(xiàn)起來(lái)好于手動(dòng)或自動(dòng)刷新頁(yè)面的方式,因此我們決定選擇AJAX方式實(shí)現(xiàn)客戶端與服務(wù)器端進(jìn)行數(shù)據(jù)交互。
客戶端的第二個(gè)部分也即會(huì)議系統(tǒng)的主要部分,該部分主要有兩個(gè)界面,第一個(gè)頁(yè)面是會(huì)議室進(jìn)入的選擇頁(yè)面。也即把已經(jīng)啟動(dòng)的會(huì)議室列出來(lái),用戶選擇一個(gè)會(huì)議室進(jìn)入,這個(gè)頁(yè)面也是使用傳統(tǒng)的Java Web技術(shù)。第二個(gè)頁(yè)面是進(jìn)入會(huì)議室后的主界面,這個(gè)界面是整個(gè)會(huì)議系統(tǒng)的主要界面,所有參與會(huì)議的操作都在這里運(yùn)行的。這個(gè)界面需要不斷的與服務(wù)器端交互傳輸數(shù)據(jù),傳輸?shù)膬?nèi)容包括用戶的發(fā)言、其它人給用戶的發(fā)言、會(huì)議室的狀態(tài)等。有的傳輸信息需要即時(shí)響應(yīng)(如用戶發(fā)言),有的信息可以設(shè)置成定時(shí)響應(yīng)(如會(huì)議室狀態(tài))。
Java Web程序中與服務(wù)器端交互數(shù)據(jù)主要有兩種方式,一種是直接刷新頁(yè)面,另外一種是使用Socket直接跟Web服務(wù)器端口通訊。由于Socket編程相對(duì)復(fù)雜,我們選擇第一種直接刷新頁(yè)面的方式,這種方式又可以分為幾種,包括傳統(tǒng)的Form提交,傳統(tǒng)的自動(dòng)刷新網(wǎng)頁(yè)取得數(shù)據(jù)以及使用ActiveXObject對(duì)象(如xmlhttp)直接與服務(wù)器交互數(shù)據(jù),也即AJAX方式。由于使用AJAX方式用戶感覺(jué)不到頁(yè)面在刷新,表現(xiàn)起來(lái)好于手動(dòng)或自動(dòng)刷新頁(yè)面的方式,因此我們決定選擇AJAX方式實(shí)現(xiàn)客戶端與服務(wù)器端進(jìn)行數(shù)據(jù)交互。
用戶發(fā)言的時(shí)候,直接使用xmlhttp對(duì)象Post數(shù)據(jù)到服務(wù)器。為了能不斷接收到別人的發(fā)言信息,需要定時(shí)不斷的從服務(wù)器端讀取數(shù)據(jù),因此,需要在客戶端啟動(dòng)一個(gè)定時(shí)器,每隔一定的時(shí)候自動(dòng)使用xmlhttp對(duì)象到服務(wù)器端下載別人的發(fā)言信息,并顯示到會(huì)議室信息主界面中。另外還要定時(shí)刷新參會(huì)的人數(shù)、會(huì)議室當(dāng)前發(fā)言人、會(huì)議室的公告等會(huì)議狀態(tài)信息,這也可以通過(guò)從客戶端啟動(dòng)一個(gè)定時(shí)器,通過(guò)xmlhttp對(duì)象與服務(wù)器交互得到。
另外還有一些操作,鎖定會(huì)議室、踢人、指定發(fā)言人的發(fā)言時(shí)間、給會(huì)議室加密碼等功能,也通過(guò)xmlhttp的方式與服務(wù)器傳輸命令實(shí)現(xiàn)。
另外還有一些操作,鎖定會(huì)議室、踢人、指定發(fā)言人的發(fā)言時(shí)間、給會(huì)議室加密碼等功能,也通過(guò)xmlhttp的方式與服務(wù)器傳輸命令實(shí)現(xiàn)。
?
五、核心代碼說(shuō)明
1、服務(wù)器端核心代碼
在EasyJF開(kāi)源團(tuán)隊(duì)的會(huì)議系統(tǒng)中,由于是以EasyJF官網(wǎng)的論壇系統(tǒng)、后臺(tái)管理等是集成一起的。服務(wù)器ChatService與ChatRoom共同合并到了一個(gè)ChatService.java類中,實(shí)現(xiàn)會(huì)議室管理及會(huì)議服務(wù)功能。ChatService類的部分主要代碼如下:
package com.easyjf.chat.business;
public class ChatService implements Runnable {
private static final Map service=new HashMap();//會(huì)議室服務(wù),系統(tǒng)中的當(dāng)前會(huì)議室存放到該表集合中
private static final int maxServices=10;//可以同時(shí)開(kāi)的最大會(huì)議室數(shù)
private static final SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
private final List msgs;//會(huì)議發(fā)言信息Chat
private final List users;//在線用戶,ChatUser
private final List talkers;//排隊(duì)發(fā)言人數(shù)Talker
private final List manager;//會(huì)議室管理員
private Talker currentTalker;//當(dāng)前發(fā)言人
private String cid;//會(huì)議室id
private String title;//會(huì)議室主題
private String intro;//會(huì)議室簡(jiǎn)介
private String owner;//會(huì)議室創(chuàng)建人
private int maxUser;//最大在線人數(shù)
private int interval;//最大刷新時(shí)間間隔
private String vrtype;//訪問(wèn)權(quán)限
private String vrvalue;//訪問(wèn)值
private String announce;
private String password;//房間進(jìn)入密碼
private int status;//會(huì)議室狀態(tài)
private String filePath;
//private Thread thread;
private boolean isStop=false;
public ChatService()
{
?this.msgs=new ArrayList();
?this.users=new ArrayList();
?this.talkers=new ArrayList();
?this.manager=new ArrayList();
?this.maxUser=1000;//最大1000人同時(shí)
?this.interval=1000*60*5;//5分鐘以前的信息
}
/**
?* 停止所有會(huì)議室
?*
?*/
public? static void clear()
{
?if(!service.isEmpty())
?{
??Iterator it=service.values().iterator();
??while(it.hasNext())
??{
???ChatService chat=(ChatService)it.next();
???chat.stop();
??}
?}
??service.clear();
}
/**
?* 創(chuàng)建一個(gè)會(huì)議室
?* @param name 會(huì)議室ID
?* @return
?*/
public static ChatService create(String name)
{
ChatService ret=null;
if(service.containsKey(name))
{
?ChatService s=(ChatService)service.get(name);
?s.stop();
?service.remove(name);
}
if(service.size()<maxServices)
{
?ret=new ChatService();?
?service.put(name,ret);
}
return ret;
}
/**
?* 停止某個(gè)會(huì)議室
?* @param name 會(huì)議室ID
?* @return
?*/
public static boolean close(String name)
{
?ChatService chatRoom=ChatService.get(name);
?if(chatRoom!=null)
??{
??chatRoom.stop();
??service.remove(name);
??}
?return true;
}
/**
?* 獲得一個(gè)會(huì)議室信息
?* @param name 會(huì)議室ID
?* @return
?*/
public static ChatService get(String name)
{
?if(service.containsKey(name))return (ChatService)service.get(name);
?else return null;
}
private static final Map service=new HashMap();//會(huì)議室服務(wù),系統(tǒng)中的當(dāng)前會(huì)議室存放到該表集合中
private static final int maxServices=10;//可以同時(shí)開(kāi)的最大會(huì)議室數(shù)
private static final SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
private final List msgs;//會(huì)議發(fā)言信息Chat
private final List users;//在線用戶,ChatUser
private final List talkers;//排隊(duì)發(fā)言人數(shù)Talker
private final List manager;//會(huì)議室管理員
private Talker currentTalker;//當(dāng)前發(fā)言人
private String cid;//會(huì)議室id
private String title;//會(huì)議室主題
private String intro;//會(huì)議室簡(jiǎn)介
private String owner;//會(huì)議室創(chuàng)建人
private int maxUser;//最大在線人數(shù)
private int interval;//最大刷新時(shí)間間隔
private String vrtype;//訪問(wèn)權(quán)限
private String vrvalue;//訪問(wèn)值
private String announce;
private String password;//房間進(jìn)入密碼
private int status;//會(huì)議室狀態(tài)
private String filePath;
//private Thread thread;
private boolean isStop=false;
public ChatService()
{
?this.msgs=new ArrayList();
?this.users=new ArrayList();
?this.talkers=new ArrayList();
?this.manager=new ArrayList();
?this.maxUser=1000;//最大1000人同時(shí)
?this.interval=1000*60*5;//5分鐘以前的信息
}
/**
?* 停止所有會(huì)議室
?*
?*/
public? static void clear()
{
?if(!service.isEmpty())
?{
??Iterator it=service.values().iterator();
??while(it.hasNext())
??{
???ChatService chat=(ChatService)it.next();
???chat.stop();
??}
?}
??service.clear();
}
/**
?* 創(chuàng)建一個(gè)會(huì)議室
?* @param name 會(huì)議室ID
?* @return
?*/
public static ChatService create(String name)
{
ChatService ret=null;
if(service.containsKey(name))
{
?ChatService s=(ChatService)service.get(name);
?s.stop();
?service.remove(name);
}
if(service.size()<maxServices)
{
?ret=new ChatService();?
?service.put(name,ret);
}
return ret;
}
/**
?* 停止某個(gè)會(huì)議室
?* @param name 會(huì)議室ID
?* @return
?*/
public static boolean close(String name)
{
?ChatService chatRoom=ChatService.get(name);
?if(chatRoom!=null)
??{
??chatRoom.stop();
??service.remove(name);
??}
?return true;
}
/**
?* 獲得一個(gè)會(huì)議室信息
?* @param name 會(huì)議室ID
?* @return
?*/
public static ChatService get(String name)
{
?if(service.containsKey(name))return (ChatService)service.get(name);
?else return null;
}
public void run() {
?// TODO Auto-generated method stub
?//this.thread=Thread.currentThread();
?while(!isStop)
?{
?//System.out.println("開(kāi)始監(jiān)控一個(gè)會(huì)議室!"+this.title);
?this.flash();
?try{
?Thread.sleep(5000);
?}
?catch(Exception e)
?{
??e.printStackTrace();??
?}?
?}
?//System.out.println("結(jié)束!");
}
public void stop()
{
?this.flashAll();
?isStop=true;
}
//會(huì)議室中有人發(fā)言
public boolean talk(Chat chat)
{
?boolean ret=false;
?if(canTalk(chat.getSender()))
??{??
??this.msgs.add(chat);
????? ret=true;
??}
?return ret;
}
?// TODO Auto-generated method stub
?//this.thread=Thread.currentThread();
?while(!isStop)
?{
?//System.out.println("開(kāi)始監(jiān)控一個(gè)會(huì)議室!"+this.title);
?this.flash();
?try{
?Thread.sleep(5000);
?}
?catch(Exception e)
?{
??e.printStackTrace();??
?}?
?}
?//System.out.println("結(jié)束!");
}
public void stop()
{
?this.flashAll();
?isStop=true;
}
//會(huì)議室中有人發(fā)言
public boolean talk(Chat chat)
{
?boolean ret=false;
?if(canTalk(chat.getSender()))
??{??
??this.msgs.add(chat);
????? ret=true;
??}
?return ret;
}
public boolean exit(ChatUser user)
{????
? talk(geneSystemMsg(user.getUserName()+"退出了會(huì)議室!"));
? return this.users.remove(user);
}
}
//刷新信息,保存會(huì)議信息
public void flash()
{
?flashChatMsg();
?flashChatUser();
}
}
{????
? talk(geneSystemMsg(user.getUserName()+"退出了會(huì)議室!"));
? return this.users.remove(user);
}
}
//刷新信息,保存會(huì)議信息
public void flash()
{
?flashChatMsg();
?flashChatUser();
}
}
?
?
2、MVC處理部分的Action代碼
?
在EasyJF的會(huì)議系統(tǒng)中,由于使用EasyJWeb作為MVC框架,因此處理Ajax比較簡(jiǎn)單,下面是會(huì)議室系統(tǒng)的核心Action主要代碼。
package com.easyjf.chat.action;
public class ChatAction extends AbstractCmdAction {
?private ChatService chatRoom;
?public Object doBefore(WebForm form, Module module) {
?// TODO Auto-generated method stub
??if(chatRoom==null)chatRoom=ChatService.get((String)form.get("cid"));
??return super.doBefore(form, module);
?}
?public Page doInit(WebForm form, Module module) {
??// TODO Auto-generated method stub??
??return doMain(form,module);
?}?
?//用戶登錄進(jìn)入會(huì)議室
?public Page doMain(WebForm form, Module module) {???
??if(chatRoom!=null){
??ChatUser user=getChatUser();??
??if(!chatRoom.join(user))form.addResult("msg","不能加入房間,可能是權(quán)限不夠!");
??form.addResult("chatRoom",chatRoom);
??form.addResult("user",user);
??}
??else
??{
???form.addResult("msg","會(huì)議未啟動(dòng)或者會(huì)議室不存在!");
??}??
??return module.findPage("main");
?}?
?//處理用戶發(fā)言信息
?public Page doSend(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤
??Chat chat=(Chat)form.toPo(Chat.class);
??chat.setCid(chatRoom.geneId());
??chatRoom.talk(chat);
??return doRecive(form,module);
?}?
?//用戶接收發(fā)言信息
?public Page doRecive(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤
??String lastReadId=CommUtil.null2String(form.get("lastReadId"));
??//System.out.println(lastReadId);
??form.addResult("list", chatRoom.getNewestMsg(getChatUser(),lastReadId));??
??return module.findPage("msgList");
?}
?//用戶刷新會(huì)議狀態(tài)信息
?public Page doLoadConfig(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤??
??form.addResult("userList", chatRoom.getUsers());
??form.addResult("talkerList", chatRoom.getTalkers());
??return module.findPage("config");
?}
?//用戶退出
?public Page doExit(WebForm form, Module module) {??
??if(chatRoom==null)return new Page("err","/err.html","thml");//返回會(huì)議室不存在的錯(cuò)誤
??chatRoom.exit(getChatUser());
??form.addResult("msg","退出成功");
??ActionContext.getContext().getSession().removeAttribute("chatUser");
??return new Page("msg","/chat/xmlMsg.xml",Globals.PAGE_TEMPLATE_TYPE);
?}
?
?
3、客戶端AJAX部分核心代碼
?
EasyJF會(huì)議系統(tǒng)中,服務(wù)器發(fā)送給客戶端的都是格式化的xml文檔數(shù)據(jù)。下面是核心的AJAX函數(shù)及發(fā)送接收會(huì)議信息的客戶端代碼。
function newXMLHttpRequest() {
? var xmlreq = false;
? if (window.XMLHttpRequest) {??
??? xmlreq = new XMLHttpRequest();
? } else if (window.ActiveXObject) {???
??? try {?????
????? xmlreq = new ActiveXObject("Msxml2.XMLHTTP");
??? } catch (e1) {?????
????? try {??????
??????? xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
????? } catch (e2) {?????
????? }
??? }
? }
? return xmlreq;
}
//處理返回信息
//xmlHttp返回值,
//method:方法名 方法必須帶一個(gè)參數(shù)如doRecive(xNode);
function handleAjaxResult(req,method) {
? return function () {?
??? if (req.readyState == 4) {????
????? if (req.status == 200) {
????? // 將載有響應(yīng)信息的XML傳遞到處理函數(shù)
??? var objXMLDoc=new ActiveXObject("Microsoft.XMLDOM");
?????? objXMLDoc.loadXML(req.responseText);?? ??
?????? eval("if(objXMLDoc.firstChild)"+method+"(objXMLDoc.firstChild.nextSibling);");
????? } else {??????
??????? //alert("HTTP error: "+req.status);
????? }
??? }
? }
}
//執(zhí)行客戶端Ajax命令
//url 數(shù)據(jù)post地址
//postData 發(fā)送的數(shù)據(jù)包
//handleMethod 處理返回的方法
function executeAjaxCommand(url,postData,handleMethod)
{
?? var req = newXMLHttpRequest();
?? req.onreadystatechange =handleAjaxResult(req,handleMethod);???
?? req.open("POST", url, true);
?? req.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
?? req.setRequestHeader("charset","utf-8");?
?? req.send(postData);
}
//用戶發(fā)言
unction doSend()
{
?
?? if(!check())return false;
?? var msg=EditForm.content.value;
?? var reciver=EditForm.reciver.value;??
?? var url="/chat.ejf?easyJWebCommand=send&cid="+roomId+"&lastReadId="+lastReadId;
?? var postData="sender="+myName+"&reciver="+reciver+"&content="+msg;
?? clearTimeout(reciveTime);
?? executeAjaxCommand(url,postData,"recive");
?? EditForm.content.value="";
}
//接收發(fā)言信息
function doRecive()
{??
?? var reciver=EditForm.reciver.value;??
?? var url="/chat.ejf?easyJWebCommand=recive&cid="+roomId+"&lastReadId="+lastReadId;
?? executeAjaxCommand(url,"","recive");?
}
//處理接收到的發(fā)言信息
function recive(list)
{
??? var id="";??
??? for(var oNode=list.firstChild;oNode;oNode=oNode.nextSibling)?// 依次分析每個(gè)節(jié)點(diǎn)
?{
??? chatContent.innerHTML+=showMsg(oNode);
??? id=oNode.getAttribute("cid");
?}
??? if(id!="") lastReadId=id;
??? chatContent.scrollTop=chatContent.scrollHeight;
??? reciveTime=setTimeout("doRecive();",5000);?
}
?
六、系統(tǒng)演示
?
大家可以到EasyJF開(kāi)源團(tuán)隊(duì)的官方網(wǎng)站看程序演示效果,地址是:
結(jié)束語(yǔ)
Ajax從技術(shù)上講主要就是javascript、dhtml、css、xmldom、xmlhttp等一些我們很早就接觸了的技術(shù)。而xmldom及xmlhttp也沒(méi)有什么東西,寫程序的時(shí)候把參考文檔打開(kāi)Copy就OK,dhtml及javascript涉及的東西就多了,不能只是看參考文檔,需要把他真正消化,并能靈活動(dòng)用,這就需要大家都練習(xí)了。筆者建議大家不要濫用Ajax。對(duì)于高手建議多研究一些業(yè)務(wù)及系統(tǒng)級(jí)算法設(shè)計(jì)等,對(duì)于新手嘛,把基本的技術(shù)(客戶端的包括dhtml、css、javascript、xml等,J2EE服務(wù)器端的設(shè)計(jì)模式、UML建模、Servlet、JDBC或ORM系統(tǒng)、XML、EJB及一些框架、工具等)學(xué)好才是硬道理。