openfire简介

详细文章请下载附件。。。。。。

Openfire简介




陈科 [email protected]
2011-9-23



目录
一.Openfire简介 3
二.Xmpp协议介绍 3
三.开发环境搭建 5
四.Openfire架构介绍 8
五.Openfire插件开发 10
六.Openfire开发心得 15



一. Openfire简介


Openfire 采用Java开发,开源的实时协作(RTC)服务器基于XMPP(Jabber)协议。
Openfire并非简单实现xmpp协议(rfc3920),而是在这之上实现了xmpp-im(rfc-3921),该协议对实施协作的各种场景有较全面的考虑和解决方案,例如用户状态切换,消息订阅和通知等等。

二. Xmpp协议介绍


这里简单介绍一下rfc3920和rfc3921分别解决什么样的问题。
可扩展消息和出席信息协议(XMPP)[rfc3921],这个协议采用XML流实现在任意两个网络终端接近实时的交换结构化信息。XMPP提供一个通用的可扩展的框架来交换XML数据,它主要用来建立即时消息和出席信息应用以实现 RFC 2779 的需求。
如果需要了解具体的xmpp大家可以直接阅读rfc3920.这里直接拿一个标准的xmpp通讯流程来举例说明:
C: <?xml version='1.0'?>
      <stream:stream
          to='example.com'
          xmlns='jabber:client'
          xmlns:stream='http://etherx.jabber.org/streams'
          version='1.0'>
   S: <?xml version='1.0'?>
      <stream:stream
          from='example.com'
          id='someid'
          xmlns='jabber:client'
          xmlns:stream='http://etherx.jabber.org/streams'
          version='1.0'>
   …  encryption, authentication, and resource binding ...
   C:   <message from='[email protected]'
                 to='[email protected]'
                 xml:lang='en'>
   C:     <body>Art thou not Romeo, and a Montague?</body>
   C:   </message>
   S:   <message from='[email protected]'
                 to='[email protected]'
                 xml:lang='en'>
   S:     <body>Neither, fair saint, if either thee dislike.</body>
   S:   </message>
C: </stream:stream>
   S: </stream:stream>
这里的C代表客户端,S代表服务器。
这里我们简单翻译一下这个通信过程。
1. 客户端发起一个流请求。
2. 服务端响应客户端,并且在响应中提供了流的特性,例如验证机制,加密机制,压缩机制等等。
3. 略去鉴权过程。
4. 客户端发送消息给此域中的某用户。
5. 服务端收到某用户消息之后响应客户端。
6. 客户端要求结束流。
7. 服务端响应客户端并结束流。
这里还要介绍个概念,JID。Xmpp中的通信过程和消息传递机制都是通过JID来作为标识的。比如这次会话中的from和to.具体的格式大家可以查阅rfc3920这里就不做介绍了。


即时消息和出席信息功能的扩展,定义在 XMPP-IM 协议[rfc3921],该协议主要在xmpp基础上扩展了IM的功能,并且具体介绍了语法和消息类型。本协议从用户角度出发主要需要实现以下几个功能。
• 和其他用户交换消息
• 和其他用户交换出席信息
• 管理和其他用户之间的订阅和被订阅
• 管理联系人列表中的条目(在 XMPP 中这被称为 "roster")
• 屏蔽和特定的其他用户之间的通信(出或入)
以上功能主要是对message, IQ, presence进行了功能扩展。
例如出席信息(presence)
出席信息节的'type'属性是可选的(OPTIONAL). 一个不拥有任何'type'属性的出席信息节用来通知服务器发送者已经在线并且可以进行通信了, 'type' 属性表示缺乏可用性, 请求管理对其他实体的出席信息的订阅, 请求其他实体的当前出席信息, 或发生了和上次发出的出席信息节有关的错误. 如果包含了它, 'type'属性必须(MUST)拥有以下值之一:
• unavailable -- 通知实体将不可通信.
• subscribe -- 发送者希望订阅接收者的出席信息.
• subscribed -- 发送者允许接收者接收他们的出席信息.
• unsubscribe -- 发送者取消订阅另一个实体的出席信息.
• unsubscribed -- 订阅者的请求被拒绝或以前的订阅被取消.
• probe -- 对一个实体当前的出席信息的请求; 只应(SHOULD)由服务器代替一个用户生成.
• error -- 处理或递送之前发送的出席信息节的时候发生了错误.
关于出席信息语义学的详细信息和基于XMPP的即时消息和出席信息应用程序的订阅模式,参考 rfc3921交换出席信息Exchanging Presence Information(第五章) 和 管理订阅Managing Subscriptions(第六章).

关于其他细节有兴趣可以阅读rfc3921.


三. 开发环境搭建


Openfire的开发环境搭建相对简单。
1.首先从官网下载源码包。
wget http://www.igniterealtime.org/downloads/download-landing.jsp?file=openfire/openfire_src_3_7_0.tar.gz
2.安装java开发环境以及eclipse.
3.在eclipse中新建java project.

4.不要直接finish,选择next.

5.选择Link additional source to project。然后把源代码文件夹中的openfire_src_3_7_0\openfire_src\src\java目录导入。

6.通过add external jars导入openfire_src_3_7_0\openfire_src\build\lib下的所有jar包。
7.在openfire_src_3_7_0\openfire_src\build目录下运行ant命令即可编译代码。编译出的可执行目录在openfire_src_3_7_0\openfire_src\target\openfire\bin下面。
假如需要以debug方式启动,则执行openfire.bat –debug.默认是suspend=n。你可以修改脚本为suspend=y这样只有当你远程debug连上之后应用代码才开始跑。


四. Openfire架构介绍


首先看一下大致的架构图


这边有几个概念阐述下。
1.xmppServer:openfire服务器的单例类。承担了服务器各个模块的启动和关闭的作用。
2.module:openfire把各个独立功能通过模块的形式来组装,在启动的时候按顺序加载。
3.xxxlistener:假如某些模块或者xmppserver本身有扩展点希望暴露给开发者,那么他也许会提供某种listener。
4.xxxHandler:某些模块可能会有handler的概念,用于帮助你处理真正的业务逻辑。
5.plugin:提供给开发者的扩展点,你可以在有限的条件下开发插件。
注意:上图中特意把connectionManagerImpl单独拿了出来,其他它也是一个module,只不过它是最后被加载,因为它需要其他一些模块的实例,并且最终的网络连接等服务由它来提供。

最后我们来看一下openfire的启动流程


1.用户启动xmppserver
2.xmppserver初始化配置参数等。
3.xmppserver装载,初始化,启动modules
4.xmppserver异步启动plugins.
5.xmppserver fire它的serverlisteners。


五. openfire插件开发


开发一个openfire的插件相对简单,只要实现以下几个步骤就可以了。
1.实现plugin接口。
public interface Plugin {

    /**
     * Initializes the plugin.
     *
     * @param manager the plugin manager.
     * @param pluginDirectory the directory where the plugin is located.
     */
    public void initializePlugin(PluginManager manager, File pluginDirectory);

    /**
     * Destroys the plugin.<p>
     *
     * Implementations of this method must release all resources held
     * by the plugin such as file handles, database or network connections,
     * and references to core Openfire classes. In other words, a
     * garbage collection executed after this method is called must be able
     * to clean up all plugin classes.
     */
public void destroyPlugin();

该接口的2个方法相对简单,一个初始化,一个销毁。
2.打jar包。包里面的目录格式为:
[pluginDir]
*    |-- plugin.xml
*    |-- classes/
*    |-- lib/
Plugin.xml为配置文件
Classes为插件需要的一些resources文件
Lib则是插件的源代码以及源代码依赖的jar包。

举例我开发一个用户状态发生变化后,把状态值插入到redis服务器中的插件。
1.写代码:
package com.netease.openfire.plugin;

import java.io.File;

import org.apache.commons.pool.impl.GenericObjectPool;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.event.SessionEventDispatcher;
import org.jivesoftware.openfire.event.SessionEventListener;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.user.PresenceEventDispatcher;
import org.jivesoftware.openfire.user.PresenceEventListener;
import org.xmpp.packet.JID;
import org.xmpp.packet.Presence;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class StatusPlugin implements Plugin, PresenceEventListener,
SessionEventListener {
// private XMPPServer server = XMPPServer.getInstance();
// private PresenceUpdateHandler wpuh;
// private Map<Class, Module> modules;
private static JedisPool pool;
static {
GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();
poolConfig.maxActive = 30;
poolConfig.minIdle = 30;
poolConfig.minEvictableIdleTimeMillis = 500;
pool = new JedisPool(poolConfig, "192.168.150.53");
}

@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
PresenceEventDispatcher.addListener(this);
System.out.println("add  PresenceEventListener ok !");
SessionEventDispatcher.addListener(this);
System.out.println("add  SessionEventListener ok !");

}

@Override
public void destroyPlugin() {
PresenceEventDispatcher.removeListener(this);
}

private void updateCache(String key, String value) {
Jedis jedis = pool.getResource();
jedis.set(key, value);
pool.returnResource(jedis);
}

@Override
public void availableSession(ClientSession session, Presence presence) {
updateCache(session.getAddress().toFullJID(), presence.getStatus());

}

@Override
public void unavailableSession(ClientSession session, Presence presence) {
updateCache(session.getAddress().toFullJID(), presence.getStatus());

}

@Override
public void presenceChanged(ClientSession session, Presence presence) {
updateCache(session.getAddress().toFullJID(), presence.getStatus());

}

@Override
public void subscribedToPresence(JID subscriberJID, JID authorizerJID) {
// TODO Auto-generated method stub

}

@Override
public void unsubscribedToPresence(JID unsubscriberJID, JID recipientJID) {
// TODO Auto-generated method stub

}

@Override
public void sessionCreated(Session session) {
// TODO Auto-generated method stub

}

@Override
public void sessionDestroyed(Session session) {
updateCache(session.getAddress().toFullJID(),
Presence.Type.unavailable.name());

}

@Override
public void anonymousSessionCreated(Session session) {
// TODO Auto-generated method stub

}

@Override
public void anonymousSessionDestroyed(Session session) {
// TODO Auto-generated method stub

}

@Override
public void resourceBound(Session session) {
// TODO Auto-generated method stub

}
}

2.把源代码打成jar包->status_plugin.jar
3.写plugin.xml配置
<?xml version="1.0" encoding="UTF-8"?>

<plugin>
    <class>com.netease.openfire.plugin.StatusPlugin</class>
    <name>status</name>
    <description>status plugin</description>
    <author>chenke</author>
    <version>0.1</version>
    <date>16/9/2011</date>
    <minServerVersion>3.7.0</minServerVersion>
    <adminconsole/><!- 注意这里既是没有控制台配置,也需要有个单标签 ->
</plugin>

最后按照要求打成插件的jar包。

把status_plugin.jar放入到lib目录中。
4.把打出来的插件jar包放到openfire的plugins目录下即可。
5.重启openfire.





六.Openfire开发心得


这里简单介绍下你可以基于openfire做扩展的几种方式。
本人通过2个星期对openfire的学习和部分功能的扩展。总结出集中扩展方式。
1. 通过开发插件配合xxxlistener。因为plugin能做的事情非常有限,只能拿到pluginmanager和xmppserver的实例。假如你有强力功能要开发,可能不是很好搞定。
2. 通过hacker思想,在启动plugin的时候替换xmppserver的module并且重启xmppserver
3. 在build的时候做手脚,动态字节码增强。

总之,openfire虽然具有模块化的思想,并且留给大家一些扩展点。但是没有统一约定扩展点,并且比较凌乱,需要自己仔细阅读源代码来搞定。并且还不一定能做到。不过大家尽量还是通过第一种方式来开发,是在憋不住,就改源代码吧。。

最后希望大家与我多多交流哈。。。[email protected]

你可能感兴趣的:(openfire)