【0】如何将离线消息存档,且在接收者上线之后,推送消息到接收者
发送方发送消息 -> 检测接收方是否在线 -> -> y -> 发送在线消息 -> 发送成功后,存档到消息记录; -> n -> 修改数据包(XML流) 发送离线消息 -> 存档到消息记录 -> 不断检测某离线消息的接收方是否在线 -> y -> 推送离线消息记录(未读)到 刚刚登陆的离线用户; -> n -> 继续检测;
【1】消息交互
1)intro: 以下消息内容
是小生我从 smack client 发送 消息(hello, abcdefg) 和另外一个 client 与 的 消息交互情况;
2)接收方离线和发送方在线,而后接收方在线的情况
Attention)incoming(true->sender,false->receiver), processed(true->after processed, false->before processed)
step1)发送msg时,接收者离线;(openfire处理前)
<message to="[email protected]" id="8akb9-36" type="chat" from="[email protected]/Smack"><body>hello, abcdefg</body><thread>1d5f0c93-4eb6-4461-a09a-0da6fc9d3ac6</thread></message> [email protected] status: 3 address: [email protected]/Smack id: 8vqkiso6gv presence: <presence id="8akb9-34" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = true, processed = falsestep2)发送msg时,接收者离线;(openfire处理后)
<message to="[email protected]" id="8akb9-36" type="chat" from="[email protected]/Smack"><body>hello, abcdefg</body><thread>1d5f0c93-4eb6-4461-a09a-0da6fc9d3ac6</thread></message> [email protected] status: 3 address: [email protected]/Smack id: 8vqkiso6gv presence: <presence id="8akb9-34" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = true, processed = trueAttention)
A1)以上就是当发送者在线,而接收者离线的openfire消息记录;A2)以下是 接收者登录后的 openifre 消息记录;
step3)接收者登录在线(openfire处理前)
<message to="[email protected]" id="8akb9-36" type="chat" from="[email protected]/Smack"><body>hello, abcdefg</body><thread>1d5f0c93-4eb6-4461-a09a-0da6fc9d3ac6</thread><delay xmlns="urn:xmpp:delay" from="lenovo-pc" stamp="2016-07-18T00:39:29.759Z"/></message> [email protected] status: 3 address: [email protected]/Smack id: 4qrph1nzci presence: <presence id="2h2Pk-7" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = false, processed = falsestep4)接收者登录在线(openfire处理后)
<message to="[email protected]" id="8akb9-36" type="chat" from="[email protected]/Smack"><body>hello, abcdefg</body><thread>1d5f0c93-4eb6-4461-a09a-0da6fc9d3ac6</thread><delay xmlns="urn:xmpp:delay" from="lenovo-pc" stamp="2016-07-18T00:39:29.759Z"/></message> [email protected] status: 3 address: [email protected]/Smack id: 4qrph1nzci presence: <presence id="2h2Pk-7" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = false, processed = true
Conclusion)openfire
对离线消息的处理: 当接收者不在线的时候,openfire 会把 msg 存储到 ofoffline 这个数据库表中;当 接收者登录在线后,openfire 会把该消息push 给 接收者,因而接收者在线后接受 openfire 推送的离线消息 后,会发送 响应消息(参见session 的 from 元素值);之后openfire 会将该消息从 ofoffline 表中清空(即时删除已经发送到用户的离线消息);
3)接收方和发送方都在线的情况
step1)发送方发送消息(处理前)
<message to="[email protected]" id="8akb9-76" type="chat" from="[email protected]/Smack"><body>i am a student.</body><thread>20b6bc6b-64b8-4cc8-a58c-c416090ae1bc</thread></message> [email protected] status: 3 address: [email protected]/Smack id: 30glj4xlhu presence: <presence id="8akb9-72" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = true, processed = falsestep2)接收方接收消息(处理前)
<message to="[email protected]" id="8akb9-76" type="chat" from="[email protected]/Smack"><body>i am a student.</body><thread>20b6bc6b-64b8-4cc8-a58c-c416090ae1bc</thread></message> [email protected] status: 3 address: [email protected]/Smack id: 3t4gvqjm80 presence: <presence id="ZvrMI-7" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = false, processed = falsestep3)接收方接收消息(处理后)
<message to="[email protected]" id="8akb9-76" type="chat" from="[email protected]/Smack"><body>i am a student.</body><thread>20b6bc6b-64b8-4cc8-a58c-c416090ae1bc</thread></message> [email protected] status: 3 address: [email protected]/Smack id: 3t4gvqjm80 presence: <presence id="ZvrMI-7" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = false, processed = truestep4)发送方发送消息(处理后)
<message to="[email protected]" id="8akb9-76" type="chat" from="[email protected]/Smack"><body>i am a student.</body><thread>20b6bc6b-64b8-4cc8-a58c-c416090ae1bc</thread></message> [email protected] status: 3 address: [email protected]/Smack id: 30glj4xlhu presence: <presence id="8akb9-72" from="[email protected]/Smack"> <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" node="http://www.igniterealtime.org/projects/smack" ver="os2Kusj3WEOivn5n4iFr/ZEO8ls="/> </presence> incoming = true, processed = true
Conclusion)
C1)我们发现,不管是发送在线消息 还是 离线消息,在openfire 处理前后,都可以对消息进行拦截;
C2)根据以上打印info,我们计划在 incoming = true, processed = true
的case下 存储消息记录;C3) 如果接收方成功接收消息后(incoming = false, processed = true),我们存储在线消息,有且仅在该case下存储消息到
ofchatlog 数据库表;如果没有接收方的响应信息(incoming 始终等于true,即session.jsi 始终是 发送者,没有接收者,表明接收者离线),openfire会存储离线消息; 当离线的接收者登录后,openfire 推送离线消息给
该接收者(参见“2->step3,step4”的交互消息步骤),我们就在 incoming = false, processed = true的case下 将该消息转储到到 ofchatlog;C4)一句话说完,我们的ChatLog
插件 涉及的 ofchatlog 数据库表 只存储 在线消息,而离线消息存储在 ofoffline(owned by openfire);所以当接收者离线的时候,离线消息不会存储到 ofchatlog,而是会存储到 ofoffline,离线用户登录后,会将 消息从 ofoffline 转储到 ofchatlog;
<pre name="code" class="java">public class ChatlogPlugin implements PacketInterceptor, Plugin { private static final Logger log = LoggerFactory.getLogger(ChatlogPlugin.class); private static PluginManager pluginManager; private static ChatlogManager logsManager; // Hook for intercpetorn private InterceptorManager interceptorManager; private PresencePlugin presencePlugin; public ChatlogPlugin() { interceptorManager = InterceptorManager.getInstance(); logsManager = ChatlogManager.getInstance(); } /** * <b>function:</b> 拦截消息核心方法,Packet就是拦截消息对象 */ @Override public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException { Packet copyPacket = packet.createCopy(); if (packet instanceof Message) { Message message = (Message) copyPacket; System.out.println(message.toXML()); System.out.println(session); System.out.println("incoming = " + incoming + ", processed = " + processed); if (message.getType() == Message.Type.chat) { // 一对一聊天,单人模式 if (!incoming && processed) { // 有且仅在该case下,存储在线消息(或转储离线消息->chatlog 数据库表) logsManager.insert(buildChatlog(packet, incoming, session)); } else { return ; }