脚本支持是Tigase的一个基本内置API,不须要任何额外的代价就能让全部的组件都自动支持脚本。但它只能访问那些经过你的代码继承到的父类组件变量,因此你须要把你的数据传递给脚本API。这篇文档会教你如何扩展示有的脚本API来访问组件的数据结构。 java
组件与脚本引擎的集成只须要简单几行代码: 数组
1
2
3
4
5
6
7
8
9
|
privatestaticfinalString BAD_WORDS_VAR ="badWords";
privatestaticfinalString WHITE_LIST_VAR ="whiteList";
@Override
publicvoidinitBindings(Bindings binds) {
super.initBindings(binds);
binds.put(BAD_WORDS_VAR, badWords);
binds.put(WHITE_LIST_VAR, whiteList);
}
|
上面的代码传递了两个组件变量给脚本:“badWords”和“whiteList”,在脚本中变量的名称是一致的。固然也可使用不一样的名称,但一致的名称让事情变得简单和清晰易懂,因此咱们在脚本中使用相同的命名。 服务器
这样就能够了,实际上,全部的事情都已经完成。在咱们过去的版本中,这两个变量是java的字符串数组,因此咱们只可以改变她们的元素,却不能经过脚本向数据结构添加或删除元素。这种方式不够“智慧”,为脚本的开发带来了不少限制。为了解决这个问题,咱们把保存白名单和垃圾关键字的数据结构调整为“java.util.Set”。这给咱们访问数据带来了不少便利也更加灵活。 数据结构
由于组件已经能够和脚本API进行交互了,接下来咱们演示如何经过ad-hoc指令来发送脚本,并对数据结构当中的数据进行添加或删除操做。 ide
若是你使用Psi客户端:首先,在服务发现列表窗口当中双击“test”组件,会弹出一个包含ad-hoc命令列表的新窗口,其余客户端的展示方式也许不一样。 学习
命令列表 spa
点击“New command Script”指令会弹出下面的窗口,你须要填写脚本描述和脚本ID。在样例中咱们使用Groovy语言,但其实你可使用更多脚本语言。 线程
垃圾关键字列表脚 orm
若是想要添加更多脚本语言支持,请参考Tigase脚本文档来得到所有细节。对Tigase API而言,全部的语言都是同样的。你须要从窗口的下拉菜单中选择一个合适的语言。若是想使用的脚本语言不在下拉菜单中,那么它没有被正确的安装,因此Tigase没法检测到。 server
使用Groovy语言来获取当前垃圾关键字列表的代码以下:
1
2
3
4
|
defbadw = (java.util.Set)badWords
defresult =""
for (sinbadw) { result += s +"\n"}
returnresult
|
就像你在脚本中看到的那样,你须要定义一个脚本变量来引用组件中的变量,请使用正确的类型。剩下的事情就是很是简单的纯脚本工做了。执行脚本的结果以下图:
垃圾关键字脚本执行结果
下面的脚本容许你更新(添加/删除)垃圾关键字:
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
|
importtigase.server.Command
importtigase.server.Packet
defWORDS_LIST_KEY ="words-list"
defOPERATION_KEY ="operation"
defREMOVE ="Remove"
defADD ="Add"
defOPERATIONS = [ADD, REMOVE]
defbadw = (java.util.Set)badWords
defPacket p = (Packet)packet
defwords = Command.getFieldValue(p, WORDS_LIST_KEY)
defoperation = Command.getFieldValue(p, OPERATION_KEY)
if(words ==null) {
// No data to process, let's ask user to provide
// a list of words
defres = (Packet)p.commandResult(Command.DataType.form)
Command.addFieldValue(res, WORDS_LIST_KEY,"","Bad words list")
Command.addFieldValue(res, OPERATION_KEY, ADD,"Operation",
(String[])OPERATIONS, (String[])OPERATIONS)
returnres
}
defwords_list = words.tokenize(",")
if(operation == ADD) {
words_list.each{ badw.add(it.trim()) }
return"Words have been added."
}
if(operation == REMOVE) {
words_list.each{ badw.remove(it.trim()) }
return"Words have been removed."
}
return"Unknown operation: "+ operation
|
学习这两个脚本只是开始。脚本应用的空间是很是普遍,如今咱们仅仅为脚本添加了不多的几行代码,将来你能够借助脚本在运行时扩展你的应用,为它添加各类各样的功能;你也能够从新加载脚本,添加/修改或删除你须要的功能。不须要重启服务器,也不须要从新编译代码,更可使用任何你但愿使用的脚本语言。
固然了白名单的操做其实和垃圾关键字的操做是彻底同样的,这里再也不多讲了。
下面是咱们在文章一开始提到的把字符串数组调整为Set的完整代码:
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
importjava.util.Arrays;
importjava.util.Collections;
importjava.util.Map;
importjava.util.Set;
importjava.util.concurrent.CopyOnWriteArraySet;
importjava.util.logging.Level;
importjava.util.logging.Logger;
importjavax.script.Bindings;
importtigase.server.AbstractMessageReceiver;
importtigase.server.Packet;
importtigase.stats.StatisticsList;
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";
privatestaticfinalString BAD_WORDS_VAR ="badWords";
privatestaticfinalString WHITE_LIST_VAR ="whiteList";
privatestaticfinalString[] INITIAL_BAD_WORDS = {"word1","word2","word3"};
privatestaticfinalString[] INITIAL_WHITE_LIST = {"admin@localhost"};
/**
* 当Set在一个线程当中进行遍历的时候内容有可能被另外一个线程修改,咱们认为这种修改是很是小而且不多会发生的,由于绝大多数的操做仅仅是遍历
*/
privateSet<String> badWords =newCopyOnWriteArraySet<String>();
/**
* 当Set在一个线程当中进行遍历的时候内容有可能被另外一个线程修改,咱们认为这种修改是很是小而且不多会发生的,由于绝大多数的操做仅仅是调用contains(...)方法
*/
privateSet<String> whiteList =newConcurrentSkipListSet<String>();
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(!whiteList.contains(from)) {
// 若是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);
Collections.addAll(badWords, INITIAL_BAD_WORDS);
Collections.addAll(whiteList, INITIAL_WHITE_LIST);
defs.put(BAD_WORDS_KEY, INITIAL_BAD_WORDS);
defs.put(WHITELIST_KEY, INITIAL_WHITE_LIST);
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);
Collections.addAll(badWords, (String[])props.get(BAD_WORDS_KEY));
Collections.addAll(whiteList, (String[])props.get(WHITELIST_KEY));
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";
}
@Override
publicvoidgetStatistics(StatisticsList list) {
super.getStatistics(list);
list.add(getName(),"Spam messages found", totalSpamCounter,
Level.INFO);
list.add(getName(),"All messages processed", messagesCounter,
Level.FINE);
if(list.checkLevel(Level.FINEST)) {
// 能够把那些很是消耗系统资源的统计数据产生代码写在下面
}
}
@Override
publicvoidinitBindings(Bindings binds) {
super.initBindings(binds);
binds.put(BAD_WORDS_VAR, badWords);
binds.put(WHITE_LIST_VAR, whiteList);
}
}
|