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

// Time Stanza

<iq type='result' to='[email protected]' from='[email protected]' id='time_1'>
    <query xmlns='jabber:iq:time'>
       <utc>20020910T17:58:35</utc>
       <tz>MDT</tz>
       <display>Tue Sep 10 12:58:35 2002</display>
    </query>
</iq>
//Time IQ Class

class Time extends IQ {
    private Date utc;
    private TimeZone timeZone;
    private String display;

    @Override
    public String getChildElementXML() {
       return null;
    }

    public void setUtc(String utcString) {
       try {
         utc = StringUtils.parseDate(utcString);
       } catch (ParseException e) {
       }
    }

    public void setTimeZone(String zone) {
       timeZone = TimeZone.getTimeZone(zone);
    }

    public void setDisplay(String timeDisplay) {
       display = timeDisplay;
    }
}

//Time Provider

public class TimeProvider extends IQIntrospectionProvider<Time> {

    public TimeProvider() {
        super(Time.class);
    }

}

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

自定义IQProvider例子

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

<iq type='set' from='[email protected]/balcony' to='[email protected]'>
  <myiq xmlns='example:iq:foo' token='secret'>
    <user age='42'>John Doe</user>
    <location>New York</location>
  </myiq>
</iq>

自定义的IQ Provider

public class MyIQProvider extends IQProvider<MyIQ> {

  @Override
  public MyIQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException {
    // Define the data we are trying to collect with sane defaults
    int age = -1;
    String user = null;
    String location = null;

    // Start parsing loop
    outerloop: while(true) {
      int eventType = parser.next();
      switch(eventType) {
      case XmlPullParser.START_TAG:
        String elementName = parser.getName();
        switch (elementName) {
        case "user":
          age = ParserUtils.getIntegerAttribute(parser, "age");
          user = parser.nextText();
          break;
        case "location"
          location = parser.nextText();
          break;
        }
        break;
      case XmlPullParser.END_TAG:
        // Abort condition: if the are on a end tag (closing element) of the same depth
        if (parser.getDepth() == initialDepth) {
          break outerloop;
        }
        break;
      }
    }

    // Construct the IQ instance at the end of parsing, when all data has been collected
    return new MyIQ(user, age, location);
  }
}

DiscoItemsProvider

Disco Items Stanza

<iq type='result' from='shakespeare.lit' to='[email protected]/orchard' id='items1'>
   <query xmlns='http://jabber.org/protocol/disco#items'>
       <item jid='people.shakespeare.lit' name='Directory of Characters'/>
       <item jid='plays.shakespeare.lit' name='Play-Specific Chatrooms'/>
       <item jid='mim.shakespeare.lit' name='Gateway to Marlowe IM'/>
       <item jid='words.shakespeare.lit' name='Shakespearean Lexicon'/>
       <item jid='globe.shakespeare.lit' name='Calendar of Performances'/>
       <item jid='headlines.shakespeare.lit' name='Latest Shakespearean News'/>
       <item jid='catalog.shakespeare.lit' name='Buy Shakespeare Stuff!'/>
       <item jid='en2fr.shakespeare.lit' name='French Translation Service'/>
   </query>
</iq>

Disco Items IQProvider



public class DiscoverItemsProvider implements IQProvider<DiscoverItems> {

    public DiscoverItems parseIQ(XmlPullParser parser, int initialDepth) throw XmlPullParserException, IOException {
       DiscoverItems discoverItems = new DiscoverItems();
       DiscoverItems.Item item;
       String jid = "";
       String name = "";
       String action = "";
       String node = "";
       discoverItems.setNode(parser.getAttributeValue("", "node"));
       outerloop: while (true) {
         int eventType = parser.next();
         switch (eventType) {
         case XmlPullParser.START_TAG:
          String elementName = parser.getName();
          switch (elementName) {
          case "item":
              // Initialize the variables from the parsed XML
              jid = parser.getAttributeValue("", "jid");
              name = parser.getAttributeValue("", "name");
              node = parser.getAttributeValue("", "node");
              action = parser.getAttributeValue("", "action");
              break;
          }
          break;
         case XmlPullParser.END_TAG:
          String elementName = parser.getName();
          switch (elementName) {
          case "item":
              // Create a new Item and add it to DiscoverItems.
              item = new DiscoverItems.Item(jid);
              item.setName(name);
              item.setNode(node);
              item.setAction(action);
              discoverItems.addItem(item);
              break;
          case "query":
              if (parser.getDepth() == initialDepth) {
                 break outerloop;
              }
              break;
          }
         }
       }
       return discoverItems;
    }
}

Extension Providers(扩展Providers)

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

Pubsub Subscription Stanza

<iq type='result' from='pubsub.shakespeare.lit' to='[email protected]/barracks' id='sub1'>
   <pubsub xmlns='http://jabber.org/protocol/pubsub'>
       <subscription node='princely_musings' jid='[email protected]' subscription='unconfigured'>
          <subscribe-options>
            <required/>
          </subscribe-options>
       </subscription>
   </pubsub>
</iq>

Subscription PacketExtensionProvider Implementation

public class SubscriptionProvider implements PacketExtensionProvider {
    public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
       String jid = parser.getAttributeValue(null, "jid");
       String nodeId = parser.getAttributeValue(null, "node");
       String subId = parser.getAttributeValue(null, "subid");
       String state = parser.getAttributeValue(null, "subscription");
       boolean isRequired = false;

       int tag = parser.next();

       if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options")) {
         tag = parser.next();

         if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required"))
          isRequired = true;

         while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options");
       }
       while (parser.getEventType() != XmlPullParser.END_TAG) parser.next();
       return new Subscription(jid, nodeId, subId, (state == null ? null : Subscription.State.valueOf(state), isRequired);
    }
}

Provider file format(Provider文件格式)

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

<?xml version="1.0"?>
<smackProviders>
<iqProvider>
    <elementName>query</elementName>
    <namespace>jabber:iq:time</namespace>
    <className>org.jivesoftware.smack.packet.Time</className>
</iqProvider>

<iqProvider>
    <elementName>query</elementName>
    <namespace>http://jabber.org/protocol/disco#items</namespace>
    <className>org.jivesoftware.smackx.provider.DiscoverItemsProvider</className>
</iqProvider>

<extensionProvider>
    <elementName>subscription</elementName>
    <namespace>http://jabber.org/protocol/pubsub</namespace>
    <className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
</extensionProvider>
</smackProviders>

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