1.8 Provider 体系结构
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.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='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 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='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> {
@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='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 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
负责解析包扩展,自定义名称空间中的子元素的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
将覆盖其他之前加载。