根据网上一些reverse ajax例子,自己随便写了个群内聊天的例子,只实现了群聊天,其它一些杂七杂八的都没实现,写这么个功能只是学习下reverse ajax而已,了解服务器推技术。
?
开发工具:eclipse 3.4 纯净版
环境:tomcat 6
技术:DWR
?
工程类说明:
ChatManager.java 聊天实现类
Message.java 消息封装类
OnlineCounter.java 在线人数计算方法
OnlineCounterListener.java? 统计在线人数
User.java? 用户bean
?
页面:
index.jsp? --输入http:127.0.0.1:8080/ichat自动访问此页面
ShowModel_Frames.jsp? --登录之后群聊天的主界面
example.jsp? --FCKEDIT编辑器页面
excute_sent.jsp? --?消息发送页面
online_list.jsp? --在线列表页面
show_msg.jsp? --显示消息页面
?
?
代码:
?
ChatManager.java 聊天实现类:
package com.ccic.chat; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.directwebremoting.ScriptSession; import org.directwebremoting.ServerContextFactory; import org.directwebremoting.WebContext; import org.directwebremoting.WebContextFactory; import org.directwebremoting.proxy.dwr.Util; /** * 处理聊天相关 * * * */ public class ChatManager { /** 保存当前在线用户列表 */ public static List<User> users = new ArrayList<User>(); /** * 更新在线用户列表 * @param request */ public void updateUsersList(HttpServletRequest request) { User user = null; String flag="0";//标识用户,0:不存在 1:已存在 String name= request.getSession().getAttribute("uname").toString(); String pwd= request.getSession().getAttribute("pwd").toString(); System.out.println("用户ID="+name+" 用户密码="+pwd); user = new User(pwd, name); //保存用户到列表 //如果列表中无任何用户则添加,否则循环查找列表中是否已存在该用户, //如果不存在,则添加。如果存在,则不添加 if(users.size() == 0){ users.add(user); }else{ for(int i=0;i<users.size();i++){ User us =(User)users.get(i); if(us.getUsername().equals(name)){ flag="1"; break; } } if(flag.equals("0")){ users.add(user); } } /*//统计在线人数 long count=OnlineCounter.getOnline(); StringBuffer sb=new StringBuffer(); sb.append("<script language='JavaScript' defer='defer'>"); sb.append("d = new dTree(\'d\');"); sb.append("d.add(0,-1,'在线用户列表(当前"+count+"人)');"); for(int i=0;i<users.size();i++){ User us =(User)users.get(i); sb.append("d.add("+(i+1)+",0,'"+us.getUsername()+"','','','');"); } sb.append("document.write(d);"); sb.append("d.config.useCookies=false;"); sb.append("d.config.closeSameLevel=true;"); sb.append("</script");//注意这里并不是好了“>”括号,而是在页面另有处理 System.out.println("dd="+sb.toString());*/ //将用户id和页面脚本session绑定 //this.setScriptSessionFlag(user.getUsername()); // 获得WEB上下文 WebContext wctx = WebContextFactory.get(); //获得在线列表 页面的所有脚本session Collection sessions = wctx.getScriptSessionsByPage("/ichat/pages/main/online_list.jsp"); Util util = new Util(sessions); //处理这些页面中的一些元素 //util.addFunctionCall("cBack_list", sb.toString()); util.removeAllOptions("users"); util.addOptions("users", users, "username"); } /** * 将用户id和页面脚本session绑定 * @param userid */ public void setScriptSessionFlag(String userid) { WebContextFactory.get().getScriptSession().setAttribute("userid", userid); } /** * 根据用户id获得指定用户的页面脚本session * @param userid * @param request * @return */ @SuppressWarnings("unchecked") public ScriptSession getScriptSession(String userid, HttpServletRequest request) { ScriptSession scriptSessions = null; Collection<ScriptSession> sessions = new HashSet<ScriptSession>(); sessions.addAll(ServerContextFactory.get(request.getSession().getServletContext()) .getScriptSessionsByPage("/chat/index.jsp")); for (ScriptSession session : sessions) { String xuserid = (String) session.getAttribute("userid"); if (xuserid != null && xuserid.equals(userid)) { scriptSessions = session; } } return scriptSessions; } /** * 发送消息 * @param sender 发送人 * @param msg 发送内容 * @param request 发送请求 */ public void send(String sender,String msg,HttpServletRequest request){ System.out.println("sender="+sender+" msg="+msg); LinkedList<Message> messages = new LinkedList<Message>(); if (msg != null && msg.trim().length() > 0) { //AA 说(2010-07-13): <br/>你好! messages.addFirst(new Message(sender,msg)); while (messages.size() > 3) { messages.removeLast(); } } // 获得WEB上下文 WebContext wctx = WebContextFactory.get(); //向指定页面推送消息 Collection sessions = wctx.getScriptSessionsByPage("/ichat/pages/main/show_msg.jsp"); Util utilAll = new Util(sessions); utilAll.addFunctionCall("callBack", messages); } //获得离线消息,思路:当A发消息给B时,将A用户发送的消息保存到B用户的ScriptSession中, //当B用户上线时把已经保持在B的ScriptSession中的消息读取处理并全部推送到页面。 public void getOfflineMsg(){ } }
?
?
Message.java 消息封装类
package com.ccic.chat; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import org.directwebremoting.Security; public class Message { DateFormat df = new SimpleDateFormat("HH:mm:ss"); private String serverTime = df.format(new Date()); private String text; private String online; public String getOnline() { return online; } public void setOnline(String online) { this.online = online; } public Message(String sender,String newtext) { text = newtext; if (text.length() > 256) { text = text.substring(0, 256); } text = sender+" 说 "+"("+serverTime+"):<br/>"+text; } public String getText() { return text; } }
?
?
OnlineCounter.java 在线人数计算方法
?
package com.ccic.chat; public class OnlineCounter { private static long online = 0; public static long getOnline() { return online; } public static void raise() { online++; } public static void reduce() { online--; } }
?
?
OnlineCounterListener.java? 统计在线人数
?
package com.ccic.chat; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * 统计在线用户数量 * */ public class OnlineCounterListener implements HttpSessionListener{ /** * 当SESSION创建时,自动增加 */ public void sessionCreated(HttpSessionEvent hse) { OnlineCounter.raise(); } /** * 当SESSION销毁时,自动减少 */ public void sessionDestroyed(HttpSessionEvent hse){ OnlineCounter.reduce(); } }
?
?
User.java? 用户bean
?
?
package com.ccic.chat; /** * 用户 * * * */ public class User { private String password; private String username; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public User(String password, String username) { super(); this.password = password; this.username = username; } }
?
?
index.jsp? --输入http:127.0.0.1:8080/ichat自动访问此页面
?
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <title>Insert title here</title> <script language="javascript" type="text/javascript"> function subm(){ var uname=document.form1.username.value; var pwd=document.form1.password.value; if (uname == "") { alert("请输入用户ID"); return; } window.location="<%=request.getContextPath() %>/pages/frames/ShowModel_Frames.jsp?uname="+uname+"&pwd="+pwd; } </script> </head> <body> <form id="ff" name="form1"> <fieldset> <legend>登录</legend> <table align="center"> <tr> <td>username:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>password:</td> <td><input type="text" name="password"></td> </tr> <tr align="center"> <td colspan="2"><input type="button" name="sub" value="登录" onclick="subm();"><input type="reset" name="res" value="重置"></td> </tr> </table> </fieldset> </form> </body> </html>
?
?
ShowModel_Frames.jsp? --登录之后群聊天的主界面
?
<%@ page language="java" pageEncoding="GB2312"%> <% //(模拟)获取登录的唯一用户ID,放到SESSION供online_list.jsp加载在线用户列表 String uname = request.getParameter("uname"); uname=new String(uname.getBytes("ISO-8859-1"),"gbk"); String pwd = request.getParameter("pwd"); request.getSession().setAttribute("uname",uname); request.getSession().setAttribute("pwd",pwd); %> <html> <head> <meta HTTP-EQUIV="Pragma" CONTENT="no-cache"> <title>聊天主界面框架</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <frameset cols="*,230" frameborder="yes" border="0" framespacing="0"> <frameset rows="400,*" frameborder=yes border="5" framespacing="5"> <frame src="<%=request.getContextPath() %>/pages/main/show_msg.jsp" name="main_one" scrolling=yes noresize> <frame src="<%=request.getContextPath() %>/pages/main/excute_sent.jsp" name="main_two" scrolling=yes noresize> </frameset> <frame src="<%=request.getContextPath() %>/pages/main/online_list.jsp" name="main_top" scrolling=no noresize> </frameset> <noframes> <body> 很抱谦,您使用的浏览器不支持框架功能,请采用新版本的浏览器。 </body> </noframes> </html>
?
?
example.jsp? --FCKEDIT编辑器页面
excute_sent.jsp? --?消息发送页面
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <script type="text/javascript" src="<%=request.getContextPath() %>/fckeditor/fckeditor.js"></script> <script type='text/javascript' src='/ichat/dwr/interface/ChatManager.js'></script> <script type='text/javascript' src='/ichat/dwr/engine.js'></script> <script type='text/javascript' src='/ichat/dwr/util.js'></script> <script language="javascript"> /** * 发送消息 */ function send() { var sender = '<%=request.getSession().getAttribute("uname").toString() %>'; // 获得发送者名字 //var receiver = dwr.util.getValue('receiver'); // 获得接受者id var msg = getEditorHTMLContents('edt1'); // 获得消息内容 ChatManager.send(sender, msg); // 发送消息 SetEditorContents('edt1','');//清空编辑器中发送的消息 } //获取编辑器中HTML内容 function getEditorHTMLContents(EditorName) { var oEditor = FCKeditorAPI.GetInstance(EditorName); return oEditor.GetXHTML(true); } //设置编辑器中内容 function SetEditorContents(EditorName, ContentStr) { var oEditor = FCKeditorAPI.GetInstance(EditorName) ; oEditor.SetHTML(ContentStr) ; } </script> </head> <body style="margin: 0px;"> <form method="post" name="frm1"> <script type="text/javascript"> var oFCKeditor = new FCKeditor("edt1"); oFCKeditor.BasePath = "<%=request.getContextPath() %>/fckeditor/"; oFCKeditor.ToolbarSet="ichat"; oFCKeditor.Height='160'; oFCKeditor.Value=""; oFCKeditor.Create(); </script> <input type="button" value="发 送" onclick="send();"> </form> </body> </html>
?
?
online_list.jsp? --在线列表页面
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK" import="com.ccic.chat.OnlineCounter" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <link rel="stylesheet" href="<%=request.getContextPath()%>/include/css/dtree.css" type="text/css"> <script src="<%=request.getContextPath()%>/include/js/dtree.js"></script> <script type='text/javascript' src='/ichat/dwr/interface/ChatManager.js'></script> <script type='text/javascript' src='/ichat/dwr/engine.js'></script> <script type='text/javascript' src='/ichat/dwr/util.js'></script> </head> <script language="javascript"> function register() { //把我输入的用户名注册到服务器 ChatManager.updateUsersList(); } //页面初始化 function init() { dwr.engine.setActiveReverseAjax(true); // 激活反转 重要 register(); } //回调函数 //function cBack_list(data){ //document.getElementById("online_list").insertAdjacentHTML("afterBegin",""); //var str="aa"+data+">"; //document.getElementById("online_list").insertAdjacentHTML("afterBegin",str); //} </script> <body onload="init();"> 在线用户列表(当前<%=OnlineCounter.getOnline() %>人): <ul id="users"> </ul> <!--<div id="online_list"> </div> --> </body> </html>
?
?
show_msg.jsp? --显示消息页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Insert title here</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <script type='text/javascript' src='/ichat/dwr/interface/ChatManager.js'></script> <script type='text/javascript' src='/ichat/dwr/engine.js'></script> <script type='text/javascript' src='/ichat/dwr/util.js'></script> </head> <script language="javascript"> //页面初始化 function init() { dwr.engine.setActiveReverseAjax(true); // 激活反转 重要 } function callBack(data){ DWRUtil.addRows("tbodyId", data, cellFunctions,{escapeHtml:false}); } var cellFunctions = [ function(item) { return item.text; } ]; </script> <body onload="init();"> <table> <tbody id="tbodyId"> </tbody> </table> </body> </html>
?
?dwr.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd"> <dwr> <allow> <convert converter="bean" match="com.ccic.chat.User"/> <create creator="new" javascript="ChatManager"> <param name="class" value="com.ccic.chat.ChatManager"/> </create> <convert converter="bean" match="com.ccic.chat.Message"/> </allow> </dwr>
?
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>ichat</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class> org.directwebremoting.servlet.DwrServlet </servlet-class> <init-param> <description>调试DWR,发布系统时应将其设为false</description> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <init-param> <description>使用服务器推技术(反转AJAX)</description> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name> initApplicationScopeCreatorsAtStartup </param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>maxWaitAfterWrite</param-name> <param-value>100</param-value> </init-param> <load-on-startup>4</load-on-startup> </servlet> <listener> <listener-class> com.ccic.chat.OnlineCounterListener </listener-class> </listener> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> </web-app>
?
关于FCKEDIT编辑器,我已经删除多余的一些东西了,收集:
----------------------FCKEDIT HTML在线编辑器------------------------------ 配置:http://blog.csdn.net/xiaokuang513204/archive/2010/07/06/5715029.aspx 插件开发:http://blog.csdn.net/flying_huang/archive/2007/03/23/1539206.aspx 火狐 兼容fckedt:http://www.wangzhanweb.com/html/2010-05/231.html FCKEditor的赋值和取值操作 :http://www.blogjava.net/feingto/archive/2008/01/09/173963.html
?
我参考的网上两个例子,见附件!名称“chat”
一个是所有对象间聊天的,访问时对应得页面是index.jsp.
一个是点对点对象聊天的,访问时对应得页面是sample.jsp.
两者都只是简单的例子实现。
?
自己实现的群聊天简单例子,见附件!名称“ichat”
运行:输入http://127.0.0.1:8080/ichat/ 进入登录界面,输入“AA” 或者别的就行了。
?
?
参考:
开源的comet实现:pushlet,dwr 2.0的reverse ajax和dojo的io.bind(),
简单例子(reverse ajax):http://blog.sina.com.cn/s/blog_5bd96d520100gau4.html
?
谈谈webIM :http://akalius.iteye.com/blog/192727
?
?
Thanks
WebContext context = WebContextFactory.get();
Collection<ScriptSession> sessions = context.getScriptSessionsByPage("/dwrpush/index.jsp");
总是报空指针错误
我看你的例子比如通过WebContext实现session与userid的绑定,怎么办呢?在jsf里怎么构造WebContext呢?