本文翻譯自 – 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」類別/類型。這僅僅是用來演示。在真實應用的時候請參照服務發現標識註冊表當中的類別/類型來挑選一個最適合。添加完上面兩個方法並從新啓動服務器以後,再查看服務發現列表窗口,可能會出現下圖所示的效果。 服務器
這很簡單,但事實上除了顯示效果上有一些變化以外,本質上組件並無發生任何改變。下面咱們修改它的「本質」。 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";
}
}
|