1、编写第一个插件
2、插件的注册与注销
3、通过抛出异常,截获消息
这一节课,我们讲解一下,怎么做一个openfire插件。本课要做的插件的功能是:插件接收用户发过来的消息,如果消息的正文中包含了“fuck”这个单词,插件则截断这条消息,不允许openfire进行转发。如果消息不包含“fuck”这个单词,那么插件什么也做。
本课源码在这里下载
怎么开始写一个插件呢?插件的结构是固定的,我们前面2节课已经讲解了插件的基本目录结构,如下图所示:
既然插件的结构是固定的,那么我们就可以通过拷贝以前的插件,或者openfire源码中plugin文件夹中的某个插件,作为我们插件的框架。
例如将openfire_src\src\plugins中的contentFilter拷贝一份,作为我们自己的代码,将其改名为messageFilter,并放到openfire_src\src\plugins目录中。
这样,我们就可以开始写插件了。记住,最好,将contentFilter中的src目录下的源文件删除,因为那不是你需要的源码。
仅仅将messageFilter拷贝到源码目录openfire_src\src\plugins中,还不能编译插件。我们需要将其添加到Java Build Path中。添加步骤为:
右键项目->Build Path->Configure Build Path...->Source(选项卡)->Add Folder,将messageFilter\src\java添加到源码目录中,类似下图:
Plugin.xml是插件配置文件,它包含了插件的基本信息。如插件的名字,用途、作者、版本、日期,以及最小支持的openfire版本等,请在源码中找到这个文件,或者自己新建一个xml文件,写入下面的内容。我们的plugin.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?> <plugin> <class>com.myopenfire.plugin.MessageFilterPlugin</class> <name>过滤插件</name> <description>过滤一些脏话.</description> <author>jack</author> <version>0.0.1</version> <date>9/13/2013</date> <url>http://www.myopenfire.com</url> <minServerVersion>3.10.2</minServerVersion> </plugin>
下面对上面的插件信息进行详细讲解:
1、class属性表示插件的启动类,这个类必须实现Plugin接口,我们前面的课程已经说了该接口。该接口包含initializePlugin和destroyPlugin函数。
2、name表示插件的名字。
3、description表示插件的用途,这些都没有什么用处。
4、minServerVersion很重要,表示这个插件需要在openfire为3.10.2以上的版本运行(包含3.10.2)。如果openfire低于这个版本,那么插件可能运行不正常。
MessageFilterPlugin这个类是插件的实现类,这个类主要完成插件的注册和注销工作。 为了便于学习,我们将其代码列出如下:
package com.myopenfire.plugin; import java.io.File; import org.jivesoftware.openfire.container.Plugin; import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.interceptor.InterceptorManager; import org.jivesoftware.openfire.interceptor.PacketInterceptor; import org.jivesoftware.openfire.interceptor.PacketRejectedException; import org.jivesoftware.openfire.session.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.packet.Message; import org.xmpp.packet.Packet; /** * 过滤插件:当body中有fuck时,将消息截断,不转发消息。 * */ public class MessageFilterPlugin implements Plugin ,PacketInterceptor{ // A:生成一个日志实例,用于打印日志,日志被打印在openfire_src\target\openfire\logs目录中 private static final Logger Log = LoggerFactory.getLogger(MessageFilterPlugin.class); //B: 消息拦截器 private InterceptorManager interceptorManager; //C: 插件初始化函数 @Override public void initializePlugin(PluginManager manager, File pluginDirectory) { Log.info("MessageFilter init"); // 将当前插件加入到消息拦截管理器(interceptorManager )中,当消息到来或者发送出去的时候,会触发本插件的interceptPacket方法。 interceptorManager = InterceptorManager.getInstance(); interceptorManager.addInterceptor(this); } //D: 插件销毁函数 @Override public void destroyPlugin() { Log.info("MessageFilter destory"); // 当插件被卸载的时候,主要通过openfire管理控制台卸载插件时,被调用。注意interceptorManager的addInterceptor和removeInterceptor需要成对调用。 interceptorManager.removeInterceptor(this); } //E 插件拦截处理函数 @Override public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException { // incoming表示本条消息刚进入openfire。processed为false,表示本条消息没有被openfire处理过。这说明这是一条处女消息,也就是没有被处理过的消息。 if (incoming && processed == false) { // packet可能是IQ、Presence、Message,这里当packet是message的时候,进行处理。 if (packet instanceof Message) { // 将packet强制转换为Message Message msg = (Message)packet; // 取得message中的body内容,就是消息正文 String body = msg.getBody(); // 如果内容中包含fuck,则拒绝处理消息 if(body != null && body.contains("fuck")){ // F: 这里通过抛出异常的方式,来阻止程序流程继续执行下去。 PacketRejectedException rejectedException = new PacketRejectedException(); rejectedException.setRejectionMessage("fuck is error"); throw rejectedException; } } } } }
下面对上面的代码,进行详细的讲解:
1、A处代码:这里定义了一个Log日志,日志被打印在openfire_src\target\openfire\logs目录中的文件中,一般有debug.log、error.log、info.log 等文件。
2、B处代码:定义了一个消息拦截器interceptorManager,用来给插件发送通知。例如当消息进入openfire时,或者消息离开openfire时,给插件发送通知。
3、C处代码:这里是插件初始化函数。
4、D处代码:这里是插件销毁函数。
5、E处代码:这里是插件拦截处理函数。详细的解释请看注释。
6、F处代码:这里的代码非常重要。我们先看看interceptPacket函数,这个函数接受一个异常,其原型如下:
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException
需要注意的是:一旦interceptPacket函数内部抛出PacketRejectedException异常,那么流入interceptPacket中的packet包将被丢弃,不被进一步处理。也就是说,这条消息的生命周期,就到这里了,后面的原本需要将这条消息送到目的地的动作,就没有机会执行了。对于接收消息的客户端而言,就是收不到这条消息了。
7、我们接下来,再看看F处的三行代码,如下:
PacketRejectedException rejectedException = new PacketRejectedException(); rejectedException.setRejectionMessage("fuck is error"); throw rejectedException;
首先new了一个PacketRejectedException 对象,通过源码,我们可以知道,这个对象最终会被转换为一个Message。
上面的第二行代码,通过setRejectionMessage给这条message的body设置为fuck is error, 最后抛出这个消息被拒绝的异常。
当interceptPacket函数抛出这个异常,并向上面发出这个异常后,openfire中的消息处理逻辑,会发现这个异常,并且当这个异常PacketRejectedException对象中的message不为空的时候,会给发送者返回一条body为fuck is error的消息。这时,你的发送方客户端就会收到这条非法消息。
Ok,讲了这么多,不知道您明白了没有呢。如果没有明白,就动手自己写一些吧。或者看我们后面的视频课程,动手实践一下。视频中有关于插件、客户端、协议分析的完整讲解,可以降低一点您的学习难度。
经过上面的步骤,如果编译器没有报语法错误,那么您的插件应该是写好了。现在只剩下最后一步,就是编译插件了。 在eclipse中打开Ant视图,步骤如下:
Eclipse->Window->Show View->Othre->Ant->Ant,即可打开Ant视图,如下图所示:
双击 plugins编译插件,即可在目录openfire_src\target\openfire\plugins中找到插件,将其通过openfire控制台传到openfire上就可以了。
Openfire插件是扩展openfire功能非常重要的机制,可以说学会这个机制,扩展openfire的大部分功能,就不成问题了。
所以,无论如何,大家要重视对openfire插件的学习和实践。
最后,感谢大家的阅读,我们下节课再见。