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";
  }
 
}
相关文章
相关标签/搜索