Liny_@NotePad

沉迷ACG中

DWR+EXT在线聊天

YOYO posted @ 2010年9月28日 17:34 in 【Java EE】 with tags DWR EXT , 4276 阅读

嗯,么想到这么快就过了123456hits,没有截图到囧。大概是因为这几天开始校园招聘了吧,发现关键字里面很多都是XX 笔试……

那么放送一个半年前搞的Ext+DWR实现聊天的段落、、
玩这个是因为毕业设计里面有一个需要教师在线指导学生的模块,类似于聊天室,界面如下图(中的在线指导子页面)所示:

Ext用的是Ext JS Library 2.0.2,DWR则使用2.0,因为支持Server Push。

贴一段技术手册里的介绍^^:

系统中教师指导学生模块可以通过在线聊天的方式进行,该模块的实现采用了Server Push(服务器推送)技术,实时发送消息,优于现在浏览器通过AJAX轮询服务端的策略。基于Java开发的DWR框架从2.0开始提供了push功能,通过它建立的应用可以大大节省服务器的资源消耗。

DWR 2中提供的push功能是一种Comet方式,即长连接机制(long live http)。浏览器向服务器请求时,服务器一直保持着这个连接,在关闭之前都可以主动地推送数据给浏览器。下面是本系统中聊天模块的实现示例图:

  

图1-2 聊天模块实现

那么贴一下实验代码,即还没整合到该系统里面时先测试的程式。

首先是DWR这边的model:

package org.yoyo.chat;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Message {

	private static DateFormat format = new SimpleDateFormat("HH:mm:ss");

	private String sender;

	private Date sendTime;

	private String content;

	public Message(String sender, String content) {
		this.sender = sender;
		this.sendTime = new Date();
		this.content = content;
	}

	public String getSender() {
		return sender;
	}

	public void setSender(String sender) {
		this.sender = sender;
	}

	public Date getSendTime() {
		return sendTime;
	}

	public void setSendTime(Date sendTime) {
		this.sendTime = sendTime;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getText() {
		return sender + " " + format.format(sendTime) + "<br />" + content + "<br/>";
	}

	@Override
	public String toString() {
		return getText();
	}

}
package org.yoyo.chat;

public class User {
	
	private String userId;
	
	private String username;

	public User(String id, String username) {
		this.userId = id;
		this.username = username;
	}

	public String getUserId() {
		return userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

}

然后是业务处理:

package org.yoyo.chat;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.directwebremoting.ScriptSession;
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>();

	private static List<Message> messages = new ArrayList<Message>();
	
	private static Message newMessage;

	@SuppressWarnings("unchecked")
	public String updateUsersList(String newUserName, HttpServletRequest request) {
		User user = null;
		if (newUserName != null) {
			user = new User(request.getSession().getId(), newUserName);
			users.add(user);
		}

		WebContext wctx = WebContextFactory.get();
		Collection sessions = wctx.getScriptSessionsByPage(wctx
				.getCurrentPage());
		Util util = new Util(sessions);

		util.removeAllOptions("users");
		util.addOptions("users", users, "username");

		return newUserName == null ? null : user.getUserId();
	}

	/**
	 * 将用户id和页面脚本session绑定
	 * 
	 * @param userid
	 */
	public void setScriptSessionFlag(String userid) {
		WebContextFactory.get().getScriptSession().setAttribute("userid",
				userid);
	}

	/**
	 * 根据用户id获得指定用户的页面脚本session
	 * 
	 * @param userid
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public ScriptSession getScriptSession(String userid) {
		WebContext wctx = WebContextFactory.get();
		ScriptSession scriptSessions = null;
		Collection<ScriptSession> sessions = wctx.getScriptSessionsByPage(wctx
				.getCurrentPage());

		for (ScriptSession session : sessions) {
			String xuserid = (String) session.getAttribute("userid");
			if (xuserid != null && xuserid.equals(userid)) {
				scriptSessions = session;
			}
		}
		return scriptSessions;
	}

	/**
	 * 发送消息
	 * 
	 * @param sender
	 *            发送者
	 * @param msg
	 *            消息内容
	 */
	@SuppressWarnings("unchecked")
	public void send(String sender, String msg) {
		newMessage = new Message(sender, toHtml(msg));
		messages.add(newMessage);
		updateMessage();
	}
	
	private String toHtml(String msg) {
		int index = -1;
		while ((index = msg.indexOf("\r\n")) >= 0) {
			msg = msg.substring(0, index) + "<br>" + msg.substring(index + 2);
		}
		while ((index = msg.indexOf("\n")) >= 0) {
			msg = msg.substring(0, index) + "<br>" + msg.substring(index + 1);
		}
		return msg;
	}

	/**
	 * 获得消息
	 * @return
	 */
	public String getMessages() {
		StringBuffer content = new StringBuffer("");
		
		for (Message msg: messages) {
			content.append(msg + "<br>");
		}
		
		return content.toString();
	}

	/**
	 * 更新聊天记录
	 */
	@SuppressWarnings("unchecked")
	public void updateMessage() {
		WebContext wctx = WebContextFactory.get();
		Collection sessions = wctx.getScriptSessionsByPage(wctx
				.getCurrentPage());

		Util utilAll = new Util(sessions);
		utilAll.addFunctionCall("appendMessage", newMessage == null ? "" : newMessage.toString() + "<br>");
	}
}

接着是前端JS:

var content = "";

/**
 * 页面初始化
 */
function init() {
	dwr.engine.setActiveReverseAjax(true); // 激活反转 重要
	ChatManager.updateUsersList(null); // 当你打开界面的时候,先获得在线用户列表.
	ChatManager.updateMessage(null); // 获得聊天记录
	ChatManager.getMessages(function(data) {
		content = data;
	});
}

function appendMessage(msg) {
	var bd = Ext.getCmp("chatlog").body;
	content = content + msg;
	bd.update(content);
	bd.dom.scrollTop = bd.dom.scrollHeight;
}

/**
 * 注册帐号
 */
function register(button) {
	if ($("username").value === "" || $("username").value.length <= 0) {
		alert("\u8bf7\u8f93\u5165\u6635\u79f0");
		return;
	}
	/* 下面是对一些按钮的禁用和激活操作 */
	$("username").disabled = true;
	button.disabled = true;
	$("message").disabled = false;
	/* 把我输入的用户名注册到服务器,并获得用户id(这里用session id 代替) */
	ChatManager.updateUsersList($("username").value, function (data) {
		if (data !== null && data.length > 0) {
			$("userid").value = data; // 注册成功,把userid放到当前页面
		}
	});
}

/**
 * 发送消息
 */
function send() {
	var sender = dwr.util.getValue("username"); // 获得发送者名字
	var msg = dwr.util.getValue("message"); // 获得消息内容
	if (msg !== "") {
		ChatManager.send(sender, msg); // 发送消息
		$("message").value = "";
	}
}
window.onload = init;//页面加载完毕后执行初始化方法init

最后是Ext搞的界面:

<%@ page language="java" pageEncoding="UTF-8"%>
<html>
	<head>
		<title>chat</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" type="text/css"
			href="lib/ext/resources/css/ext-all.css" />
		<link rel="stylesheet" type="text/css"
			href="lib/ext/resources/css/ext-patch.css" />
		<script type="text/javascript" src="lib/ext/adapter/ext/ext-base.js"></script>
		<script type="text/javascript" src="lib/ext/ext-all.js"></script>
		<script type='text/javascript' src='dwr/interface/ChatManager.js'></script>
		<script type='text/javascript' src='dwr/engine.js'></script>
		<script type='text/javascript' src='dwr/util.js'></script>
		<script type="text/javascript" src="chat.js" charset="utf-8"></script>
	</head>
	<script>
		var chatlog = new Ext.Panel({
							region:"center",
							id: "chatlog",
							autoScroll: true,
							height: 450,
				            xtype: 'textarea',
				    		readOnly: true
				            });
	Ext.onReady(function(){
		new Ext.Viewport({
			layout:"border",
			items:[
			 	{
					region:"center",
					title: "指导记录",
					layout:"border",
					items: [chatlog]
						
				}, 
				{
					html: '<ul id="users"></ul>',
					region:"east",
					width:120,
					title:"参与者"
				}, {
					region:"south",
				    height: 150,
					layout:"border",
					title: "发送信息",
					buttonAlign: "center",
					buttons: [{
									id: 'send',
									xtype: 'button',
									text: '发送',
									handler:function(){
					                    send();
					                }
								}, 
								{
									id: 'exit',
									xtype: 'button',
									text: '结束指导'
								}
							],
					items: [
						{
							region:"center",
							id: "message",
				            xtype: 'textarea',
				    		emptyText: '请输入内容',
				    		enableKeyListener: true,
				    		 listeners:{
   		   						specialkey:function(f,e){
   		   							if(e.getKey() == e.ENTER){
   		   						  		send();
									} 
								}
							}
						}]
				}
			]
		});
	});
	</script>
	<body>
	</body>
</html>

貌似register被整合到我登陆的页面去了,因此米有在这里体现,需要的话可自己加个输入框输入用户名、再来个按钮调用该方法、、


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter