mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-25 15:52:06 +01: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
|
@ -170,54 +170,63 @@
|
|||
<!-- jar -->
|
||||
<!-- ======================================================================================= -->
|
||||
<target name="jar" depends="compile" unless="jar.uptodate" description="Produces smack.jar">
|
||||
<copy todir="${compile.dir}/META-INF" file="${basedir}/build/resources/META-INF/jul.properties" />
|
||||
<copy todir="${compile.dir}/META-INF" file="${basedir}/build/resources/META-INF/smack-config.xml" />
|
||||
<jar destfile="${jar.dest.dir}/smack.jar"
|
||||
<copy todir="${compile.dir}/META-INF" file="${basedir}/build/resources/META-INF/core.providers" />
|
||||
<property name="smack.jar.name" value="${jar.dest.dir}/smack.jar" />
|
||||
<jar destfile="${smack.jar.name}"
|
||||
basedir="${compile.dir}"
|
||||
includes="org/jivesoftware/smack/**/*.class, **/smack-config.xml">
|
||||
includes="org/jivesoftware/smack/**/*.class, **/smack-config.xml, **/core.providers, **/jul.properties">
|
||||
<zipfileset src="${merge.lib.dir}/xpp.jar"/>
|
||||
</jar>
|
||||
<taskdef resource="aQute/bnd/ant/taskdef.properties" classpath="${basedir}/build/build/biz.aQute.bnd.jar"/>
|
||||
<bndwrap jars="${jar.dest.dir}/smack.jar" output="${jar.dest.dir}/smack.jar" />
|
||||
<jar file="${jar.dest.dir}/smack.jar" update="true">
|
||||
<bndwrap jars="${smack.jar.name}" output="${smack.jar.name}" />
|
||||
<jar file="${smack.jar.name}" update="true">
|
||||
<manifest>
|
||||
<attribute name="Bundle-SymbolicName" value="org.igniterealtime.smack" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<copy todir="${compile.dir}/META-INF" file="${basedir}/build/resources/META-INF/smack.providers" />
|
||||
<jar destfile="${jar.dest.dir}/smackx.jar"
|
||||
|
||||
<property name="smackx.jar.name" value="${jar.dest.dir}/smackx.jar" />
|
||||
<copy todir="${compile.dir}/META-INF" file="${basedir}/build/resources/META-INF/extension.providers" />
|
||||
<jar destfile="${smackx.jar.name}"
|
||||
basedir="${compile.dir}"
|
||||
includes="org/jivesoftware/smackx/**/*.class, **/*.providers"
|
||||
includes="org/jivesoftware/smackx/**/*.class, **/extension.providers"
|
||||
excludes="org/jivesoftware/smackx/debugger/*.class">
|
||||
<manifest>
|
||||
<attribute name="Class-Path" value="smack.jar" />
|
||||
</manifest>
|
||||
<zipfileset src="${merge.lib.dir}/jzlib.jar"/>
|
||||
</jar>
|
||||
<bndwrap jars="${jar.dest.dir}/smackx.jar" output="${jar.dest.dir}/smackx.jar" />
|
||||
<jar file="${jar.dest.dir}/smackx.jar" update="true">
|
||||
<bndwrap jars="${smackx.jar.name}" output="${smackx.jar.name}" />
|
||||
<jar file="${smackx.jar.name}" update="true">
|
||||
<manifest>
|
||||
<attribute name="Bundle-SymbolicName" value="org.igniterealtime.smack-ext" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<copy todir="${compile.dir}/images">
|
||||
|
||||
<property name="debug.jar.name" value="${jar.dest.dir}/smack-debug.jar" />
|
||||
<copy todir="${compile.dir}/images">
|
||||
<fileset dir="${basedir}/build/resources/images">
|
||||
<include name="*.png"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<jar destfile="${jar.dest.dir}/smackx-debug.jar"
|
||||
<jar destfile="${debug.jar.name}"
|
||||
basedir="${compile.dir}"
|
||||
includes="org/jivesoftware/smackx/debugger/*.class, **/*.png">
|
||||
<manifest>
|
||||
<attribute name="Class-Path" value="smack.jar" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<bndwrap jars="${jar.dest.dir}/smackx-debug.jar" output="${jar.dest.dir}/smackx-debug.jar" />
|
||||
<jar file="${jar.dest.dir}/smackx-debug.jar" update="true">
|
||||
<bndwrap jars="${debug.jar.name}" output="${debug.jar.name}" />
|
||||
<jar file="${debug.jar.name}" update="true">
|
||||
<manifest>
|
||||
<attribute name="Bundle-SymbolicName" value="org.igniterealtime.smack-ext-debug" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<jar destfile="${jar.dest.dir}/smackx-jingle.jar"
|
||||
|
||||
<property name="jingle.jar.name" value="${jar.dest.dir}/smack-jingle.jar" />
|
||||
<jar destfile="${jingle.jar.name}"
|
||||
basedir="${compile.dir}/jingle/extension"
|
||||
includes="org/jivesoftware/smackx/**/*.class">
|
||||
<manifest>
|
||||
|
@ -225,14 +234,14 @@
|
|||
</manifest>
|
||||
<zipfileset src="${jingle.extension.merge.lib.dir}/jstun.jar"/>
|
||||
</jar>
|
||||
<bndwrap jars="${jar.dest.dir}/smackx-jingle.jar" output="${jar.dest.dir}/smackx-jingle.jar" />
|
||||
<jar file="${jar.dest.dir}/smackx-jingle.jar" update="true">
|
||||
<bndwrap jars="${jingle.jar.name}" output="${jingle.jar.name}" />
|
||||
<jar file="${jingle.jar.name}" update="true">
|
||||
<manifest>
|
||||
<attribute name="Bundle-SymbolicName" value="org.igniterealtime.smack-ext-jingle" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<delete file="${compile.dir}/META-INF/smack-config.xml" />
|
||||
<delete file="${compile.dir}/META-INF/smack.providers" />
|
||||
<delete file="${compile.dir}/META-INF/*.providers" />
|
||||
<delete>
|
||||
<fileset dir="${compile.dir}/images">
|
||||
<include name="*.png"/>
|
||||
|
|
|
@ -43,10 +43,14 @@
|
|||
</else>
|
||||
</if>
|
||||
<property name="release.dir" value="${basedir}/target/release/${release.name}" />
|
||||
<property name="release.samples.dir" value="${release.dir}/samples" />
|
||||
<property name="release-dev.dir" value="${basedir}/target/release/${release-dev.name}" />
|
||||
<property name="release-dev.samples.dir" value="${release-dev.name}/samples" />
|
||||
<!-- create release dirs -->
|
||||
<mkdir dir="${release.dir}" />
|
||||
<mkdir dir="${release.samples.dir}" />
|
||||
<mkdir dir="${release-dev.dir}" />
|
||||
<mkdir dir="${release-dev.dir.samples}/samples" />
|
||||
<!-- Copy smack.jar -->
|
||||
<copy todir="${release.dir}">
|
||||
<fileset dir="${jar.dest.dir}" includes="smack.jar" />
|
||||
|
@ -54,12 +58,20 @@
|
|||
<fileset dir="${jar.dest.dir}" includes="smackx-debug.jar" />
|
||||
<fileset dir="${jar.dest.dir}" includes="smackx-jingle.jar" />
|
||||
</copy>
|
||||
<copy todir="${release.samples.dir}">
|
||||
<fileset dir="${basedir}/build/resources/META-INF" includes="sample.providers" />
|
||||
<fileset dir="${basedir}/build/resources/META-INF" includes="smack-config.xml" />
|
||||
</copy>
|
||||
<copy todir="${release-dev.dir}">
|
||||
<fileset dir="${jar.dest.dir}" includes="smack.jar" />
|
||||
<fileset dir="${jar.dest.dir}" includes="smackx.jar" />
|
||||
<fileset dir="${jar.dest.dir}" includes="smackx-debug.jar" />
|
||||
<fileset dir="${jar.dest.dir}" includes="smackx-jingle.jar" />
|
||||
</copy>
|
||||
<copy todir="${release-dev.samples.dir}">
|
||||
<fileset dir="${basedir}/build/resources/META-INF" includes="sample.providers" />
|
||||
<fileset dir="${basedir}/build/resources/META-INF" includes="smack-config" />
|
||||
</copy>
|
||||
<!-- Copy build dir -->
|
||||
<copy todir="${release-dev.dir}/build">
|
||||
<fileset dir="${basedir}/build">
|
||||
|
@ -110,7 +122,10 @@
|
|||
</copy>
|
||||
<!-- Copy resources -->
|
||||
<copy todir="${release-dev.dir}/build/resources">
|
||||
<fileset dir="${basedir}/build/resources" includes="META-INF/smack.providers" />
|
||||
<fileset dir="${basedir}/build/resources" includes="META-INF/core.providers" />
|
||||
<fileset dir="${basedir}/build/resources" includes="META-INF/extension.providers" />
|
||||
<fileset dir="${basedir}/build/resources" includes="META-INF/sample.providers" />
|
||||
<fileset dir="${basedir}/build/resources" includes="META-INF/jul.properties" />
|
||||
<fileset dir="${basedir}/build/resources" includes="META-INF/smack-config.xml" />
|
||||
</copy>
|
||||
<copy todir="${release-dev.dir}/build/resources/images">
|
||||
|
|
19
build/resources/META-INF/core.providers
Normal file
19
build/resources/META-INF/core.providers
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Providers file for default Smack extensions -->
|
||||
<smackProviders>
|
||||
|
||||
<!-- Privacy -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:privacy</namespace>
|
||||
<className>org.jivesoftware.smack.provider.PrivacyProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Ping (XEP-199) Manager -->
|
||||
<iqProvider>
|
||||
<elementName>ping</elementName>
|
||||
<namespace>urn:xmpp:ping</namespace>
|
||||
<className>org.jivesoftware.smack.ping.provider.PingProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
</smackProviders>
|
|
@ -223,13 +223,6 @@
|
|||
<className>org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Privacy -->
|
||||
<iqProvider>
|
||||
<elementName>query</elementName>
|
||||
<namespace>jabber:iq:privacy</namespace>
|
||||
<className>org.jivesoftware.smack.provider.PrivacyProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<!-- Ad-Hoc Command -->
|
||||
<iqProvider>
|
||||
<elementName>command</elementName>
|
||||
|
@ -653,11 +646,4 @@
|
|||
<className>org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
<!-- Ping (XEP-199) Manager -->
|
||||
<iqProvider>
|
||||
<elementName>ping</elementName>
|
||||
<namespace>urn:xmpp:ping</namespace>
|
||||
<className>org.jivesoftware.smack.ping.provider.PingProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
</smackProviders>
|
3
build/resources/META-INF/jul.properties
Normal file
3
build/resources/META-INF/jul.properties
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Java Util Logging configuration for Smack.
|
||||
handlers = java.util.logging.ConsoleHandler
|
||||
.level = WARNING
|
17
build/resources/META-INF/sample.providers
Normal file
17
build/resources/META-INF/sample.providers
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Sample providers file -->
|
||||
<smackProviders>
|
||||
|
||||
<iqProvider>
|
||||
<elementName>element</elementName>
|
||||
<namespace>ns</namespace>
|
||||
<className>com.myco.MyIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
<extensionProvider>
|
||||
<elementName>elem</elementName>
|
||||
<namespace>http://jabber.org/protocol/whoknows</namespace>
|
||||
<className>com.myco.MyExtProvider</className>
|
||||
</extensionProvider>
|
||||
|
||||
</smackProviders>
|
|
@ -22,9 +22,13 @@
|
|||
|
||||
<!-- Classes that will be loaded when Smack starts -->
|
||||
<startupClasses>
|
||||
<className>org.jivesoftware.smackx.ServiceDiscoveryManager</className>
|
||||
<className>org.jivesoftware.smack.LoggingInitializer</className>
|
||||
<className>org.jivesoftware.smack.provider.CoreInitializer</className>
|
||||
<className>org.jivesoftware.smack.provider.VmArgInitializer</className>
|
||||
<className>org.jivesoftware.smack.PrivacyListManager</className>
|
||||
<className>org.jivesoftware.smack.keepalive.KeepAliveManager</className>
|
||||
<className>org.jivesoftware.smackx.provider.ExtensionInitializer</className>
|
||||
<className>org.jivesoftware.smackx.ServiceDiscoveryManager</className>
|
||||
<className>org.jivesoftware.smackx.XHTMLManager</className>
|
||||
<className>org.jivesoftware.smackx.muc.MultiUserChat</className>
|
||||
<className>org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager</className>
|
||||
|
|
|
@ -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>
|
||||
|
|
28
source/org/jivesoftware/smack/LoggingInitializer.java
Normal file
28
source/org/jivesoftware/smack/LoggingInitializer.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package org.jivesoftware.smack;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.util.FileUtils;
|
||||
|
||||
/**
|
||||
* Initializes the Java logging system.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public class LoggingInitializer implements SmackInitializer {
|
||||
|
||||
private static Logger log = Logger.getLogger(LoggingInitializer.class.getName());
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
try {
|
||||
LogManager.getLogManager().readConfiguration(FileUtils.getStreamForUrl("classpath:META-INF/jul.properties", null));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log .log(Level.WARNING, "Could not initialize Java Logging from default file.", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,16 +20,18 @@
|
|||
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
|
||||
import org.jivesoftware.smack.parsing.ExceptionThrowingCallback;
|
||||
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
|
||||
import org.jivesoftware.smack.util.FileUtils;
|
||||
import org.xmlpull.mxp1.MXParser;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
|
@ -49,17 +51,23 @@ import org.xmlpull.v1.XmlPullParser;
|
|||
* @author Gaston Dombiak
|
||||
*/
|
||||
public final class SmackConfiguration {
|
||||
|
||||
private static final String SMACK_VERSION = "3.3.1";
|
||||
private static final String DEFAULT_CONFIG_FILE = "classpath:META-INF/smack-config.xml";
|
||||
|
||||
private static final Logger log = Logger.getLogger(SmackConfiguration.class.getName());
|
||||
|
||||
private static InputStream configFileStream;
|
||||
|
||||
private static int packetReplyTimeout = 5000;
|
||||
private static int keepAliveInterval = 30000;
|
||||
private static Vector<String> defaultMechs = new Vector<String>();
|
||||
private static List<String> defaultMechs = new ArrayList<String>();
|
||||
|
||||
private static boolean localSocks5ProxyEnabled = true;
|
||||
private static int localSocks5ProxyPort = 7777;
|
||||
private static int packetCollectorSize = 5000;
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
/**
|
||||
* The default parsing exception callback is {@link ExceptionThrowingCallback} which will
|
||||
* throw an exception and therefore disconnect the active connection.
|
||||
|
@ -81,70 +89,37 @@ public final class SmackConfiguration {
|
|||
* 1) a set of classes will be loaded in order to execute their static init block
|
||||
* 2) retrieve and set the current Smack release
|
||||
*/
|
||||
static {
|
||||
|
||||
/**
|
||||
* Sets the location of the config file on the classpath. Only required if changing from the default location of <i>classpath:META-INF/smack-config.xml</i>.
|
||||
*
|
||||
* <p>
|
||||
* This method must be called before accessing any other class in Smack.
|
||||
*
|
||||
* @param configFileUrl The location of the config file.
|
||||
* @param loader The classloader to use if the URL has a protocol of <b>classpath</> and the file is not located on the default classpath.
|
||||
* This can be set to null to use defaults and is ignored for all other protocols.
|
||||
* @throws IllegalArgumentException If the config URL is invalid in that it cannot open an {@link InputStream}
|
||||
*/
|
||||
public static void setConfigFileUrl(String configFileUrl, ClassLoader loader) {
|
||||
try {
|
||||
// Get an array of class loaders to try loading the providers files from.
|
||||
ClassLoader[] classLoaders = getClassLoaders();
|
||||
for (ClassLoader classLoader : classLoaders) {
|
||||
Enumeration<URL> configEnum = classLoader.getResources("META-INF/smack-config.xml");
|
||||
while (configEnum.hasMoreElements()) {
|
||||
URL url = configEnum.nextElement();
|
||||
InputStream systemStream = null;
|
||||
try {
|
||||
systemStream = url.openStream();
|
||||
XmlPullParser parser = new MXParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(systemStream, "UTF-8");
|
||||
int eventType = parser.getEventType();
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("className")) {
|
||||
// Attempt to load the class so that the class can get initialized
|
||||
parseClassToLoad(parser);
|
||||
}
|
||||
else if (parser.getName().equals("packetReplyTimeout")) {
|
||||
packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout);
|
||||
}
|
||||
else if (parser.getName().equals("keepAliveInterval")) {
|
||||
keepAliveInterval = parseIntProperty(parser, keepAliveInterval);
|
||||
}
|
||||
else if (parser.getName().equals("mechName")) {
|
||||
defaultMechs.add(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("localSocks5ProxyEnabled")) {
|
||||
localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("localSocks5ProxyPort")) {
|
||||
localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort);
|
||||
}
|
||||
else if (parser.getName().equals("packetCollectorSize")) {
|
||||
packetCollectorSize = parseIntProperty(parser, packetCollectorSize);
|
||||
}
|
||||
else if (parser.getName().equals("autoEnableEntityCaps")) {
|
||||
autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText());
|
||||
}
|
||||
}
|
||||
eventType = parser.next();
|
||||
}
|
||||
while (eventType != XmlPullParser.END_DOCUMENT);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
systemStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
configFileStream = FileUtils.getStreamForUrl(configFileUrl, loader);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new IllegalArgumentException("Failed to create input stream from specified file URL ["+ configFileUrl + "]", e);
|
||||
}
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link InputStream} representing the smack configuration file. This can be used to override the default with something that is not on the classpath.
|
||||
* <p>
|
||||
* This method must be called before accessing any other class in Smack.
|
||||
* @param configFile
|
||||
*/
|
||||
public static void setConfigFileStream(InputStream configFile) {
|
||||
configFileStream = configFile;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,6 +138,8 @@ public final class SmackConfiguration {
|
|||
* @return the milliseconds to wait for a response from the server
|
||||
*/
|
||||
public static int getPacketReplyTimeout() {
|
||||
initialize();
|
||||
|
||||
// The timeout value must be greater than 0 otherwise we will answer the default value
|
||||
if (packetReplyTimeout <= 0) {
|
||||
packetReplyTimeout = 5000;
|
||||
|
@ -177,6 +154,8 @@ public final class SmackConfiguration {
|
|||
* @param timeout the milliseconds to wait for a response from the server
|
||||
*/
|
||||
public static void setPacketReplyTimeout(int timeout) {
|
||||
initialize();
|
||||
|
||||
if (timeout <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
@ -192,6 +171,7 @@ public final class SmackConfiguration {
|
|||
* no keep-alive should be sent.
|
||||
*/
|
||||
public static int getKeepAliveInterval() {
|
||||
initialize();
|
||||
return keepAliveInterval;
|
||||
}
|
||||
|
||||
|
@ -204,6 +184,7 @@ public final class SmackConfiguration {
|
|||
* or -1 if no keep-alive should be sent.
|
||||
*/
|
||||
public static void setKeepAliveInterval(int interval) {
|
||||
initialize();
|
||||
keepAliveInterval = interval;
|
||||
}
|
||||
|
||||
|
@ -214,6 +195,7 @@ public final class SmackConfiguration {
|
|||
* @return The number of packets to queue before deleting older packets.
|
||||
*/
|
||||
public static int getPacketCollectorSize() {
|
||||
initialize();
|
||||
return packetCollectorSize;
|
||||
}
|
||||
|
||||
|
@ -224,6 +206,7 @@ public final class SmackConfiguration {
|
|||
* @param The number of packets to queue before deleting older packets.
|
||||
*/
|
||||
public static void setPacketCollectorSize(int collectorSize) {
|
||||
initialize();
|
||||
packetCollectorSize = collectorSize;
|
||||
}
|
||||
|
||||
|
@ -233,6 +216,8 @@ public final class SmackConfiguration {
|
|||
* @param mech the SASL mechanism to be added
|
||||
*/
|
||||
public static void addSaslMech(String mech) {
|
||||
initialize();
|
||||
|
||||
if(! defaultMechs.contains(mech) ) {
|
||||
defaultMechs.add(mech);
|
||||
}
|
||||
|
@ -244,6 +229,8 @@ public final class SmackConfiguration {
|
|||
* @param mechs the Collection of SASL mechanisms to be added
|
||||
*/
|
||||
public static void addSaslMechs(Collection<String> mechs) {
|
||||
initialize();
|
||||
|
||||
for(String mech : mechs) {
|
||||
addSaslMech(mech);
|
||||
}
|
||||
|
@ -255,9 +242,8 @@ public final class SmackConfiguration {
|
|||
* @param mech the SASL mechanism to be removed
|
||||
*/
|
||||
public static void removeSaslMech(String mech) {
|
||||
if( defaultMechs.contains(mech) ) {
|
||||
defaultMechs.remove(mech);
|
||||
}
|
||||
initialize();
|
||||
defaultMechs.remove(mech);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,9 +252,8 @@ public final class SmackConfiguration {
|
|||
* @param mechs the Collection of SASL mechanisms to be removed
|
||||
*/
|
||||
public static void removeSaslMechs(Collection<String> mechs) {
|
||||
for(String mech : mechs) {
|
||||
removeSaslMech(mech);
|
||||
}
|
||||
initialize();
|
||||
defaultMechs.removeAll(mechs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -279,7 +264,7 @@ public final class SmackConfiguration {
|
|||
* @return the list of SASL mechanisms to be used.
|
||||
*/
|
||||
public static List<String> getSaslMechs() {
|
||||
return defaultMechs;
|
||||
return Collections.unmodifiableList(defaultMechs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,6 +273,7 @@ public final class SmackConfiguration {
|
|||
* @return if the local Socks5 proxy should be started
|
||||
*/
|
||||
public static boolean isLocalSocks5ProxyEnabled() {
|
||||
initialize();
|
||||
return localSocks5ProxyEnabled;
|
||||
}
|
||||
|
||||
|
@ -297,6 +283,7 @@ public final class SmackConfiguration {
|
|||
* @param localSocks5ProxyEnabled if the local Socks5 proxy should be started
|
||||
*/
|
||||
public static void setLocalSocks5ProxyEnabled(boolean localSocks5ProxyEnabled) {
|
||||
initialize();
|
||||
SmackConfiguration.localSocks5ProxyEnabled = localSocks5ProxyEnabled;
|
||||
}
|
||||
|
||||
|
@ -306,6 +293,7 @@ public final class SmackConfiguration {
|
|||
* @return the port of the local Socks5 proxy
|
||||
*/
|
||||
public static int getLocalSocks5ProxyPort() {
|
||||
initialize();
|
||||
return localSocks5ProxyPort;
|
||||
}
|
||||
|
||||
|
@ -316,6 +304,7 @@ public final class SmackConfiguration {
|
|||
* @param localSocks5ProxyPort the port of the local Socks5 proxy to set
|
||||
*/
|
||||
public static void setLocalSocks5ProxyPort(int localSocks5ProxyPort) {
|
||||
initialize();
|
||||
SmackConfiguration.localSocks5ProxyPort = localSocks5ProxyPort;
|
||||
}
|
||||
|
||||
|
@ -324,6 +313,7 @@ public final class SmackConfiguration {
|
|||
* @return
|
||||
*/
|
||||
public static boolean autoEnableEntityCaps() {
|
||||
initialize();
|
||||
return autoEnableEntityCaps;
|
||||
}
|
||||
|
||||
|
@ -333,6 +323,7 @@ public final class SmackConfiguration {
|
|||
* @param true if Entity Caps should be auto enabled, false if not
|
||||
*/
|
||||
public static void setAutoEnableEntityCaps(boolean b) {
|
||||
initialize();
|
||||
autoEnableEntityCaps = b;
|
||||
}
|
||||
|
||||
|
@ -343,6 +334,7 @@ public final class SmackConfiguration {
|
|||
* @see ParsingExceptionCallback
|
||||
*/
|
||||
public static void setDefaultParsingExceptionCallback(ParsingExceptionCallback callback) {
|
||||
initialize();
|
||||
defaultCallback = callback;
|
||||
}
|
||||
|
||||
|
@ -353,6 +345,7 @@ public final class SmackConfiguration {
|
|||
* @see ParsingExceptionCallback
|
||||
*/
|
||||
public static ParsingExceptionCallback getDefaultParsingExceptionCallback() {
|
||||
initialize();
|
||||
return defaultCallback;
|
||||
}
|
||||
|
||||
|
@ -360,11 +353,15 @@ public final class SmackConfiguration {
|
|||
String className = parser.nextText();
|
||||
// Attempt to load the class so that the class can get initialized
|
||||
try {
|
||||
Class.forName(className);
|
||||
Class<?> initClass = Class.forName(className);
|
||||
|
||||
if (SmackInitializer.class.isAssignableFrom(initClass)) {
|
||||
SmackInitializer initializer = (SmackInitializer) initClass.newInstance();
|
||||
initializer.initialize();
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
System.err.println("Error! A startup class specified in smack-config.xml could " +
|
||||
"not be loaded: " + className);
|
||||
log.log(Level.WARNING, "A startup class [" + className + "] specified in smack-config.xml could not be loaded: ");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,27 +372,93 @@ public final class SmackConfiguration {
|
|||
return Integer.parseInt(parser.nextText());
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
nfe.printStackTrace();
|
||||
log.log(Level.SEVERE, "Could not parse integer", nfe);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of class loaders to load resources from.
|
||||
*
|
||||
* @return an array of ClassLoader instances.
|
||||
/*
|
||||
* Order of precedence for config file is VM arg, setConfigXXX methods and embedded default file location.
|
||||
*/
|
||||
private static ClassLoader[] getClassLoaders() {
|
||||
ClassLoader[] classLoaders = new ClassLoader[2];
|
||||
classLoaders[0] = SmackConfiguration.class.getClassLoader();
|
||||
classLoaders[1] = Thread.currentThread().getContextClassLoader();
|
||||
// Clean up possible null values. Note that #getClassLoader may return a null value.
|
||||
List<ClassLoader> loaders = new ArrayList<ClassLoader>();
|
||||
for (ClassLoader classLoader : classLoaders) {
|
||||
if (classLoader != null) {
|
||||
loaders.add(classLoader);
|
||||
private static void initialize() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
String configFileLocation = System.getProperty("smack.config.file");
|
||||
|
||||
if (configFileLocation != null) {
|
||||
try {
|
||||
configFileStream = FileUtils.getStreamForUrl(configFileLocation, null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.log(Level.SEVERE, "Error creating input stream for config file [" + configFileLocation + "] from VM argument", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (configFileStream == null) {
|
||||
try {
|
||||
configFileStream = FileUtils.getStreamForUrl(DEFAULT_CONFIG_FILE, null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.log(Level.INFO, "Could not create input stream for default config file [" + DEFAULT_CONFIG_FILE + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (configFileStream != null) {
|
||||
readFile(configFileStream);
|
||||
}
|
||||
else {
|
||||
log.log(Level.INFO, "No configuration file found");
|
||||
}
|
||||
}
|
||||
|
||||
private static void readFile(InputStream cfgFileStream) {
|
||||
XmlPullParser parser = new MXParser();
|
||||
try {
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(cfgFileStream, "UTF-8");
|
||||
int eventType = parser.getEventType();
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("className")) {
|
||||
// Attempt to load the class so that the class can get initialized
|
||||
parseClassToLoad(parser);
|
||||
}
|
||||
else if (parser.getName().equals("packetReplyTimeout")) {
|
||||
packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout);
|
||||
}
|
||||
else if (parser.getName().equals("keepAliveInterval")) {
|
||||
keepAliveInterval = parseIntProperty(parser, keepAliveInterval);
|
||||
}
|
||||
else if (parser.getName().equals("mechName")) {
|
||||
defaultMechs.add(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("localSocks5ProxyEnabled")) {
|
||||
localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText());
|
||||
}
|
||||
else if (parser.getName().equals("localSocks5ProxyPort")) {
|
||||
localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort);
|
||||
}
|
||||
else if (parser.getName().equals("packetCollectorSize")) {
|
||||
packetCollectorSize = parseIntProperty(parser, packetCollectorSize);
|
||||
}
|
||||
else if (parser.getName().equals("autoEnableEntityCaps")) {
|
||||
autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText());
|
||||
}
|
||||
}
|
||||
eventType = parser.next();
|
||||
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||
} catch (Exception e) {
|
||||
log.log(Level.SEVERE, "Error occurred while reading config file", e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
cfgFileStream.close();
|
||||
} catch (IOException e) {
|
||||
log.log(Level.INFO, "Error while closing config file input stream", e);
|
||||
}
|
||||
}
|
||||
return loaders.toArray(new ClassLoader[loaders.size()]);
|
||||
}
|
||||
}
|
||||
|
|
14
source/org/jivesoftware/smack/SmackInitializer.java
Normal file
14
source/org/jivesoftware/smack/SmackInitializer.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package org.jivesoftware.smack;
|
||||
|
||||
/**
|
||||
* Defines an initialization class that will be instantiated and invoked by the {@link SmackConfiguration} class during initialization.
|
||||
*
|
||||
* <p>
|
||||
* Any implementation of this class MUST have a default constructor.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public interface SmackInitializer {
|
||||
void initialize();
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
|
||||
abstract class AbstractProviderInfo {
|
||||
private String element;
|
||||
private String ns;
|
||||
private Object provider;
|
||||
|
||||
AbstractProviderInfo(String elementName, String namespace, Object iqOrExtProvider) {
|
||||
element = elementName;
|
||||
ns = namespace;
|
||||
provider = iqOrExtProvider;
|
||||
}
|
||||
|
||||
public String getElementName() {
|
||||
return element;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return ns;
|
||||
}
|
||||
|
||||
Object getProvider() {
|
||||
return provider;
|
||||
}
|
||||
}
|
15
source/org/jivesoftware/smack/provider/CoreInitializer.java
Normal file
15
source/org/jivesoftware/smack/provider/CoreInitializer.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import org.jivesoftware.smack.SmackInitializer;
|
||||
|
||||
/**
|
||||
* Loads the default provider file for the Smack core on initialization.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public class CoreInitializer extends UrlProviderFileInitializer implements SmackInitializer {
|
||||
protected String getFilePath() {
|
||||
return "classpath:META-INF/core.providers";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
/**
|
||||
* Defines the information required to register a packet extension Provider with the {@link ProviderManager} when using the
|
||||
* {@link ProviderLoader}.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public final class ExtensionProviderInfo extends AbstractProviderInfo {
|
||||
|
||||
/**
|
||||
* Defines an extension provider which implements the <code>PacketExtensionProvider</code> interface.
|
||||
*
|
||||
* @param elementName Element that provider parses.
|
||||
* @param namespace Namespace that provider parses.
|
||||
* @param extProvider The provider implementation.
|
||||
*/
|
||||
public ExtensionProviderInfo(String elementName, String namespace, PacketExtensionProvider extProvider) {
|
||||
super(elementName, namespace, extProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an extension provider which is adheres to the JavaBean spec for parsing the extension.
|
||||
*
|
||||
* @param elementName Element that provider parses.
|
||||
* @param namespace Namespace that provider parses.
|
||||
* @param beanClass The provider bean class.
|
||||
*/
|
||||
public ExtensionProviderInfo(String elementName, String namespace, Class<?> beanClass) {
|
||||
super(elementName, namespace, beanClass);
|
||||
}
|
||||
}
|
35
source/org/jivesoftware/smack/provider/IQProviderInfo.java
Normal file
35
source/org/jivesoftware/smack/provider/IQProviderInfo.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
||||
/**
|
||||
* Defines the information required to register an IQ Provider with the {@link ProviderManager} when using the
|
||||
* {@link ProviderLoader}.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public final class IQProviderInfo extends AbstractProviderInfo {
|
||||
|
||||
/**
|
||||
* Defines an IQ provider which implements the <code>IQProvider</code> interface.
|
||||
*
|
||||
* @param elementName Element that provider parses.
|
||||
* @param namespace Namespace that provider parses.
|
||||
* @param iqProvider The provider implementation.
|
||||
*/
|
||||
public IQProviderInfo(String elementName, String namespace, IQProvider iqProvider) {
|
||||
super(elementName, namespace, iqProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an IQ class which can be used as a provider via introspection.
|
||||
*
|
||||
* @param elementName Element that provider parses.
|
||||
* @param namespace Namespace that provider parses.
|
||||
* @param iqProviderClass The IQ class being parsed.
|
||||
*/
|
||||
public IQProviderInfo(String elementName, String namespace, Class<? extends IQ> iqProviderClass) {
|
||||
super(elementName, namespace, iqProviderClass);
|
||||
}
|
||||
}
|
146
source/org/jivesoftware/smack/provider/ProviderFileLoader.java
Normal file
146
source/org/jivesoftware/smack/provider/ProviderFileLoader.java
Normal file
|
@ -0,0 +1,146 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.xmlpull.mxp1.MXParser;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Loads the {@link IQProvider} and {@link PacketExtensionProvider} information from a standard provider file in preparation
|
||||
* for loading into the {@link ProviderManager}.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public class ProviderFileLoader implements ProviderLoader {
|
||||
private final static Logger log = Logger.getLogger(ProviderFileLoader.class.getName());
|
||||
|
||||
private Collection<IQProviderInfo> iqProviders;
|
||||
private Collection<ExtensionProviderInfo> extProviders;
|
||||
private InputStream providerStream;
|
||||
|
||||
public ProviderFileLoader(InputStream providerFileInputStream) {
|
||||
setInputStream(providerFileInputStream);
|
||||
}
|
||||
|
||||
public ProviderFileLoader() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IQProviderInfo> getIQProviderInfo() {
|
||||
initialize();
|
||||
return iqProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ExtensionProviderInfo> getExtensionProviderInfo() {
|
||||
initialize();
|
||||
return extProviders;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized void initialize() {
|
||||
// Check to see if already initialized
|
||||
if (iqProviders != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (providerStream == null) {
|
||||
throw new IllegalArgumentException("No input stream set for loader");
|
||||
}
|
||||
iqProviders = new ArrayList<IQProviderInfo>();
|
||||
extProviders = new ArrayList<ExtensionProviderInfo>();
|
||||
|
||||
// Load processing providers.
|
||||
try {
|
||||
XmlPullParser parser = new MXParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(providerStream, "UTF-8");
|
||||
int eventType = parser.getEventType();
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
String typeName = parser.getName();
|
||||
|
||||
try {
|
||||
if (!"smackProviders".equals(typeName)) {
|
||||
parser.next();
|
||||
parser.next();
|
||||
String elementName = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String namespace = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String className = parser.nextText();
|
||||
|
||||
try {
|
||||
// Attempt to load the provider class and then create
|
||||
// a new instance if it's an IQProvider. Otherwise, if it's
|
||||
// an IQ class, add the class object itself, then we'll use
|
||||
// reflection later to create instances of the class.
|
||||
if ("iqProvider".equals(typeName)) {
|
||||
// Add the provider to the map.
|
||||
Class<?> provider = Class.forName(className);
|
||||
|
||||
if (IQProvider.class.isAssignableFrom(provider)) {
|
||||
iqProviders.add(new IQProviderInfo(elementName, namespace, (IQProvider) provider.newInstance()));
|
||||
}
|
||||
else if (IQ.class.isAssignableFrom(provider)) {
|
||||
iqProviders.add(new IQProviderInfo(elementName, namespace, (Class<? extends IQ>)provider));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Attempt to load the provider class and then create
|
||||
// a new instance if it's an ExtensionProvider. Otherwise, if it's
|
||||
// a PacketExtension, add the class object itself and
|
||||
// then we'll use reflection later to create instances
|
||||
// of the class.
|
||||
Class<?> provider = Class.forName(className);
|
||||
if (PacketExtensionProvider.class.isAssignableFrom(provider)) {
|
||||
extProviders.add(new ExtensionProviderInfo(elementName, namespace, (PacketExtensionProvider) provider.newInstance()));
|
||||
}
|
||||
else if (PacketExtension.class.isAssignableFrom(provider)) {
|
||||
extProviders.add(new ExtensionProviderInfo(elementName, namespace, provider));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
log.log(Level.SEVERE, "Could not find provider class", cnfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException illExc) {
|
||||
log.log(Level.SEVERE, "Invalid provider type found [" + typeName + "] when expecting iqProvider or extensionProvider", illExc);
|
||||
}
|
||||
}
|
||||
eventType = parser.next();
|
||||
}
|
||||
while (eventType != XmlPullParser.END_DOCUMENT);
|
||||
}
|
||||
catch (Exception e){
|
||||
log.log(Level.SEVERE, "Unknown error occurred while parsing provider file", e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
providerStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setInputStream(InputStream providerFileInput) {
|
||||
if (providerFileInput == null) {
|
||||
throw new IllegalArgumentException("InputStream cannot be null");
|
||||
}
|
||||
providerStream = providerFileInput;
|
||||
initialize();
|
||||
}
|
||||
}
|
23
source/org/jivesoftware/smack/provider/ProviderLoader.java
Normal file
23
source/org/jivesoftware/smack/provider/ProviderLoader.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Used to load providers into the {@link ProviderManager}.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*/
|
||||
public interface ProviderLoader {
|
||||
|
||||
/**
|
||||
* Provides the IQ provider info for the creation of IQ providers to be added to the <code>ProviderManager</code>.
|
||||
* @return The IQ provider info to load.
|
||||
*/
|
||||
Collection<IQProviderInfo> getIQProviderInfo();
|
||||
|
||||
/**
|
||||
* Provides the extension providers for the creation of extension providers to be added to the <code>ProviderManager</code>.
|
||||
* @return The extension provider info to load.
|
||||
*/
|
||||
Collection<ExtensionProviderInfo> getExtensionProviderInfo();
|
||||
}
|
|
@ -20,16 +20,13 @@
|
|||
|
||||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.packet.PacketExtension;
|
||||
import org.xmlpull.mxp1.MXParser;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
|
||||
/**
|
||||
* Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of
|
||||
* providers exist:<ul>
|
||||
|
@ -102,20 +99,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
* 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. When an
|
||||
* set the properties of th class using the values in the packet extension sub-element. 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.<p>
|
||||
*
|
||||
* It is possible to provide a custom provider manager instead of the default implementation
|
||||
* provided by Smack. If you want to provide your own provider manager then you need to do it
|
||||
* before creating any {@link org.jivesoftware.smack.Connection} by sending the static
|
||||
* {@link #setInstance(ProviderManager)} message. Trying to change the provider manager after
|
||||
* an Connection was created will result in an {@link IllegalStateException} error.
|
||||
*
|
||||
* @author Matt Tucker
|
||||
*/
|
||||
public class ProviderManager {
|
||||
public final class ProviderManager {
|
||||
|
||||
private static ProviderManager instance;
|
||||
|
||||
|
@ -123,9 +114,7 @@ public class ProviderManager {
|
|||
private Map<String, Object> iqProviders = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* Returns the only ProviderManager valid instance. Use {@link #setInstance(ProviderManager)}
|
||||
* to configure your own provider manager. If non was provided then an instance of this
|
||||
* class will be used.
|
||||
* Returns the ProviderManager instance.
|
||||
*
|
||||
* @return the only ProviderManager valid instance.
|
||||
*/
|
||||
|
@ -136,127 +125,25 @@ public class ProviderManager {
|
|||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the only ProviderManager valid instance to be used by all Connections. If you
|
||||
* want to provide your own provider manager then you need to do it before creating
|
||||
* any Connection. Otherwise an IllegalStateException will be thrown.
|
||||
*
|
||||
* @param providerManager the only ProviderManager valid instance to be used.
|
||||
* @throws IllegalStateException if a provider manager was already configued.
|
||||
*/
|
||||
public static synchronized void setInstance(ProviderManager providerManager) {
|
||||
if (instance != null) {
|
||||
throw new IllegalStateException("ProviderManager singleton already set");
|
||||
}
|
||||
instance = providerManager;
|
||||
private ProviderManager() {
|
||||
super();
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
// Load IQ processing providers.
|
||||
try {
|
||||
// Get an array of class loaders to try loading the providers files from.
|
||||
ClassLoader[] classLoaders = getClassLoaders();
|
||||
for (ClassLoader classLoader : classLoaders) {
|
||||
Enumeration<URL> providerEnum = classLoader.getResources(
|
||||
"META-INF/smack.providers");
|
||||
while (providerEnum.hasMoreElements()) {
|
||||
URL url = providerEnum.nextElement();
|
||||
InputStream providerStream = null;
|
||||
try {
|
||||
providerStream = url.openStream();
|
||||
XmlPullParser parser = new MXParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(providerStream, "UTF-8");
|
||||
int eventType = parser.getEventType();
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
if (parser.getName().equals("iqProvider")) {
|
||||
parser.next();
|
||||
parser.next();
|
||||
String elementName = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String namespace = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String className = parser.nextText();
|
||||
// Only add the provider for the namespace if one isn't
|
||||
// already registered.
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
if (!iqProviders.containsKey(key)) {
|
||||
// Attempt to load the provider class and then create
|
||||
// a new instance if it's an IQProvider. Otherwise, if it's
|
||||
// an IQ class, add the class object itself, then we'll use
|
||||
// reflection later to create instances of the class.
|
||||
try {
|
||||
// Add the provider to the map.
|
||||
Class<?> provider = Class.forName(className);
|
||||
if (IQProvider.class.isAssignableFrom(provider)) {
|
||||
iqProviders.put(key, provider.newInstance());
|
||||
}
|
||||
else if (IQ.class.isAssignableFrom(provider)) {
|
||||
iqProviders.put(key, provider);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
cnfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parser.getName().equals("extensionProvider")) {
|
||||
parser.next();
|
||||
parser.next();
|
||||
String elementName = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String namespace = parser.nextText();
|
||||
parser.next();
|
||||
parser.next();
|
||||
String className = parser.nextText();
|
||||
// Only add the provider for the namespace if one isn't
|
||||
// already registered.
|
||||
String key = getProviderKey(elementName, namespace);
|
||||
if (!extensionProviders.containsKey(key)) {
|
||||
// Attempt to load the provider class and then create
|
||||
// a new instance if it's a Provider. Otherwise, if it's
|
||||
// a PacketExtension, add the class object itself and
|
||||
// then we'll use reflection later to create instances
|
||||
// of the class.
|
||||
try {
|
||||
// Add the provider to the map.
|
||||
Class<?> provider = Class.forName(className);
|
||||
if (PacketExtensionProvider.class.isAssignableFrom(
|
||||
provider)) {
|
||||
extensionProviders.put(key, provider.newInstance());
|
||||
}
|
||||
else if (PacketExtension.class.isAssignableFrom(
|
||||
provider)) {
|
||||
extensionProviders.put(key, provider);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
cnfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eventType = parser.next();
|
||||
}
|
||||
while (eventType != XmlPullParser.END_DOCUMENT);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
providerStream.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
public void addLoader(ProviderLoader loader) {
|
||||
if (loader == null) {
|
||||
throw new IllegalArgumentException("loader cannot be null");
|
||||
}
|
||||
|
||||
if (loader.getIQProviderInfo() != null) {
|
||||
for (IQProviderInfo info : loader.getIQProviderInfo()) {
|
||||
iqProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (loader.getExtensionProviderInfo() != null) {
|
||||
for (ExtensionProviderInfo info : loader.getExtensionProviderInfo()) {
|
||||
extensionProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,28 +298,4 @@ public class ProviderManager {
|
|||
buf.append("<").append(elementName).append("/><").append(namespace).append("/>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of class loaders to load resources from.
|
||||
*
|
||||
* @return an array of ClassLoader instances.
|
||||
*/
|
||||
private ClassLoader[] getClassLoaders() {
|
||||
ClassLoader[] classLoaders = new ClassLoader[2];
|
||||
classLoaders[0] = ProviderManager.class.getClassLoader();
|
||||
classLoaders[1] = Thread.currentThread().getContextClassLoader();
|
||||
// Clean up possible null values. Note that #getClassLoader may return a null value.
|
||||
List<ClassLoader> loaders = new ArrayList<ClassLoader>();
|
||||
for (ClassLoader classLoader : classLoaders) {
|
||||
if (classLoader != null) {
|
||||
loaders.add(classLoader);
|
||||
}
|
||||
}
|
||||
return loaders.toArray(new ClassLoader[loaders.size()]);
|
||||
}
|
||||
|
||||
private ProviderManager() {
|
||||
super();
|
||||
initialize();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.SmackInitializer;
|
||||
import org.jivesoftware.smack.util.FileUtils;
|
||||
|
||||
/**
|
||||
* Loads the provider file defined by the URL returned by {@link #getFilePath()}. This file will be loaded on Smack initialization.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public abstract class UrlProviderFileInitializer implements SmackInitializer {
|
||||
private static final Logger log = Logger.getLogger(UrlProviderFileInitializer.class.getName());
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
String filePath = getFilePath();
|
||||
|
||||
try {
|
||||
InputStream is = FileUtils.getStreamForUrl(filePath, getClassLoader());
|
||||
|
||||
if (is != null) {
|
||||
log.log(Level.INFO, "Loading providers for file [" + filePath + "]");
|
||||
ProviderManager.getInstance().addLoader(new ProviderFileLoader(is));
|
||||
}
|
||||
else {
|
||||
log.log(Level.WARNING, "No input stream created for " + filePath);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.log(Level.SEVERE, "Error trying to load provider file " + filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String getFilePath();
|
||||
|
||||
/**
|
||||
* Returns an array of class loaders to load resources from.
|
||||
*
|
||||
* @return an array of ClassLoader instances.
|
||||
*/
|
||||
protected ClassLoader getClassLoader() {
|
||||
return null;
|
||||
}
|
||||
}
|
23
source/org/jivesoftware/smack/provider/VmArgInitializer.java
Normal file
23
source/org/jivesoftware/smack/provider/VmArgInitializer.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package org.jivesoftware.smack.provider;
|
||||
|
||||
|
||||
/**
|
||||
* Looks for a provider file location based on the VM argument <i>smack.provider.file</>. If it is supplied, its value will
|
||||
* be used as a file location for a providers file and loaded into the {@link ProviderManager} on Smack initialization.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public class VmArgInitializer extends UrlProviderFileInitializer {
|
||||
|
||||
protected String getFilePath() {
|
||||
return System.getProperty("smack.provider.file");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (getFilePath() != null) {
|
||||
super.initialize();
|
||||
}
|
||||
}
|
||||
}
|
59
source/org/jivesoftware/smack/util/FileUtils.java
Normal file
59
source/org/jivesoftware/smack/util/FileUtils.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
package org.jivesoftware.smack.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class FileUtils {
|
||||
|
||||
private FileUtils() {
|
||||
}
|
||||
|
||||
public static InputStream getStreamForUrl(String url, ClassLoader loader) throws MalformedURLException, IOException {
|
||||
URI fileUri = URI.create(url);
|
||||
|
||||
if (fileUri.getScheme() == null) {
|
||||
throw new MalformedURLException("No protocol found in file URL: " + url);
|
||||
}
|
||||
|
||||
if (fileUri.getScheme().equals("classpath")) {
|
||||
// Get an array of class loaders to try loading the providers files from.
|
||||
ClassLoader[] classLoaders = getClassLoaders();
|
||||
for (ClassLoader classLoader : classLoaders) {
|
||||
InputStream is = classLoader.getResourceAsStream(fileUri.getSchemeSpecificPart());
|
||||
|
||||
if (is != null) {
|
||||
return is;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return fileUri.toURL().openStream();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default classloaders.
|
||||
*
|
||||
* @return an array of ClassLoader instances.
|
||||
*/
|
||||
public static ClassLoader[] getClassLoaders() {
|
||||
ClassLoader[] classLoaders = new ClassLoader[2];
|
||||
classLoaders[0] = FileUtils.class.getClassLoader();
|
||||
classLoaders[1] = Thread.currentThread().getContextClassLoader();
|
||||
// Clean up possible null values. Note that #getClassLoader may return a null value.
|
||||
List<ClassLoader> loaders = new ArrayList<ClassLoader>();
|
||||
|
||||
for (ClassLoader classLoader : classLoaders) {
|
||||
if (classLoader != null) {
|
||||
loaders.add(classLoader);
|
||||
}
|
||||
}
|
||||
return loaders.toArray(new ClassLoader[loaders.size()]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.jivesoftware.smackx.provider;
|
||||
|
||||
import org.jivesoftware.smack.provider.UrlProviderFileInitializer;
|
||||
|
||||
/**
|
||||
* Loads the default provider file for the Smack extensions on initialization.
|
||||
*
|
||||
* @author Robin Collier
|
||||
*
|
||||
*/
|
||||
public class ExtensionInitializer extends UrlProviderFileInitializer {
|
||||
@Override
|
||||
protected String getFilePath() {
|
||||
return "classpath:META-INF/extension.providers";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package org.jivesoftware.smackx.provider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.jivesoftware.smack.packet.IQ;
|
||||
import org.jivesoftware.smack.provider.ExtensionProviderInfo;
|
||||
import org.jivesoftware.smack.provider.IQProvider;
|
||||
import org.jivesoftware.smack.provider.IQProviderInfo;
|
||||
import org.jivesoftware.smack.provider.ProviderFileLoader;
|
||||
import org.jivesoftware.smack.provider.ProviderLoader;
|
||||
import org.jivesoftware.smack.provider.ProviderManager;
|
||||
import org.jivesoftware.smack.util.FileUtils;
|
||||
import org.junit.Test;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
public class ProviderConfigTest {
|
||||
|
||||
@Test
|
||||
public void addGenericLoaderProvider() {
|
||||
ProviderManager.getInstance().addLoader(new ProviderLoader() {
|
||||
|
||||
@Override
|
||||
public Collection<IQProviderInfo> getIQProviderInfo() {
|
||||
ArrayList<IQProviderInfo> l = new ArrayList<IQProviderInfo>(1);
|
||||
l.add(new IQProviderInfo("provider", "test:provider", new TestIQProvider()));
|
||||
return l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ExtensionProviderInfo> getExtensionProviderInfo() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:provider"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addClasspathFileLoaderProvider() throws Exception{
|
||||
ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:org/jivesoftware/smackx/provider/test.providers", null)));
|
||||
Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:file_provider"));
|
||||
}
|
||||
|
||||
public static class TestIQProvider implements IQProvider {
|
||||
|
||||
@Override
|
||||
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
11
test-unit/org/jivesoftware/smackx/provider/test.providers
Normal file
11
test-unit/org/jivesoftware/smackx/provider/test.providers
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Providers file for default Smack extensions -->
|
||||
<smackProviders>
|
||||
|
||||
<iqProvider>
|
||||
<elementName>provider</elementName>
|
||||
<namespace>test:file_provider</namespace>
|
||||
<className>org.jivesoftware.smackx.provider.ProviderConfigTest$TestIQProvider</className>
|
||||
</iqProvider>
|
||||
|
||||
</smackProviders>
|
Loading…
Reference in a new issue