Tigase組件第四節 – 服務發現

Tigase組件第四節 – 服務發現

發表評論做者  儲天行 on  2010/11/17

本文翻譯自 – http://www.tigase.org/content/component-implementation-lesson-4-service-discovery html

新組件在服務發現列表當中仍然顯示「未定義的描述」。它也沒有提供任何有趣的特性和子節點。 java

接下來,咱們將用簡單的方式修改組件的基本信息,並添加一些服務發現特性。除此以外,文檔還提供一些如何在運行時添加/刪除服務發現節點,如何更新已有元素的指導。 node

組件的描述和類別/類型能夠經過覆寫下面兩個方法來實現: 數組

1
2
3
4
5
6
7
8
9
@Override
publicString getDiscoDescription() {
  return"Spam filtering";
}
 
@Override
publicString getDiscoCategoryType() {
  return"spam";
}

請注意,在服務發現標識註冊表當中,並無定義「Spam」類別/類型。這僅僅是用來演示。在真實應用的時候請參照服務發現標識註冊表當中的類別/類型來挑選一個最適合。添加完上面兩個方法並從新啓動服務器以後,再查看服務發現列表窗口,可能會出現下圖所示的效果。 服務器

更新描述和分類以後的服務發現列表 less

這很簡單,但事實上除了顯示效果上有一些變化以外,本質上組件並無發生任何改變。下面咱們修改它的「本質」。 ide

經過覆寫方法來修改組件描述和類別/類型的一個限制就是:你不能在運行時動態得改變組件的信息。那兩個方法只在setProperties(…)方法執行的時候被調用一次,以後組件的服務發現信息就被建立了。但有時在運行時動態改變服務發現信息是頗有意義的,別人能夠從服務發現信息中得到有用信息。 wordpress

以我們的垃圾信息過濾組件爲例,看看到底能從組件發現信息當中得到多少有用信息。若是在每一次接收到一條消息的時候都調用: 性能

1
2
3
updateServiceDiscoveryItem(getName(),null,
  getDiscoDescription() +": ["+
  (++messagesCounter) +"]",true);

關於服務性能的小貼士:在有些狀況下調用「updateServiceDiscoveryItem(…)方法」會產生很大的性能開銷,因此一個比較好的推薦是每100條消息才調用這個方法一次,而不是每條調用一次。 spa

updateServiceDiscoveryItem(…)方法的第一個入口參數是組件的名稱,它會顯示在服務發現列表的JID列裏。之因此不使用JID是由於:Tigase服務器可能爲多個虛擬域名提供服務,域名部分會在低層方法當中被添加,因此咱們在這裏只使用組件名稱。第二個入口參數是服務發現節點,頂級的disco條目項應該傳遞爲null。第三個參數是它的描述(在disco規範中實際上稱之爲「name」)。最後一個參數是它是否僅對管理員可見。

使用上面的方法咱們還能夠爲組件元素添加子節點。雖然XMPP的服務發現信息原本不是用來顯示計數器的,可是這個例子很好的展現了Tigase API的功能,因此接下來咱們使用服務發現信息來顯示計數器。這一次,第二個參數再也不傳null,咱們傳遞一些有意義的文字看看會產生什麼效果:

1
2
3
4
5
6
// 當組件接收一條消息以後調用下面的語句
updateServiceDiscoveryItem(getName(),"messages",
  "Messages processed: ["+ (++messagesCounter) +"]",true);
// 當組件肯定消息是垃圾信息的時候調用下面的語句
updateServiceDiscoveryItem(getName(),"spam","Spam caught: ["+
  (++totalSpamCounter) +"]",true);

看看最下面的完整代碼樣例。以後咱們向組件發送幾條消息,其中一些是垃圾信息(包含垃圾信息關鍵字)。再打開服務發現列表窗口,就會出現下面的截圖:

正常和垃圾消息計數器

依據咱們的實現方式,在服務器沒有接收到任何消息以前,組件的服務發現信息是不會有子節點的;只有當組件接收到消息纔會調用「updateServiceDiscoveryItem(…)」方法。若是但願在服務啓動以後就包含子節點,那麼能夠在「setProperties(…)」方法當中調用「updateServiceDiscoveryItem(…)」。

請注意,「updateServiceDiscoveryItem(…)」方法能夠添加或修改服務發現信息項,若是是刪除操做還有一個單獨的方法:

1
2
voidremoveServiceDiscoveryItem(String jid,
  String node, String description)

實際上只有前兩個參數比較重要:「jid」和「node」必須對應已經存在的服務發現條目。

update方法還提供兩個附加的變量能夠用來更好得控制服務發現條目項。能夠爲條目項設置不一樣的類型/類別,也可讓它展現一些額外的特性。其中一個比較容易理解的變量能夠更新服務發現條目規範。XMPP有一個專門的規範文檔來描述那些已存在並註冊的特性,咱們建立的垃圾信息過濾組件使用了一個未經定義的特性「垃圾過濾」。下面的文字將說明如何建立兩個特性標識字符串,並把他們設置到咱們的新組件當中。咱們能夠這樣調用update方法:

1
2
updateServiceDiscoveryItem(getName(),null, getDiscoDescription(),
  true,"tigase:x:spam-filter","tigase:x:spam-reporting");

最好在setProperties(…)方法當中調用上面這個方法,這樣組件的服務發現信息能夠在一開始就被設定好。咱們爲組件的disco信息設置了兩個特性:「tigase:x:spam-filter」和「tigase:x:spam-reporting」。update方法能夠接受任意多個入口參數,因此咱們能夠爲它設置任意多個須要的特性(或者依據java規範,傳遞特性字符串數組)。

更新好代碼以後從新啓動服務器,看看服務發現信息發生了什麼改變。


新增的兩個特性

最後一個功能可能對於我們的垃圾過濾組件用處不大,可是對於相似MUC/PubSub這種設置了正確類別和類型的服務發現條目項而言仍是很是有用的。下面咱們爲垃圾過濾組件設置「自動」類別和「垃圾過濾」類型:

1
2
3
updateServiceDiscoveryItem(getName(),null, getDiscoDescription(),
  "automation","spam-filtering",true,
  "tigase:x:spam-filter","tigase:x:spam-reporting");

固然全部的這些設置能夠應用到任何一個服務發現條目當中,即便是條目子節點。下面是完整的代碼樣例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
importjava.util.Arrays;
importjava.util.Map;
importjava.util.logging.Logger;
importtigase.server.AbstractMessageReceiver;
importtigase.server.Packet;
importtigase.util.JIDUtils;
importtigase.xmpp.StanzaType;
 
publicclassTestComponentextendsAbstractMessageReceiver {
 
  privatestaticfinalLogger log =
    Logger.getLogger(TestComponent.class.getName());
 
  privatestaticfinalString BAD_WORDS_KEY ="bad-words";
  privatestaticfinalString WHITELIST_KEY ="white-list";
  privatestaticfinalString PREPEND_TEXT_KEY ="log-prepend";
  privatestaticfinalString SECURE_LOGGING_KEY ="secure-logging";
  privatestaticfinalString ABUSE_ADDRESS_KEY ="abuse-address";
  privatestaticfinalString NOTIFICATION_FREQ_KEY ="notification-freq";
 
  privateString[] badWords = {"word1","word2","word3"};
  privateString[] whiteList = {"admin@localhost"};
  privateString prependText ="Spam detected: ";
  privateString abuseAddress ="abuse@locahost";
  privateintnotificationFrequency =10;
  privateintdelayCounter =0;
  privatebooleansecureLogging =false;
  privatelongspamCounter =0;
  privatelongtotalSpamCounter =0;
  privatelongmessagesCounter =0;
 
  @Override
  publicvoidprocessPacket(Packet packet) {
    // 這是一個message packet嗎?
    if("message"== packet.getElemName()) {
      updateServiceDiscoveryItem(getName(),"messages",
        "Messages processed: ["+ (++messagesCounter) +"]",true);
      String from = JIDUtils.getNodeID(packet.getElemFrom());
      // 消息的發送者在白名單內嗎?
      if(Arrays.binarySearch(whiteList, from) <0) {
        // 若是ta不在白名單裏面,那麼檢查消息的內容
        String body = packet.getElemCData("/message/body");
        if(body !=null&& !body.isEmpty()) {
          body = body.toLowerCase();
          for(String word : badWords) {
            if(body.contains(word)) {
              log.finest(prependText + packet.toString(secureLogging));
              ++spamCounter;
              updateServiceDiscoveryItem(getName(),"spam","Spam caught: ["+
                (++totalSpamCounter) +"]",true);
              return;
            }
          }
        }
      }
    }
    // 不是垃圾信息,返回以便作下一步處理
    Packet result = packet.swapElemFromTo();
    addOutPacket(result);
  }
 
  @Override
  publicintprocessingThreads() {
    returnRuntime.getRuntime().availableProcessors();
  }
 
  @Override
  publicinthashCodeForPacket(Packet packet) {
    if(packet.getElemTo() !=null) {
      returnpacket.getElemTo().hashCode();
    }
    // 程序不該該運行到這裏,全部的packet都必須具備一個目的地地址,可是也許垃圾過濾器也許會過濾一些奇怪的地址
    if(packet.getElemFrom() !=null) {
      returnpacket.getElemFrom().hashCode();
    }
    // 若是程序真的運行到這一部,就應該好好檢查一下到達組件的packet是否正常,而後找到一個更好的計算hashCode方法。
    return1;
  }
 
  @Override
  publicMap<String, Object> getDefaults(Map<String, Object> params) {
    Map<String, Object> defs =super.getDefaults(params);
    defs.put(BAD_WORDS_KEY, badWords);
    defs.put(WHITELIST_KEY, whiteList);
    defs.put(PREPEND_TEXT_KEY, prependText);
    defs.put(SECURE_LOGGING_KEY, secureLogging);
    defs.put(ABUSE_ADDRESS_KEY, abuseAddress);
    defs.put(NOTIFICATION_FREQ_KEY, notificationFrequency);
    returndefs;
  }
 
  @Override
  publicvoidsetProperties(Map<String, Object> props) {
    super.setProperties(props);
    badWords = (String[])props.get(BAD_WORDS_KEY);
    whiteList = (String[])props.get(WHITELIST_KEY);
    Arrays.sort(whiteList);
    prependText = (String)props.get(PREPEND_TEXT_KEY);
    secureLogging = (Boolean)props.get(SECURE_LOGGING_KEY);
    abuseAddress = (String)props.get(ABUSE_ADDRESS_KEY);
    notificationFrequency = (Integer)props.get(NOTIFICATION_FREQ_KEY);
    updateServiceDiscoveryItem(getName(),null, getDiscoDescription(),
      "automation","spam-filtering",true,
      "tigase:x:spam-filter","tigase:x:spam-reporting");
  }
 
  @Override
  publicsynchronizedvoideveryMinute() {
    super.everyMinute();
    if((++delayCounter) >= notificationFrequency) {
      addOutPacket(Packet.getMessage(abuseAddress, getComponentId(),
        StanzaType.chat,"Detected spam messages: "+ spamCounter,
        "Spam counter",null, newPacketId("spam-")));
      delayCounter =0;
      spamCounter =0;
    }
  }
 
  @Override
  publicString getDiscoDescription() {
    return"Spam filtering";
  }
 
  @Override
  publicString getDiscoCategoryType() {
    return"spam";
  }
 
}
相關文章
相關標籤/搜索