开发openfire的内部组件


为什么写这篇文章,主要目的是为了记录这一段时间的成果,首先相关的资料非常少,同时我也不是一个专业的程序员,所以记录一下对这类特定技术领域的研究成果,可以给弟兄们一些参考和备忘。


至于openfire是什么,为什么要用他,我不想多做介绍,知道的就知道,不知道的也不用去知道。

openfire的组件分为内部组件和外部组件两种,可能大家不一定完全明白什么意思,我解释一下:

内部组件是工作在openfire运行环境的插件,是以独立的jar包存在,具有访问和控制openfire主域的权限,其最主要的能力就是,可以参与和控制主域的通信。

外部组件是实现XEP-0114规范的实例,确切的说就是独立的应用程序,通过tcp通信的方式连接到openfire上,只能参与和控制独立的子域通信。

因此,内外部组件的应用场景非常容易区分,就是看,你是否需要使用服务器主域资源。

例如,你需要在某些特定场景下枚举服务器上所有的用户,那么你无法用外部组件实现,只能通过内部组件实现。

那么为什么要有外部组件呢,子域又是什么,请看我的例子:


有2家公司,各有员工500人,需要实现xmpp互通,但是是受到逻辑约束的,某些人只能和另外公司的某些人沟通,你也许想说,把两个公司的员工都放在主域上,用内部组件进行控制,的确,没问题,但是如果两家公司各自通信有特殊的业务需求怎么办?是不是要在内部组件写很多的分支呢。

于是我们假设A公司的子域为coma,B公司子域为comb,服务器主域为example.com,那么我们就可以创建2个外部组件(类)来分别控制2个公司的通信,第一个外部组件componenta负责域coma.example.com,componentb负责comb.example.com

我们通过实现2个类来完成2个外部组件的操作,一旦实例化并和服务器建立了安全连接之后,所有的业务逻辑就很简单了。


对于外部组件今天我先暂且搁置,先说说内部组件。

下面涉及的代码是一个例子项目,任务是把openfire的用户在线状态同步到redis里,类似的解决方案在openfire源码自带的presence service插件里也有实现,不过那个是提供了一个web的rest接口,用起来当然没有redis爽快。

实现逻辑,注册一个组件,注入到包通信中,截获presence包

当presence的type为unavaiable时,认为用户离线,对比redis内原来用户状态,如果变更,则将离线状态写入redis。

当截获到type为空的presence包,则认为用户在线。

另外为组件实现一个web控制台,可以删除所有的用户以及初始化同步一次openfire已有的用户状态。

内部组件的编写比较简单,基本上下载了openfire源码就可以开始写,不用准备额外的jar包。

一个合法的内部组件,首先,位置必须在src/plugins下,建立组件的目录,再创建src/java存放java代码src/web存放web console的jsp页面


上图就是一个合法的内部组件的位置

在组件目录,例子中的synUserStateToRedis里,需要plugin.xml。这个文件是组件的描述文件,必须。

文件的内容大家可以打开源码内任何一个看看,不需要过多介绍,如果没有web console,可以把<adminconsole>节点忽略。

在src/java目录下,建立组件的package和类代码:


至于编译则非常简单,在eclipse里打开ant的panel,添加位于openfire源码的build下的build.xml

展开之后双击plugins就可以编译所有的组件,如果需要单独编译你的组件,在ant的配置中添加-Dplugin=你的组件名,该组件名是在xml里面配置的名称,也是组件类getName()方法返回的那个字符串,不是组件的类名。


组件的实现有多种方式,这个可以参考jivesoftware的官方文档,不过我看用的比较多的还是PacketInterceptor模式。

新建一个类,实现Plugin,PacketInterceptor,必须完成三个方法的重写,interceptPacket,initializePlugin,destroyPlugin。

顾名思义,分别是实现包的注入,初始化组件和销毁组件。外部组件的某些开发方式中,jivesoftware提供了继承component类来更简单的重写对不同类型数据包的操作,而在这里,你只能利用interceptPacket来完成对所有类型的包进行操作。

例如我要处理presence包,我会这么写:

public void interceptPacket(Packet packet, Session session,
			boolean incoming, boolean processed) throws PacketRejectedException {
		if (!processed && incoming) {
			if (packet instanceof Presence) {
				// Respond to presence subscription request or presence probe
				Presence presence = (Presence) packet;
				processPresence(presence);
			}
		}

	}


如果要让你的组件在openfire后台有嵌入的界面,那么就在src/web下编写你的jsp页面

主要部分:

<%@ page import="java.util.*,
                 org.jivesoftware.openfire.XMPPServer,
                 org.jivesoftware.util.*,
                 org.jivesoftware.openfire.plugin.SynUserStateToRedis"
    errorPage="error.jsp"
%>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>

<%-- Define Administration Bean --%>
<jsp:useBean id="admin" class="org.jivesoftware.util.WebManager"  />
<c:set var="admin" value="${admin.manager}" />
<% admin.init(request, response, session, application, out ); %>

<%  // Get parameters
    boolean init = request.getParameter("init") != null;
    
    SynUserStateToRedis plugin = (SynUserStateToRedis) XMPPServer.getInstance().getPluginManager().getPlugin("synuserstatetoredis");
    
%>

为了让jsp页面实现初始化同步用户的逻辑,我们在组件的类里添加一个方法:

public int initUserStates() {
		int cnt=0;
		Jedis jedis = RedisManager.getInstance().getRedis();
		Pipeline pl = jedis.pipelined();
		Collection<User> users = userManager.getUsers();
		try {

			for (User u : users) {
				int state = STATUS_ONLINE;
				Presence p = presenceManager.getPresence(u);
				if (p == null) {
					state = STATUS_OFFLINE;
				} else if (p.getType() == Type.unavailable) {
					state = STATUS_OFFLINE;
				}
				pl.hset(REDIS_KEY, u.getUsername(), String.valueOf(state));
				cnt ++;
				
			}
			pl.sync();
		} catch (Exception e) {
			// TODO: handle exception
		}
		RedisManager.getInstance().returnJedis(jedis);
		return cnt;
	}
这个方法是将openfire的用户枚举并且判断其状态,批量写入到redis

在jsp中这样调用即可

<%  if (init) { 
String initedUserNum=String.valueOf(plugin.initUserStates());
%>
    <div class="jive-success">
    <table cellpadding="0" cellspacing="0" border="0">
    <tbody>
        <tr><td class="jive-icon"><img src="images/success-16x16.gif" width="16" height="16" border="0"></td>
        <td class="jive-icon-label">
            操作执行成功,同步了<%=initedUserNum%>个用户。
        </td></tr>
    </tbody>
    </table>
    </div><br>
<% }%>

完成之后,编译,在openfire后台直接上传

相关的web console:


应该说开发内部组件还是比较简单的,理解了原理之后对于java程序员来说应该是小意思,

总体来说openfire还是性能不错的xmpp服务器,大家不妨可以换个思路考虑一下是否可以利用即时通信服务来实现原来基于web实现的业务。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值