流水不争先,争的是滔滔不绝

Tigase开发笔记5:如何自定义插件 Plugin

Tigase 云聊IM 1595℃

1. 定义一个插件

四种处理器插件接口:

  1. 第一步 – 预处理 – XMPPPreprocessorIfc:这是预处理器插件需要实现的接口
  2. 第二步 – 处理 – XMPPProcessorIfc:这是处理器插件需要实现的接口
  3. 第三步 – 投递 – XMPPPostProcessorIfc:这是投递处理器插件需要实现的接口
  4. 第四步 – 过滤 – XMPPPacketFilterIfc:这是结果过滤器插件需要实现的接口

如果要开发一个处理器插件,那么就需要实现XMPPProcessorIfc接口;如果是预处理插件,就需要实现XMPPPreprocessorIfc接口;

当然你也可以实现多个接口,这个取决于你的需求和情况,你也可以继承处理器Helper抽象类(XMPPProcessor.java抽象类)作为基类来实现所有的插件。

public class SpamFilterPlugin extends XMPPProcessor implements XMPPProcessorIfc {

/** Field : Plugin ID Setting ,this' very important 定义插件ID */
private static final String ID        = "spam-filter-xep-0076";
/** Field : Plugin xmlns Setting ,this' very important 定义命名空间 */
private static final String    XMLNS    = "jabber:client";
@Override
  public void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue results, Map settings)
      throws XMPPException {
       // code here...
   }
}

要做的第一件事情就是确定插件ID。它是唯一的,需要放到配置文件里面,告诉服务器在启动时加载并使用相对应的插件。

如果这个插件需要只对特定命名空间下特定名称的元素“感兴趣”,那么我们需要定义XMLNS元素来定义命名空间。

下面用一张图总结一下怎样定义一个插件

2. 重要的处理方法process介绍

/**
 * 处理消息
* @param packet 在PROCESS处理过程中无法修改它
 * @param session XMPPResourceConnection用于保存所有用户的数据,它提供权限访问用户的仓库数据,在没有在线用户SESSION的情况下该参数可以为null
 * @param repo NonAuthUserRepository该参数往往在参数session为NULL的时候被使用,它用于为不在线的用户保存私有或公开的数据信息。
 * @param results Queue这个为输入数据包的处理结果产生的数据包集合,它总被要求一定要存放一个输入数据包PACKET的备份到里面,其实包含了所有需要处理的PACKET,包括process生成的结果packet。
 * @param settings Map  为PLUGIN制定配置信息,一般情况下不需要使用,然而如果需要访问额外的数据库则可以通过配置文件将数据库连接字符串传给plugin
 *
 * @throws XMPPException
 */
@Override
public void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue results, Map settings)
throws XMPPException {
if (session == null) {
return;
   }
try {
      //code here ...
if (Message.ELEM_NAME == packet.getElemName()) {
      }
  }catch (NotAuthorizedException ex) {
log.log(Level.WARNING, "NotAuthorizedException for packet: {0}", packet);
   results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,
"You must authorize session first.", true));
  }
}

3. 在init.properties文件中配置插件

--sm-plugins=spam-filter-xep-0076

备注说明
#如果需要添加(+,默认为+,可忽略不写)或删除(-)插件规则是使用加减号前缀即可
--sm-plugins=+spam-filter-xep-0076,-jabber:iq:register
#如果给这个插件还定义了参数,那么我们可以参考这样的规则
sess-man/plugins-conf/插件ID/参数key = 参数value
eg.
sess-man/plugins-conf/spam-filter-xep-0076/component-jid=message-archive@10.5.1.48
sess-man/plugins-conf/spam-filter-xep-0076/default-store-method=message

4. 在客户端发请求进行调用

我使用的是Openfire的Spark客户端进行调试(Spark登陆界面->高级->启用调试模式->登录)

发送报文

<message to="jiangpr@10.5.1.48" id="OHPTO-19" type="chat" xmlns="jabber:client" from="d@10.5.1.48/Spark"><body>sdf(测试内容发送)</body><thread>qcNhnm</thread><x xmlns="jabber:x:event"><offline/><composing/></x></message>

可以看到报文中的命名空间为:xmlns="jabber:client",我们定义的插件对这个消息就会"感兴趣",这种消息就会被转发到我们定义的插件进行处理。

eg.
<message to="spam-filter@10.5.1.48" id="OHPTO-19" type="chat" xmlns="jabber:client" from="d@10.5.1.48/Spark">
<body>sdf(测试内容发送)packet3</body><thread>qcNhnm</thread>
<x xmlns="jabber:x:event"><offline/><composing/></x>
</message>

5. 常见的一些proccess中的代码处理

public void process(finalPacket packet,
    final XMPPResourceConnection session,
    final NonAuthUserRepository repo,
    final Queue results,
    final Map settings)
    throwsXMPPException {
 
  // 出于性能的考虑,最好在打印日志之前现检查一下日志级别
  if(log.isLoggable(Level.FINEST)) {
    log.finest("Processing packet: "+ packet.toString());
  }
 
  // 如果用户不在线,你也许想跳过后面的处理环节
  if(session ==null) {
    return;
  }// end of if (session == null)
 
  // 当插件在第一次处理这个用户的会话信息的时候,还有另外一种方法可以执行必要的操作
  if(session.getSessionData(ID) ==null) {
    session.putSessionData(ID, ID);
    // 你可以把你的代码放到这里
    .....
    // 如果你不希望终止操作,那么就把return语句去掉
    return;
  }
 
  // 如果用户的会话没有授权,那么每一次调用session.getUserId()方法都会抛出异常
  try{
 
    // 在比较JID之前一定记得要去掉resource部分
    // JID的组成:jid = [ node "@" ] domain [ "/" resource ]
    // 比如:chutianxing@gmail.com/home
    String id = JIDUtils.getNodeID(packet.getElemTo());
    // 检查一下这个packet是否是发给会话的拥有者
    if(session.getUserId().equals(id)) {
      // 如果是,那么这个消息的确是要发送给这个客户端的
      Element elem = packet.getElement().clone();
      Packet result =newPacket(elem);
      // 这里就是我们为最终收到消息的用户设置客户端组件地址的地方了
      // 在大多数情况,这可能是一个能够保持于客户端连接的c2s或Bosh组件
      result.setTo(session.getConnectionId(packet.getElemTo()));
      // 在大多数情况,这一步可以跳过,但是当packet的投递过程出现了什么问题,这么做可以为调用者返回一个错误
      result.setFrom(packet.getTo());
      // 最后不要忘记把结果packet放到结果队列里面去,否则结果会丢失
      results.offer(result);
    }// end of else
 
    // 在比较JID之前一定记得要去掉resource部分
    id = JIDUtils.getNodeID(packet.getElemFrom());
    // 检查一下这个packet是否由会话的拥有者发出
    if(session.getUserId().equals(id)) {
      // 这是一个由客户端发出的packet,最简单的处理就是把packet转发到packet的目的地地址:
      // 简单的对XML元素进行克隆,然后……
      Element result = packet.getElement().clone();
      // 把他放到传出packet队列里面就行了
      results.offer(newPacket(result));
      return;
    }
 
    // 程序真的会运行到这里吗?
    // 是的,一些packet即没有from也没有to地址。最容易理解的一个例子是向服务器发送的获取某些数据的IQ请求。这类packet没有任何地址,并且需要对它做很多复杂的处理
    // 下面的代码展示了如何确定这个seesion就是请求发起者的session
    id = packet.getFrom();
    // 下面的处理和检查getElementFrom差不多
    if(session.getConnectionId().equals(id)) {
      // 这里需要针对IQ packet做一些特别处理,但是我们需要处理的是message,所以这里只需要对它进行转发
      Element result = packet.getElement().clone();
      // 如果程序运行到这里说明packet的from地址是没有的,现在对from属性就行设置
      result.setAttribute("from", session.getJID());
      // 最后把传出packet放到结果队列里面就ok乐
      results.offer(newPacket(result));
    }
 
  }catch(NotAuthorizedException e) {
    log.warning("NotAuthorizedException for packet: "  +
      packet.getStringData());
    results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,
      "You must authorize session first.",true));
  }// end of try-catch
 
}
版权声明:部分文章、图片等内容为用户发布或互联网整理而来,仅供学习参考。如有侵犯您的版权,请联系我们,将立刻删除。
点击这里给我发消息