/**
*
* Copyright 2003-2007 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.provider;
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:
*
IQProvider -- parses IQ requests into Java objects.
*
PacketExtension -- parses XML sub-documents attached to packets into
* PacketExtension instances.
*
* IQProvider
*
* By default, Smack only knows how to process IQ packets with sub-packets that
* are in a few namespaces such as:
*
jabber:iq:auth
*
jabber:iq:roster
*
jabber:iq:register
*
* 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:
*
*
*
* Each IQ provider is associated with an element name and a namespace. If multiple provider
* entries attempt to register to handle the same namespace, the first entry loaded from the
* classpath will take precedence. The IQ provider class can either implement the IQProvider
* interface, or extend the IQ class. In the former case, each IQProvider is responsible for
* parsing the raw XML stream to create an IQ instance. In 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:
*
*
* 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
* value from the XML into a boolean, int, long, float, double, or Class depending on the
* type the IQ instance expects.
*
* A pluggable system for packet extensions, child elements in a custom namespace for
* message and presence packets, also exists. Each extension provider
* is registered with a name space in the smack.providers file as in the following example:
*
*
*
* 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. 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 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.
*
* @author Matt Tucker
*/
public final class ProviderManager {
private static ProviderManager instance;
private Map extensionProviders = new ConcurrentHashMap();
private Map iqProviders = new ConcurrentHashMap();
/**
* Returns the ProviderManager instance.
*
* @return the only ProviderManager valid instance.
*/
public static synchronized ProviderManager getInstance() {
if (instance == null) {
instance = new ProviderManager();
}
return instance;
}
private ProviderManager() {
super();
}
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());
}
}
if (loader.getExtensionProviderInfo() != null) {
for (ExtensionProviderInfo info : loader.getExtensionProviderInfo()) {
extensionProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider());
}
}
}
/**
* Returns the IQ provider registered to the specified XML element name and namespace.
* For example, if a provider was registered to the element name "query" and the
* namespace "jabber:iq:time", then the following packet would trigger the provider:
*
*
Note: this method is generally only called by the internal Smack classes.
*
* @param elementName the XML element name.
* @param namespace the XML namespace.
* @return the IQ provider.
*/
public Object getIQProvider(String elementName, String namespace) {
String key = getProviderKey(elementName, namespace);
return iqProviders.get(key);
}
/**
* Returns an unmodifiable collection of all IQProvider instances. Each object
* in the collection will either be an IQProvider instance, or a Class object
* that implements the IQProvider interface.
*
* @return all IQProvider instances.
*/
public Collection