mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2025-09-09 10:19:41 +02:00
SMACK-286 Made ProviderManager much more configurable.
Separated the reading of provider files from the ProviderManager. Manager now only manages. Added ability to add collections of providers to the manager via a ProviderLoader, of which there is one default implementation which loads from the default file format. Now provider files can be programmatically added at any time. Also updated the configuration abilities so that a provider file can also be set via VM arg, as well as the smack configuration itself. Introduced Java Util Logging as well. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/branches/smack_3_4_0@13861 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
7e3d4186bb
commit
f155cb4d07
25 changed files with 1709 additions and 1000 deletions
|
@ -40,6 +40,37 @@ over which features applications require:
|
|||
</ul>
|
||||
|
||||
|
||||
<p class="subheader">Configuration</p>
|
||||
Smack has an initialization process that involves 2 phases.
|
||||
<ul>
|
||||
<li>Initializing system properties - Initializing all the system properties accessible through the class
|
||||
<b>SmackConfiguration</b>. These properties are retrieve by the <i>getXXX</i> methods on that class.
|
||||
<li>Initializing startup classes - Initializing any classes meant to be active at startup by instantiating
|
||||
the class, and then calling the <i>initialize</i> method on that class if it extends <b>SmackInitializer</b>.
|
||||
If it does not extend this interface, then initialization will have to take place in a static block of code
|
||||
which is automatically executed when the class is loaded.
|
||||
</ul>
|
||||
<p>
|
||||
Initialization is accomplished via a configuration file. By default, Smack will load the one embedded in
|
||||
the Smack jar at <i>META-INF/smack-config.xml</i>. This particular configuration contains all the default
|
||||
property values as well as a list of initializer classes to load. All manager type classes are contained
|
||||
in this list of initializers. If your application does not use all the features provided by Smack via the
|
||||
aforementioned managers, you may want to 'turn them off' by providing a custom config file that does not
|
||||
include that feature.
|
||||
<p>
|
||||
If you want to change the configuration file used, you have two options:
|
||||
<ul>
|
||||
<li>Programmatically - Call the <i>setConfigFileUrl</i> method of <b>SmackConfiguration</b> with the location
|
||||
of a new config file.
|
||||
<pre>SmackConfiguration.setConfigFileUrl("classpath:test/smack-config.xml", null)</pre>
|
||||
<li>VM Argument - Set the VM argument <i>smack.config.file</i> to the location of a new config file.
|
||||
<pre>-Dsmack.config.file=file:///c:/com/myco/provider/myco_custom_config.xml</pre>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Please note, there is a copy of the <b>smack-config.xml</b> in the <i>samples</i> directory of the deployment
|
||||
archive file (zip or tar).
|
||||
|
||||
<p class="subheader">
|
||||
Establishing a Connection
|
||||
</p>
|
||||
|
|
|
@ -15,44 +15,68 @@ Provider Architecture: Packet Extensions and Custom IQ's
|
|||
</div>
|
||||
|
||||
<p>
|
||||
<p class="subheader">Introduction</p>
|
||||
|
||||
The Smack provider architecture is a system for plugging in
|
||||
custom XML parsing of packet extensions and IQ packets. The
|
||||
standard <a href="extensions/index.html">Smack Extensions</a>
|
||||
are built using the provider architecture. Two types of
|
||||
providers exist:<ul>
|
||||
are built using the provider architecture. There are two types of
|
||||
providers:<ul>
|
||||
<li><tt>IQProvider</tt> -- parses IQ requests into Java objects.
|
||||
<li><tt>PacketExtension</tt> -- parses XML sub-documents attached to
|
||||
<li><tt>Extension Provider</tt> -- parses XML sub-documents attached to
|
||||
packets into PacketExtension instances.</ul>
|
||||
|
||||
<p class="subheader">IQProvider</p>
|
||||
|
||||
By default, Smack only knows how to process IQ packets with sub-packets that
|
||||
are in a few namespaces such as:<ul>
|
||||
By default, Smack only knows how to process a few standard packets and sub-packets
|
||||
that are in a few namespaces such as:<ul>
|
||||
<li>jabber:iq:auth
|
||||
<li>jabber:iq:roster
|
||||
<li>jabber:iq:register</ul>
|
||||
|
||||
Because many more IQ types are part of XMPP and its extensions, a pluggable IQ parsing
|
||||
mechanism is provided. IQ providers are registered programatically or by creating a
|
||||
smack.providers file in the META-INF directory of your JAR file. The file is an XML
|
||||
document that contains one or more iqProvider entries, as in the following example:
|
||||
There are many more IQ types and extensions that are part of XMPP standards, and of
|
||||
course an endless number that can be added as custom extensions. To support this, an
|
||||
extensible parsing mechanism is provided via Smack and user build providers.
|
||||
<p>
|
||||
Whenever a packet extension is found in a packet, parsing will
|
||||
be passed to the correct provider. Each provider can either implement the
|
||||
PacketExtensionProvider interface or be a standard Java Bean. In the
|
||||
former case, each extension provider is responsible for parsing the raw
|
||||
XML stream, via the <a href="http://www.xmlpull.org/">XML Pull Parser</a>, to contruct an object. In the latter case, bean introspection
|
||||
is used to try to automatically set the properties of the class using
|
||||
the values in the packet extension sub-element.
|
||||
<p>
|
||||
When no extension provider is registered for an element name and
|
||||
namespace combination, Smack will store all top-level elements of the
|
||||
sub-packet in the DefaultPacketExtension object and then attach it to the packet.
|
||||
|
||||
<p>
|
||||
Management of these providers is accomplished via the <a href="">ProviderManager</a>
|
||||
class. There are multiple ways to add providers to the manager.<ul>
|
||||
<li>Call addXXProvider methods - You can call the appropriate add methods directly.
|
||||
<pre>
|
||||
ProviderManager.getInstance().addIQProvider("element", "namespace", new MyIQProvider());
|
||||
ProviderManager.getInstance().addExtensionProvider("element", "namespace", new MyExtProvider());
|
||||
</pre>
|
||||
<li>Add a loader - You can add a ProviderLoader which will inject a means of loading multiple
|
||||
providers (both types) into the manager. This is the mechanism used by Smack to load from the
|
||||
Smack specific file format (via ProviderFileLoader). Implementers can provide the means to load
|
||||
providers from any source they wish, or simply reuse the ProviderFileLoader to load from
|
||||
their own provider files.
|
||||
<pre>
|
||||
ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:com/myco/provider/myco_custom.providers", null)));
|
||||
</pre>
|
||||
<li>VM Argument - You can add a provider file via the VM argument <i>smack.provider.file</i>.
|
||||
This will load the file at the specified URL during startup when Smack initializes.
|
||||
This also assumes the default configuration, since it requires that the <b>VmArgInitializer</b> was
|
||||
part of the startup configuration.
|
||||
|
||||
<pre>
|
||||
<?xml version="1.0"?>
|
||||
<smackProviders>
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:time</namespace>
|
||||
<className>org.jivesoftware.smack.packet.Time</className>
|
||||
</iqProvider>
|
||||
</smackProviders></pre>
|
||||
-Dsmack.provider.file=classpath:com/myco/provider/myco_custom.providers
|
||||
or
|
||||
-Dsmack.provider.file=file:///c:/myco/provider/myco_custom.providers
|
||||
</pre>
|
||||
</ul>
|
||||
|
||||
Each IQ provider is associated with an element name and a namespace. In the
|
||||
example above, the element name is <tt>query</tt> and the namespace is
|
||||
<tt>abber:iq:time</tt>. If multiple provider entries attempt to register to
|
||||
handle the same namespace, the first entry loaded from the classpath will
|
||||
take precedence. <p>
|
||||
<p class="subheader">IQ Providers</p>
|
||||
|
||||
The IQ provider class can either implement the IQProvider
|
||||
interface, or extend the IQ class. In the former case, each IQProvider is
|
||||
|
@ -61,54 +85,191 @@ the latter case, bean introspection is used to try to automatically set
|
|||
properties of the IQ instance using the values found in the IQ packet XML.
|
||||
For example, an XMPP time packet resembles the following:
|
||||
|
||||
<p>
|
||||
<i>Introspection</i>
|
||||
<p>
|
||||
<u>Time Packet</u>
|
||||
<pre>
|
||||
<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></pre>
|
||||
<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>
|
||||
</pre>
|
||||
|
||||
In order for this packet to be automatically mapped to the Time object listed in the
|
||||
providers file above, it must have the methods setUtc(String), setTz(String), and
|
||||
setDisplay(String). The introspection service will automatically try to convert the String
|
||||
<p>
|
||||
<u>Time IQ Class</u>
|
||||
<pre>
|
||||
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;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
The introspection service will automatically try to convert the String
|
||||
value from the XML into a boolean, int, long, float, double, or Class depending on the
|
||||
type the IQ instance expects.<p>
|
||||
type the IQ instance expects.
|
||||
|
||||
<p class="subheader">PacketExtensionProvider</p>
|
||||
<p>
|
||||
<i>IQProvider Implementation</i>
|
||||
<p>
|
||||
<u>Disco Items Packet</u>
|
||||
<pre>
|
||||
<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>
|
||||
</pre>
|
||||
|
||||
Packet extension providers provide a pluggable system for
|
||||
packet extensions, which are child elements in a custom namespace
|
||||
of IQ, message and presence packets.
|
||||
Each extension provider is registered with an element name and namespace
|
||||
in the smack.providers file as in the following example:
|
||||
<p>
|
||||
<u>Disco Items IQProvider</u>
|
||||
<pre>
|
||||
public class DiscoverItemsProvider implements IQProvider {
|
||||
|
||||
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||
DiscoverItems discoverItems = new DiscoverItems();
|
||||
boolean done = false;
|
||||
DiscoverItems.Item item;
|
||||
String jid = "";
|
||||
String name = "";
|
||||
String action = "";
|
||||
String node = "";
|
||||
discoverItems.setNode(parser.getAttributeValue("", "node"));
|
||||
while (!done) {
|
||||
int eventType = parser.next();
|
||||
|
||||
if (eventType == XmlPullParser.START_TAG && "item".equals(parser.getName())) {
|
||||
// Initialize the variables from the parsed XML
|
||||
jid = parser.getAttributeValue("", "jid");
|
||||
name = parser.getAttributeValue("", "name");
|
||||
node = parser.getAttributeValue("", "node");
|
||||
action = parser.getAttributeValue("", "action");
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG && "item".equals(parser.getName())) {
|
||||
// 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);
|
||||
}
|
||||
else if (eventType == XmlPullParser.END_TAG && "query".equals(parser.getName())) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
return discoverItems;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p class="subheader">Extension Providers</p>
|
||||
|
||||
Packet extension providers are responsible for parsing packet extensions, which are
|
||||
child elements in a custom namespace of IQ, message and presence packets.
|
||||
<p>
|
||||
<u>Pubsub Subscription Packet</u>
|
||||
|
||||
<pre>
|
||||
<?xml version="1.0"?>
|
||||
<smackProviders>
|
||||
<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>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<u>Subscription PacketExtensionProvider Implementation</u>
|
||||
|
||||
<pre>
|
||||
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);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p class="subheader">Provider file format</p>
|
||||
|
||||
This is the format for a provider file which can be parsed by the <b>ProviderFileLoader</b>.
|
||||
<pre>
|
||||
<?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>x</elementName>
|
||||
<namespace>jabber:iq:event</namespace>
|
||||
<className>org.jivesoftware.smack.packet.MessageEvent</className>
|
||||
<elementName>subscription</elementName>
|
||||
<namespace>http://jabber.org/protocol/pubsub</namespace>
|
||||
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
|
||||
</extensionProvider>
|
||||
</smackProviders></pre>
|
||||
</smackProviders></pre>
|
||||
|
||||
If multiple provider entries attempt to register to handle the same element
|
||||
name and namespace, the first entry loaded from the classpath will take
|
||||
precedence.<p>
|
||||
Each provider is associated with an element name and a namespace. If multiple provider entries attempt to register to
|
||||
handle the same namespace, the last entry added to the <b>ProviderManager</b> will overwrite any other that was loaded
|
||||
before it.
|
||||
<p>
|
||||
|
||||
Whenever a packet extension is found in a packet, parsing will
|
||||
be passed to the correct provider. Each provider can either implement the
|
||||
PacketExtensionProvider interface or be a standard Java Bean. In the
|
||||
former case, each extension provider is responsible for parsing the raw
|
||||
XML stream to contruct an object. In the latter case, bean introspection
|
||||
is used to try to automatically set the properties of the class using
|
||||
the values in the packet extension sub-element.<p>
|
||||
|
||||
When an extension provider is not registered for an element name and
|
||||
namespace combination, Smack will store all top-level elements of the
|
||||
sub-packet in DefaultPacketExtension object and then attach it to the packet.
|
||||
|
||||
|
||||
<br clear="all" /><br><br>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue