Tigase開發筆記5:如何自定義插件 Plugin

其他博客比較好的介紹推薦:http://my.oschina.net/greki/blog/209726


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<Packet> results, Map<String, Object> 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的備份到裏面,其實包含了所有需要處理的PACKET,
<strong>包括process生成的結果packet</strong>
 * @param settings Map<String, Object>  爲PLUGIN制定配置信息,一般情況下不需要使用,然而如果需要訪問額外的數據庫則可以通過配置文件將數據庫連接字符串傳給plugin
*
* @throws XMPPException
*/
@Override
public void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> 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=[email protected]
sess-man/plugins-conf/spam-filter-xep-0076/default-store-method=message




4. 在客戶端發請求進行調用
我使用的是Openfire的Spark客戶端進行調試(Spark登陸界面->高級->啓用調試模式->登錄)

發送報文
<message to="[email protected]" id="OHPTO-19" type="chat" xmlns="jabber:client" from="[email protected]/Spark"><body>sdf(測試內容發送)</body><thread>qcNhnm</thread><x xmlns="jabber:x:event"><offline/><composing/></x></message>

可以看到報文中的命名空間爲:xmlns="jabber:client",我們定義的插件對這個消息就會"感興趣",這種消息就會被轉發到我們定義的插件進行處理。


eg.
<message to="[email protected]" id="OHPTO-19" type="chat" xmlns="jabber:client" from="[email protected]/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<Packet> results,
    final Map<String, Object> 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 ]
    // 比如:[email protected]/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
 
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章