提供者体系结构 : Packet扩充和自定义IQ
Smack提供者体系是一个插入packet extensions和IQ packets的定制XML解析的机制。 标准 Smack扩展使用提供者体系构造.两类提供者:
- IQProvider —解析IQ请求为Java对象。
- PacketExtension — 解析附于packe中的XML子文档为PacketExtension实像。
默认情况下, Smack仅知道怎样处理含有子packet的IQ 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.providersor-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='joe@example.com' from='mary@example.com' 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 Classclass Time extends IQ {private Date utc;private TimeZone timeZone;private String display;@Overridepublic 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 Providerpublic 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='juliet@capulet.example/balcony' to='romeo@montage.example'><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> {@Overridepublic MyIQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException {// Define the data we are trying to collect with sane defaultsint age = -1;String user = null;String location = null;// Start parsing loopouterloop: 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 depthif (parser.getDepth() == initialDepth) {break outerloop;}break;}}// Construct the IQ instance at the end of parsing, when all data has been collectedreturn new MyIQ(user, age, location);}}
DiscoItemsProvider
Disco Items Stanza
<iq type='result' from='shakespeare.lit' to='romeo@montague.net/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 XMLjid = 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负责解析包扩展,自定义名称空间中的子元素的IQ、message和presence packets(数据包)。
Pubsub Subscription Stanza
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'><pubsub xmlns='http://jabber.org/protocol/pubsub'><subscription node='princely_musings' jid='francisco@denmark.lit' 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将覆盖其他之前加载。
