提供者体系结构 : Packet扩充和自定义IQ

Smack提供者体系是一个插入packet extensionsIQ packets的定制XML解析的机制。 标准 Smack扩展使用提供者体系构造.两类提供者:

  • IQProvider —解析IQ请求为Java对象。
  • PacketExtension — 解析附于packe中的XML子文档为PacketExtension实像。

默认情况下, Smack仅知道怎样处理含有子packetIQ packet,这些子packet存在像如下所示的命名空间中:

  • jabber:iq:auth
  • jabber:iq:roster
  • jabber:iq:register

有很多IQ类型和扩展XMPP标准的一部分,当然,可以添加不限数量的自定义扩展。为了支持这一点,一个可扩展的用户提供通过Smack和解析机制构建供应商。

无论何时发现包扩展包,解析将被传递到正确的供应商。每个提供者必须实现PacketExtensionProvider接口。每个扩展提供者负责解析原始XML流,通过XML Pull解析器,contruct对象。

您还可以创建一个内部提供者(provider.IntrospectionProvider.PacketExtensionIntrospectionProvider)。这里,使用bean内省来自动设置的属性类使用数据包中的值扩展元素。

当没有扩展提供者注册一个元素名称和命名空间的组合,Smack将存储所有的顶级元素sub-packet DefaultPacketExtension对象,然后将它附加到数据包。

通过ProviderManager类完成这些providers的管理。有多种方法往manager中添加providers。

  • 调用addXXProvider方法——你可以直接调用适当的添加方法
    1. ProviderManager.addIQProvider("element", "namespace", new MyIQProvider());
    2. ProviderManager.addExtensionProvider("element", "namespace", new MyExtProvider());
  • 添加一个加载器——你可以添加一个ProviderLoader将注入的方式加载多个提供者(两种类型)经理。这是打加载所使用的机制从特定的文件格式(通过ProviderFileLoader)。实现者可以提供负载的手段提供他们希望从任何来源,或简单地重用ProviderFileLoader从他们自己的供应商文件来加载。
    1. ProviderManager.addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:com/myco/provider/myco_custom.providers", null)));
  • VM参数——你可以通过VM参数smack.provider.file文件添加一个提供者。这将加载文件在指定的URL在启动时初始化。这还假设缺省配置,因为它要求VmArgInitializer是启动配置的一部分。
    1. -Dsmack.provider.file=classpath:com/myco/provider/myco_custom.providers
    2. or
    3. -Dsmack.provider.file=file:///c:/myco/provider/myco_custom.providers

IQ Providers(IQ提供者)

IQ提供者类必须实现IQProvider接口。每个IQProvider负责解析原始XML流创建一个IQ实例。

您还可以创建一个内省提供者(provider.IntrospectionProvider.IQIntrospectionProvider)。使用bean内省来自动设置属性的IQ实例使用IQ包中发现XML值。例如,一个XMPP时间包类似于以下:

  1. // Time Stanza
  2. <iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'>
  3. <query xmlns='jabber:iq:time'>
  4. <utc>20020910T17:58:35</utc>
  5. <tz>MDT</tz>
  6. <display>Tue Sep 10 12:58:35 2002</display>
  7. </query>
  8. </iq>
  1. //Time IQ Class
  2. class Time extends IQ {
  3. private Date utc;
  4. private TimeZone timeZone;
  5. private String display;
  6. @Override
  7. public String getChildElementXML() {
  8. return null;
  9. }
  10. public void setUtc(String utcString) {
  11. try {
  12. utc = StringUtils.parseDate(utcString);
  13. } catch (ParseException e) {
  14. }
  15. }
  16. public void setTimeZone(String zone) {
  17. timeZone = TimeZone.getTimeZone(zone);
  18. }
  19. public void setDisplay(String timeDisplay) {
  20. display = timeDisplay;
  21. }
  22. }
  23. //Time Provider
  24. public class TimeProvider extends IQIntrospectionProvider<Time> {
  25. public TimeProvider() {
  26. super(Time.class);
  27. }
  28. }

自检服务将自动尝试从XML字符串值转换为一个boolean, int, long, float, double,或者Class,根据预设的IQ实例的类型。

自定义IQProvider例子

让我们假设你想写一个新的provider,不支持的IQ在Smack中。
自定义的IQ

  1. <iq type='set' from='juliet@capulet.example/balcony' to='romeo@montage.example'>
  2. <myiq xmlns='example:iq:foo' token='secret'>
  3. <user age='42'>John Doe</user>
  4. <location>New York</location>
  5. </myiq>
  6. </iq>

自定义的IQ Provider

  1. public class MyIQProvider extends IQProvider<MyIQ> {
  2. @Override
  3. public MyIQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException {
  4. // Define the data we are trying to collect with sane defaults
  5. int age = -1;
  6. String user = null;
  7. String location = null;
  8. // Start parsing loop
  9. outerloop: while(true) {
  10. int eventType = parser.next();
  11. switch(eventType) {
  12. case XmlPullParser.START_TAG:
  13. String elementName = parser.getName();
  14. switch (elementName) {
  15. case "user":
  16. age = ParserUtils.getIntegerAttribute(parser, "age");
  17. user = parser.nextText();
  18. break;
  19. case "location"
  20. location = parser.nextText();
  21. break;
  22. }
  23. break;
  24. case XmlPullParser.END_TAG:
  25. // Abort condition: if the are on a end tag (closing element) of the same depth
  26. if (parser.getDepth() == initialDepth) {
  27. break outerloop;
  28. }
  29. break;
  30. }
  31. }
  32. // Construct the IQ instance at the end of parsing, when all data has been collected
  33. return new MyIQ(user, age, location);
  34. }
  35. }

DiscoItemsProvider

Disco Items Stanza

  1. <iq type='result' from='shakespeare.lit' to='romeo@montague.net/orchard' id='items1'>
  2. <query xmlns='http://jabber.org/protocol/disco#items'>
  3. <item jid='people.shakespeare.lit' name='Directory of Characters'/>
  4. <item jid='plays.shakespeare.lit' name='Play-Specific Chatrooms'/>
  5. <item jid='mim.shakespeare.lit' name='Gateway to Marlowe IM'/>
  6. <item jid='words.shakespeare.lit' name='Shakespearean Lexicon'/>
  7. <item jid='globe.shakespeare.lit' name='Calendar of Performances'/>
  8. <item jid='headlines.shakespeare.lit' name='Latest Shakespearean News'/>
  9. <item jid='catalog.shakespeare.lit' name='Buy Shakespeare Stuff!'/>
  10. <item jid='en2fr.shakespeare.lit' name='French Translation Service'/>
  11. </query>
  12. </iq>

Disco Items IQProvider

  1. public class DiscoverItemsProvider implements IQProvider<DiscoverItems> {
  2. public DiscoverItems parseIQ(XmlPullParser parser, int initialDepth) throw XmlPullParserException, IOException {
  3. DiscoverItems discoverItems = new DiscoverItems();
  4. DiscoverItems.Item item;
  5. String jid = "";
  6. String name = "";
  7. String action = "";
  8. String node = "";
  9. discoverItems.setNode(parser.getAttributeValue("", "node"));
  10. outerloop: while (true) {
  11. int eventType = parser.next();
  12. switch (eventType) {
  13. case XmlPullParser.START_TAG:
  14. String elementName = parser.getName();
  15. switch (elementName) {
  16. case "item":
  17. // Initialize the variables from the parsed XML
  18. jid = parser.getAttributeValue("", "jid");
  19. name = parser.getAttributeValue("", "name");
  20. node = parser.getAttributeValue("", "node");
  21. action = parser.getAttributeValue("", "action");
  22. break;
  23. }
  24. break;
  25. case XmlPullParser.END_TAG:
  26. String elementName = parser.getName();
  27. switch (elementName) {
  28. case "item":
  29. // Create a new Item and add it to DiscoverItems.
  30. item = new DiscoverItems.Item(jid);
  31. item.setName(name);
  32. item.setNode(node);
  33. item.setAction(action);
  34. discoverItems.addItem(item);
  35. break;
  36. case "query":
  37. if (parser.getDepth() == initialDepth) {
  38. break outerloop;
  39. }
  40. break;
  41. }
  42. }
  43. }
  44. return discoverItems;
  45. }
  46. }

Extension Providers(扩展Providers)

Stanza extension providers负责解析包扩展,自定义名称空间中的子元素的IQmessagepresence packets(数据包)。

Pubsub Subscription Stanza

  1. <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'>
  2. <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  3. <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'>
  4. <subscribe-options>
  5. <required/>
  6. </subscribe-options>
  7. </subscription>
  8. </pubsub>
  9. </iq>

Subscription PacketExtensionProvider Implementation

  1. public class SubscriptionProvider implements PacketExtensionProvider {
  2. public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
  3. String jid = parser.getAttributeValue(null, "jid");
  4. String nodeId = parser.getAttributeValue(null, "node");
  5. String subId = parser.getAttributeValue(null, "subid");
  6. String state = parser.getAttributeValue(null, "subscription");
  7. boolean isRequired = false;
  8. int tag = parser.next();
  9. if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options")) {
  10. tag = parser.next();
  11. if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required"))
  12. isRequired = true;
  13. while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options");
  14. }
  15. while (parser.getEventType() != XmlPullParser.END_TAG) parser.next();
  16. return new Subscription(jid, nodeId, subId, (state == null ? null : Subscription.State.valueOf(state), isRequired);
  17. }
  18. }

Provider file format(Provider文件格式)

这是Provider文件的格式可以由ProviderFileLoader解析。

  1. <?xml version="1.0"?>
  2. <smackProviders>
  3. <iqProvider>
  4. <elementName>query</elementName>
  5. <namespace>jabber:iq:time</namespace>
  6. <className>org.jivesoftware.smack.packet.Time</className>
  7. </iqProvider>
  8. <iqProvider>
  9. <elementName>query</elementName>
  10. <namespace>http://jabber.org/protocol/disco#items</namespace>
  11. <className>org.jivesoftware.smackx.provider.DiscoverItemsProvider</className>
  12. </iqProvider>
  13. <extensionProvider>
  14. <elementName>subscription</elementName>
  15. <namespace>http://jabber.org/protocol/pubsub</namespace>
  16. <className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
  17. </extensionProvider>
  18. </smackProviders>

每个提供者与元素名称和名称空间相关联。如果多个提供者条目试图注册处理相同的名称空间中,最后一个条目添加到ProviderManager将覆盖其他之前加载。