/ 闲敲棋子 / Openfire好友关系解决方案

Openfire好友关系解决方案

2014-05-07 posted in [学习笔记]

今天是真把RFC-6121磕下来了,明确几个概念。

首先xmpp中的roster和出席订阅是两回事。roster的关系更像微博的订阅,不需要对方同意即可完成。而出席信息则“有点儿”像加好友,需要对方批准才能获取信息。

roster是可以独立于订阅状态存在的,因此只使用“花名册”功能的话,roster属性中的subscribe值是none。只有要求订阅对方的出席状态,subscribe值才会出现from、to、both。网上关于openfire的from、to、both、none关系文章太多了,而他们对应在openfire的roster表中sub、ask、recv状态关系也不胜枚举,就不再赘述了。

我们主要想解决一个类似QQ的加好友流程。 User A 发送好友请求至 User B, User B点击同意后,与User A 成为好友(subscribe状态变成both)。

解决方案如下,其中红色的部分是重点关注的地方,也是客户端和服务器需要修改的工作。

image

以上解决方案用到了RFC-6121 第3.4节中提到的预批准被订阅请求协议,openfire在[3.9.1版本](http://issues.igniterealtime.org/browse/OF-738)已经支持了这个特性。

因此,我们需要做的只是:

  1. 客户端自动同意订阅请求
  2. 客户端发出订阅请求包后,发送预批准包至服务器
  3. 服务器升级至3.9.1
  4. 更多的测试

当然,也可以通过重写RosterProvider的方式整个把Roster关系替换掉。不过我觉得,在遵从xmpp协议,并且之前已经有很多接口调用的情况下,这个方式还是比较值得考虑的。

PS:我的图画的是有多糟糕啊T^T

update

看样子openfire还没释放以上特性,我们可以通过插件形式完成以上操作。

核心代码如下:

@Override
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed)
		throws PacketRejectedException {
	if (session != null) {
		log.info("intercept packet from:" + packet.getFrom() + ",to:" + packet.getTo());
	}
	if (packet != null) {
		//只处理出席信息。
		if (packet instanceof Presence) {
			Presence presence = (Presence) packet;
			//处理订阅类的出席信息,会接收到两次出席信息,只处理resource不为空的那次。
			if (Presence.Type.subscribe.equals(presence.getType())) {
				if (presence.getFrom() != null && !StringUtils.isEmpty(presence.getFrom().getResource())) {
					Presence subscribedPresence = new Presence();
					subscribedPresence.setType(Presence.Type.subscribed);
					subscribedPresence.setTo(presence.getFrom().toBareJID());
					subscribedPresence.setFrom(presence.getTo().toBareJID());
					log.info("acceptSubscription: " + subscribedPresence.toXML());
					router.route(subscribedPresence);
				}
			}
		}
	}
}

原理:

使用插件实现PacketInterceptor接口,拦截presence包,收到订阅包的同时,新建一个presence包,把 from 和 to 换一下,类型为subscribe,发出去(代替客户端同意好友请求)。

效果:

自动同意好友请求,当对方上线时,按同意按钮自动反加。双方订阅关系变为both。