原文:http://myopenfire.com/article/getarticle/26
1、openfire默认怎么存离线消息
在默认情况下,不添加任何插件的情况下,当用户不在线,对于发送给该用户的消息,会被openfire存放到数据库中的ofoffline表中。
为了更深入的了解离线消息的原理,我们来看一下ofoffline这个表,这个表的结构如下所示:
下面对这几个字段进行详细说明:
(1)Username:是用户名,如myopenfire1、myopenfire2。
(2)MessageID:是openfire产生的一个数字,openfire每处理一条消息到离线,这个值就会增1。
(3)CreateDate:表示这条消息被插入数据库的时间,注意插入数据库的时间不一定是这条消息到达服务器的时间。因为他们之间始终是有一点误差的。
(4)MessageSize:表示存储在stanza字段中的消息的字节数。
(5)Stanza:翻译为节的意思,这里指的就是数据包,存放的是message消息。
2、离线表中实际的数据是什么
Ok,我们来看看实际的存储,你就会更容易理解了,如下图是离线表中的数据:
注意stanza是存放的是xmpp协议表示的消息。
3、存放离线消息的类
在目录openfire_srcsrcjavaorgjivesoftwareopenfire下,有一个OfflineMessageStore.java类,这个类就是用来存放离线消息的。
首先这个类是一个单例类,它的实例保存在XMPPServer中,代码如下:
public static OfflineMessageStore getInstance() { return XMPPServer.getInstance().getOfflineMessageStore(); }
我们可以通过getInstance来获得OfflineMessageStore的实例。
如果要向离线表中插入一条数据,那么可以调用addMessage方法,addMessage的源码分析如下:
public void addMessage(Message message) { // 如果消息为null,直接返回 if (message == null) { return; } // shouldStoreMessage判断哪些消息应该被存储,例如只有类型为chat的消息,才允许被存储。 if(!shouldStoreMessage(message)) { return; } JID recipient = message.getTo(); String username = recipient.getNode(); // 如果没有接受者,那么就不必要存离线了。 if (username == null || !UserManager.getInstance().isRegisteredUser(recipient)) { return; } else if (!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())) { // 如果这条消息的域名不对,是发送到其他服务器的,那么也不需要存了 return; } // 产生下一条消息的一个序号 long messageID = SequenceManager.nextID(JiveConstants.OFFLINE); // 获得xml格式的消息 String msgXML = message.getElement().asXML(); // 打开数据库连接,并且存储消息到数据库 Connection con = null; PreparedStatement pstmt = null; try { con = DbConnectionManager.getConnection(); pstmt = con.prepareStatement(INSERT_OFFLINE); pstmt.setString(1, username); pstmt.setLong(2, messageID); pstmt.setString(3, StringUtils.dateToMillis(new java.util.Date())); pstmt.setInt(4, msgXML.length()); pstmt.setString(5, msgXML); pstmt.executeUpdate(); } catch (Exception e) { Log.error(LocaleUtils.getLocalizedString("admin.error"), e); } finally { DbConnectionManager.closeConnection(pstmt, con); } // 为存储离线的用户的缓存空间加上相应的字节数,如果设置了一个用户只能存储几M的数据,那么这个就是统计,这个用户已经存储了多少字节的离线消息 if (sizeCache.containsKey(username)) { int size = sizeCache.get(username); size += msgXML.length(); sizeCache.put(username, size); } }
如果要删除某条消息,可以使用deleteMessages这个函数,传入用户名作为参数,代码如下:
public void deleteMessages(String username) { Connection con = null; PreparedStatement pstmt = null; try { // 打开数据库连接 con = DbConnectionManager.getConnection(); // DELETE_OFFLINE为 DELETE FROM ofOffline WHERE username=? pstmt = con.prepareStatement(DELETE_OFFLINE); pstmt.setString(1, username); pstmt.executeUpdate(); // 将这个用户已经占用多少字节,重新置为0 removeUsernameFromSizeCache(username); } catch (Exception e) { Log.error("Error deleting offline messages of username: " + username, e); } finally { // 关闭数据库连接 DbConnectionManager.closeConnection(pstmt, con); } }
4、小结
本节讲解了离线消息的存储类OfflineMessageStore,这个类中有很多关于离线消息的处理函数,需要对openfire离线消息进行扩展的同学,不妨多看看这个类的实现。