第七课、编写第一个openfire插件(过滤脏话插件)

阅读:4135

1、编写第一个插件

2、插件的注册与注销

3、通过抛出异常,截获消息

一、前言

这一节课,我们讲解一下,怎么做一个openfire插件。本课要做的插件的功能是:插件接收用户发过来的消息,如果消息的正文中包含了“fuck”这个单词,插件则截断这条消息,不允许openfire进行转发。如果消息不包含“fuck”这个单词,那么插件什么也做。

本课源码在这里下载

二、怎么开始写一个插件

怎么开始写一个插件呢?插件的结构是固定的,我们前面2节课已经讲解了插件的基本目录结构,如下图所示:

既然插件的结构是固定的,那么我们就可以通过拷贝以前的插件,或者openfire源码中plugin文件夹中的某个插件,作为我们插件的框架。

例如将openfire_src\src\plugins中的contentFilter拷贝一份,作为我们自己的代码,将其改名为messageFilter,并放到openfire_src\src\plugins目录中。

这样,我们就可以开始写插件了。记住,最好,将contentFilter中的src目录下的源文件删除,因为那不是你需要的源码。

三、将messageFilter源码添加到eclipse的源码目录中

仅仅将messageFilter拷贝到源码目录openfire_src\src\plugins中,还不能编译插件。我们需要将其添加到Java Build Path中。添加步骤为:

右键项目->Build Path->Configure Build Path...->Source(选项卡)->Add Folder,将messageFilter\src\java添加到源码目录中,类似下图:

四、编写插件配置文件plugin.xml

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低于这个版本,那么插件可能运行不正常。

五、编写com.myopenfire.plugin.MessageFilterPlugin类

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插件的学习和实践。

最后,感谢大家的阅读,我们下节课再见。

提问或评论

登陆后才可留言或提问哦:) 登陆 | 注册 登陆后请返回本课提问
用户名
密   码
验证码
付老师疑难问答