网上部分关于Openfire的代码,只实现了,从单一应用与Openfire已经封装好的Spark客户端交互的功能,并没有实现一个应用内,多个用户交互的功能。
下面编写一个Openfire网页版单对单聊天的实例,用Smack的api与Struts2实现。仅包含两在线用户聊天部分。
如下图,开两个浏览器,模拟两个用户,实现网页版的Openfire聊天。
具体文件结构如下:
除了补充了一个fastjson-1.1.24.jar实现Java的容器类到Json字符串的转换,详见《【Java】读取网页中的内容》(点击打开链接),其余关于Openfire的用户处理部分在《【Openfire】网页版的用户注册、登录、修改密码》(点击打开链接)一文中已经详细介绍,这次工程是在其上面开发的。在struts.xml新增了部分关于聊天的Ajax Action,更新之后如下:
/index.jsp
/chat.jsp
/index.jsp
/index.jsp
/index.jsp
具体改动如下:
基本上已经可以通过Struts2的Ajax Action略窥一二,网页聊天主要实现三个事情,一个是初始化init,一个是让前台JavaScript的定时器setInterval定时执行的,载入信息的Action,还有一个是用户点击按钮的发送信息的Action。
这些Action全写在chat.java这个文件里面,具体代码如下,详情请留意注释:
package test;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.struts2.ServletActionContext;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import tool.DB;
import com.alibaba.fastjson.JSON;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
public class chat extends ActionSupport {
private String msg;//用户要发送的信息
private String to_user;//用户选择的收信人
private Map session = ActionContext.getContext().getSession();//Session
//将1970年到现在的long时间记法,转换成真实时间,人看的时间。
private String get_realTime(long systime) {
return new SimpleDateFormat("hh:mm:ss").format(new Date(systime))
.toString();
}
//聊天系统的初始化
public void init() throws IOException {
PrintWriter printWriter = ServletActionContext.getResponse()
.getWriter();//用于打印Ajax
//新建一个名为message_list存信息的ArrayList放入session
ArrayList
message_list = new ArrayList
();
session.put("message_list", message_list);
//Openfire的关键步骤,为当前的连接,新建聊天信息接受的接听器。
Connection connection = (Connection) session.get("connection");
ChatManager chatmanager = connection.getChatManager();
chatmanager.addChatListener(new ChatManagerListener() {
@Override
public void chatCreated(Chat chat, boolean arg1) {
//对Openfire的消息处理进行重写
chat.addMessageListener(new MessageListener() {
//要求这个监听器,收到信息,通通放入session中的message_list
public void processMessage(Chat arg0,
Message message_receive) {
if (message_receive.getBody() != null) {
ArrayList
message_list = (ArrayList
) session
.get("message_list");
//由于Smack的api是没有对Openfire中Time属性的处理
//因此必须自己再处理一下
message_receive.setProperty("time",
System.currentTimeMillis());
message_list.add(message_receive);
session.put("message_list", message_list);
}
}
});
}
});//这样重写了监听器之后,就可以避免原来Openfire必须指定接听哪个用户发来的信息的情况
//任意用户给当前登录用户发来信息,都会放入session中,一会儿载入信息的load_msg()仅仅需要对session进行处理
//载入Openfire中所有用户列表,剔除当前用户,和在初始化Openfire服务器时设定系统总管理用户admin
DB db = DB.getInstance();
List
userlist = db .getBySql("select username from ofuser where username !='" + session.get("username") + "' and username!='admin'"); String json = JSON.toJSONString(userlist); //将用户列表打印给前台 printWriter.print(json); printWriter.flush(); printWriter.close(); } //载入用户信息 public void load_msg() throws IOException, XMPPException { ServletActionContext.getResponse().setContentType( "text/html;charset=UTF-8");//由于用户发送的信息可能有中文,所以必须要先设置编码 PrintWriter printWriter = ServletActionContext.getResponse() .getWriter(); //对session中的message_list进行处理构造JSON ArrayList
message_list = (ArrayList
) session .get("message_list"); ArrayList
> message_json = new ArrayList
>();//这个message_json会被fastjson-1.1.24.jar转化成json字符串 for (int i = 0; i < message_list.size(); i++) { Message message = message_list.get(i); HashMap
one_message = new HashMap
(); String from = message.getFrom().split("@")[0];//由于Openfire会自动在发信人后面带上一堆服务器、设备参数,因此,做此划分 String time = get_realTime((long) message.getProperty("time"));//将long时间转现实时间输出到前台 one_message.put("from", from); one_message.put("time", time); one_message.put("body", message.getBody()); message_json.add(one_message);//每一条消息都这样处理 } //打印到前台 String json = JSON.toJSONString(message_json); printWriter.print(json); printWriter.flush(); printWriter.close(); } // 信息发送 public void submit_msg() throws IOException, XMPPException { PrintWriter printWriter = ServletActionContext.getResponse() .getWriter(); Connection connection = (Connection) session.get("connection");//取连接 //按Openfire要求的格式构造发信、收信人 String from = (String) session.get("username") + "@" + connection.getServiceName(); String to = to_user + "@" + connection.getServiceName(); //发信息核心代码 ChatManager chatmanager = connection.getChatManager(); Chat chat = chatmanager.createChat(to, null);//新建一个会话,指定收信人,不对收信人所回复的信息进行监听 //因为在初始化的时候,已经对此连接收到信息都进行了监听,无须重复监听 chat.sendMessage(msg);//对此会话进行发送 //但同样要将发送的信息记录在session中的message_list里面,用户打印,这才符合现时主流的聊天工具一问一答的方式 ArrayList
message_list = (ArrayList
) session .get("message_list"); Message message_send = new Message(); message_send.setFrom(from); message_send.setTo(to); message_send.setBody(msg); message_send.setProperty("time", System.currentTimeMillis()); message_list.add(message_send); session.put("message_list", message_list); //其实,消息发送,是没有任何东西交互给前台的,但必须打印一个空信息,才能保证ajax过程不出错。 printWriter.print(""); printWriter.flush(); printWriter.close(); } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getTo_user() { return to_user; } public void setTo_user(String to_user) { this.to_user = to_user; } }
最后,配合前台的chat.jsp一切就大功告成,HTML布局部分不重要就表格里面的几行和一个按钮。关键是javascript部分:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<script src="jquery-1.8.3.js" type="text/javascript"></script>
聊天
欢迎,${sessionScope.username},登出
<script type="text/javascript">
function init(){
$.ajax({
type : "post",
url : "init",
dataType : "json",
success : function(data) {
for(var i=0;i
"+data[i]+"");
}
},
error : function() {
alert("出错了");
}
});
setInterval("load_msg()",2000);
}
function load_msg(){
var msg="";
$.ajax({
type : "post",
url : "load_msg",
dataType : "json",
success : function(data) {
for(var i=0;i
"+data[i].time+"
"+
"
"+data[i].from+":"+data[i].body+"
";
$("#msg_list").html(msg);
}
},
error : function() {
alert("出错了");
}
});
}
function submit_msg() {
var msg = $("#msg").val();
var to_user = $("#user_list").val();
$("#msg").val("");
$.ajax({
type : "post",
url : "submit_msg",
dataType : "text",
data : {
msg : msg,
to_user : to_user
},
success : function(data) {
},
error : function() {
alert("出错了");
}
});
}
init();
</script>
要求进入这个页面就马上执行init()的代码,载入用户列表的同时,同时触发读取信息的定时器,每2秒一次执行load_msg()里面的所有内容。
将发送信息的按钮的点击事件,绑定于submit_msg()事件与后台进行交互。
作者:yongh701
原文地址:http://blog.youkuaiyun.com/yongh701/article/details/52032869#comments