1
0
Fork 0
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:
rcollier 2014-01-16 05:14:39 +00:00
parent 7e3d4186bb
commit f155cb4d07
25 changed files with 1709 additions and 1000 deletions

View file

@ -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>

View file

@ -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>
&lt;?xml version="1.0"?&gt;
&lt;smackProviders&gt;
&lt;iqProvider&gt;
&lt;elementName&gt;query&lt;/elementName&gt;
&lt;namespace&gt;jabber:iq:time&lt;/namespace&gt;
&lt;className&gt;org.jivesoftware.smack.packet.Time&lt/className&gt;
&lt;/iqProvider&gt;
&lt;/smackProviders&gt;</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>
&lt;iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'&gt;
&lt;query xmlns='jabber:iq:time'&gt;
&lt;utc&gt;20020910T17:58:35&lt;/utc&gt;
&lt;tz&gt;MDT&lt;/tz&gt;
&lt;display&gt;Tue Sep 10 12:58:35 2002&lt;/display&gt;
&lt;/query&gt;
&lt;/iq&gt;</pre>
&lt;iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'&gt;
&lt;query xmlns='jabber:iq:time'&gt;
&lt;utc&gt;20020910T17:58:35&lt;/utc&gt;
&lt;tz&gt;MDT&lt;/tz&gt;
&lt;display&gt;Tue Sep 10 12:58:35 2002&lt;/display&gt;
&lt;/query&gt;
&lt;/iq&gt;
</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>
&lt;iq type='result' from='shakespeare.lit' to='romeo@montague.net/orchard' id='items1'&gt;
&lt;query xmlns='http://jabber.org/protocol/disco#items'&gt;
&lt;item jid='people.shakespeare.lit' name='Directory of Characters'/&gt;
&lt;item jid='plays.shakespeare.lit' name='Play-Specific Chatrooms'/&gt;
&lt;item jid='mim.shakespeare.lit' name='Gateway to Marlowe IM'/&gt;
&lt;item jid='words.shakespeare.lit' name='Shakespearean Lexicon'/&gt;
&lt;item jid='globe.shakespeare.lit' name='Calendar of Performances'/&gt;
&lt;item jid='headlines.shakespeare.lit' name='Latest Shakespearean News'/&gt;
&lt;item jid='catalog.shakespeare.lit' name='Buy Shakespeare Stuff!'/&gt;
&lt;item jid='en2fr.shakespeare.lit' name='French Translation Service'/&gt;
&lt;/query&gt;
&lt;/iq&gt;
</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>
&lt;?xml version="1.0"?&gt;
&lt;smackProviders&gt;
&lt;iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'&gt;
&lt;pubsub xmlns='http://jabber.org/protocol/pubsub'&gt;
&lt;subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'&gt;
&lt;subscribe-options&gt;
&lt;required/&gt;
&lt;/subscribe-options&gt;
&lt;/subscription&gt;
&lt;/pubsub&gt;
&lt;/iq&gt;
</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>
&lt;?xml version="1.0"?&gt;
&lt;smackProviders&gt;
&lt;iqProvider&gt;
&lt;elementName&gt;query&lt;/elementName&gt;
&lt;namespace&gt;jabber:iq:time&lt;/namespace&gt;
&lt;className&gt;org.jivesoftware.smack.packet.Time&lt/className&gt;
&lt;/iqProvider&gt;
&lt;iqProvider&gt;
&lt;elementName&gt;query&lt;/elementName&gt;
&lt;namespace&gt;http://jabber.org/protocol/disco#items&lt;/namespace&gt;
&lt;className&gt;org.jivesoftware.smackx.provider.DiscoverItemsProvider&lt/className&gt;
&lt;/iqProvider&gt;
&lt;extensionProvider&gt;
&lt;elementName&gt;x&lt;/elementName&gt;
&lt;namespace&gt;jabber:iq:event&lt;/namespace&gt;
&lt;className&gt;org.jivesoftware.smack.packet.MessageEvent&lt/className&gt;
&lt;elementName&gt;subscription&lt;/elementName&gt;
&lt;namespace&gt;http://jabber.org/protocol/pubsub&lt;/namespace&gt;
&lt;className&gt;org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider&lt/className&gt;
&lt;/extensionProvider&gt;
&lt;/smackProviders&gt;</pre>
&lt;/smackProviders&gt;</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>