KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > smack > provider > ProviderManager


1 /**
2  * $RCSfile$
3  * $Revision: 2466 $
4  * $Date: 2005-03-22 23:36:19 -0300 (Tue, 22 Mar 2005) $
5  *
6  * Copyright 2003-2004 Jive Software.
7  *
8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */

20
21 package org.jivesoftware.smack.provider;
22
23 import org.jivesoftware.smack.packet.IQ;
24 import org.jivesoftware.smack.packet.PacketExtension;
25 import org.xmlpull.v1.*;
26 import org.xmlpull.mxp1.MXParser;
27
28 import java.util.*;
29 import java.net.URL JavaDoc;
30
31 /**
32  * Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of
33  * providers exist:<ul>
34  * <li>IQProvider -- parses IQ requests into Java objects.
35  * <li>PacketExtension -- parses XML sub-documents attached to packets into
36  * PacketExtension instances.</ul>
37  *
38  * <b>IQProvider</b><p>
39  *
40  * By default, Smack only knows how to process IQ packets with sub-packets that
41  * are in a few namespaces such as:<ul>
42  * <li>jabber:iq:auth
43  * <li>jabber:iq:roster
44  * <li>jabber:iq:register</ul>
45  *
46  * Because many more IQ types are part of XMPP and its extensions, a pluggable IQ parsing
47  * mechanism is provided. IQ providers are registered programatically or by creating a
48  * smack.providers file in the META-INF directory of your JAR file. The file is an XML
49  * document that contains one or more iqProvider entries, as in the following example:
50  *
51  * <pre>
52  * &lt;?xml version="1.0"?&gt;
53  * &lt;smackProviders&gt;
54  * &lt;iqProvider&gt;
55  * &lt;elementName&gt;query&lt;/elementName&gt;
56  * &lt;namespace&gt;jabber:iq:time&lt;/namespace&gt;
57  * &lt;className&gt;org.jivesoftware.smack.packet.Time&lt/className&gt;
58  * &lt;/iqProvider&gt;
59  * &lt;/smackProviders&gt;</pre>
60  *
61  * Each IQ provider is associated with an element name and a namespace. If multiple provider
62  * entries attempt to register to handle the same namespace, the first entry loaded from the
63  * classpath will take precedence. The IQ provider class can either implement the IQProvider
64  * interface, or extend the IQ class. In the former case, each IQProvider is responsible for
65  * parsing the raw XML stream to create an IQ instance. In the latter case, bean introspection
66  * is used to try to automatically set properties of the IQ instance using the values found
67  * in the IQ packet XML. For example, an XMPP time packet resembles the following:
68  * <pre>
69  * &lt;iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'&gt;
70  * &lt;query xmlns='jabber:iq:time'&gt;
71  * &lt;utc&gt;20020910T17:58:35&lt;/utc&gt;
72  * &lt;tz&gt;MDT&lt;/tz&gt;
73  * &lt;display&gt;Tue Sep 10 12:58:35 2002&lt;/display&gt;
74  * &lt;/query&gt;
75  * &lt;/iq&gt;</pre>
76  *
77  * In order for this packet to be automatically mapped to the Time object listed in the
78  * providers file above, it must have the methods setUtc(String), setTz(String), and
79  * setDisplay(String). The introspection service will automatically try to convert the String
80  * value from the XML into a boolean, int, long, float, double, or Class depending on the
81  * type the IQ instance expects.<p>
82  *
83  * A pluggable system for packet extensions, child elements in a custom namespace for
84  * message and presence packets, also exists. Each extension provider
85  * is registered with a name space in the smack.providers file as in the following example:
86  *
87  * <pre>
88  * &lt;?xml version="1.0"?&gt;
89  * &lt;smackProviders&gt;
90  * &lt;extensionProvider&gt;
91  * &lt;elementName&gt;x&lt;/elementName&gt;
92  * &lt;namespace&gt;jabber:iq:event&lt;/namespace&gt;
93  * &lt;className&gt;org.jivesoftware.smack.packet.MessageEvent&lt/className&gt;
94  * &lt;/extensionProvider&gt;
95  * &lt;/smackProviders&gt;</pre>
96  *
97  * If multiple provider entries attempt to register to handle the same element name and namespace,
98  * the first entry loaded from the classpath will take precedence. Whenever a packet extension
99  * is found in a packet, parsing will be passed to the correct provider. Each provider
100  * can either implement the PacketExtensionProvider interface or be a standard Java Bean. In
101  * the former case, each extension provider is responsible for parsing the raw XML stream to
102  * contruct an object. In the latter case, bean introspection is used to try to automatically
103  * set the properties of the class using the values in the packet extension sub-element. When an
104  * extension provider is not registered for an element name and namespace combination, Smack will
105  * store all top-level elements of the sub-packet in DefaultPacketExtension object and then
106  * attach it to the packet.
107  *
108  * @author Matt Tucker
109  */

110 public class ProviderManager {
111
112     private static Map extensionProviders = new Hashtable();
113     private static Map iqProviders = new Hashtable();
114
115     static {
116         // Load IQ processing providers.
117
try {
118             // Get an array of class loaders to try loading the providers files from.
119
ClassLoader JavaDoc[] classLoaders = getClassLoaders();
120             for (int i=0; i<classLoaders.length; i++) {
121                 Enumeration providerEnum = classLoaders[i].getResources(
122                         "META-INF/smack.providers");
123                 while (providerEnum.hasMoreElements()) {
124                     URL JavaDoc url = (URL JavaDoc)providerEnum.nextElement();
125                     java.io.InputStream JavaDoc providerStream = null;
126                     try {
127                         providerStream = url.openStream();
128                         XmlPullParser parser = new MXParser();
129                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
130                         parser.setInput(providerStream, "UTF-8");
131                         int eventType = parser.getEventType();
132                         do {
133                             if (eventType == XmlPullParser.START_TAG) {
134                                 if (parser.getName().equals("iqProvider")) {
135                                     parser.next();
136                                     parser.next();
137                                     String JavaDoc elementName = parser.nextText();
138                                     parser.next();
139                                     parser.next();
140                                     String JavaDoc namespace = parser.nextText();
141                                     parser.next();
142                                     parser.next();
143                                     String JavaDoc className = parser.nextText();
144                                     // Only add the provider for the namespace if one isn't
145
// already registered.
146
String JavaDoc key = getProviderKey(elementName, namespace);
147                                     if (!iqProviders.containsKey(key)) {
148                                         // Attempt to load the provider class and then create
149
// a new instance if it's an IQProvider. Otherwise, if it's
150
// an IQ class, add the class object itself, then we'll use
151
// reflection later to create instances of the class.
152
try {
153                                             // Add the provider to the map.
154
Class JavaDoc provider = Class.forName(className);
155                                             if (IQProvider.class.isAssignableFrom(provider)) {
156                                                 iqProviders.put(key, provider.newInstance());
157                                             }
158                                             else if (IQ.class.isAssignableFrom(provider)) {
159                                                 iqProviders.put(key, provider);
160                                             }
161                                         }
162                                         catch (ClassNotFoundException JavaDoc cnfe) {
163                                             cnfe.printStackTrace();
164                                         }
165                                     }
166                                 }
167                                 else if (parser.getName().equals("extensionProvider")) {
168                                     parser.next();
169                                     parser.next();
170                                     String JavaDoc elementName = parser.nextText();
171                                     parser.next();
172                                     parser.next();
173                                     String JavaDoc namespace = parser.nextText();
174                                     parser.next();
175                                     parser.next();
176                                     String JavaDoc className = parser.nextText();
177                                     // Only add the provider for the namespace if one isn't
178
// already registered.
179
String JavaDoc key = getProviderKey(elementName, namespace);
180                                     if (!extensionProviders.containsKey(key)) {
181                                         // Attempt to load the provider class and then create
182
// a new instance if it's a Provider. Otherwise, if it's
183
// a PacketExtension, add the class object itself and
184
// then we'll use reflection later to create instances
185
// of the class.
186
try {
187                                             // Add the provider to the map.
188
Class JavaDoc provider = Class.forName(className);
189                                             if (PacketExtensionProvider.class.isAssignableFrom(
190                                                     provider))
191                                             {
192                                                 extensionProviders.put(key, provider.newInstance());
193                                             }
194                                             else if (PacketExtension.class.isAssignableFrom(
195                                                     provider))
196                                             {
197                                                 extensionProviders.put(key, provider);
198                                             }
199                                         }
200                                         catch (ClassNotFoundException JavaDoc cnfe) {
201                                             cnfe.printStackTrace();
202                                         }
203                                     }
204                                 }
205                             }
206                             eventType = parser.next();
207                         } while (eventType != XmlPullParser.END_DOCUMENT);
208                     }
209                     finally {
210                         try { providerStream.close(); }
211                         catch (Exception JavaDoc e) { }
212                     }
213                 }
214             }
215         }
216         catch (Exception JavaDoc e) {
217             e.printStackTrace();
218         }
219     }
220
221     /**
222      * Returns the IQ provider registered to the specified XML element name and namespace.
223      * For example, if a provider was registered to the element name "query" and the
224      * namespace "jabber:iq:time", then the following packet would trigger the provider:
225      *
226      * <pre>
227      * &lt;iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'&gt;
228      * &lt;query xmlns='jabber:iq:time'&gt;
229      * &lt;utc&gt;20020910T17:58:35&lt;/utc&gt;
230      * &lt;tz&gt;MDT&lt;/tz&gt;
231      * &lt;display&gt;Tue Sep 10 12:58:35 2002&lt;/display&gt;
232      * &lt;/query&gt;
233      * &lt;/iq&gt;</pre>
234      *
235      * <p>Note: this method is generally only called by the internal Smack classes.
236      *
237      * @param elementName the XML element name.
238      * @param namespace the XML namespace.
239      * @return the IQ provider.
240      */

241     public static Object JavaDoc getIQProvider(String JavaDoc elementName, String JavaDoc namespace) {
242         String JavaDoc key = getProviderKey(elementName, namespace);
243         return iqProviders.get(key);
244     }
245
246     /**
247      * Returns an Iterator for all IQProvider instances.
248      *
249      * @return an Iterator for all IQProvider instances.
250      */

251     public static Iterator getIQProviders() {
252         return Collections.unmodifiableCollection(new HashMap(iqProviders).values()).iterator();
253     }
254
255     /**
256      * Adds an IQ provider (must be an instance of IQProvider or Class object that is an IQ)
257      * with the specified element name and name space. The provider will override any providers
258      * loaded through the classpath.
259      *
260      * @param elementName the XML element name.
261      * @param namespace the XML namespace.
262      * @param provider the IQ provider.
263      */

264     public static void addIQProvider(String JavaDoc elementName, String JavaDoc namespace,
265             Object JavaDoc provider)
266     {
267         if (!(provider instanceof IQProvider || (provider instanceof Class JavaDoc &&
268                 IQ.class.isAssignableFrom((Class JavaDoc)provider))))
269         {
270             throw new IllegalArgumentException JavaDoc("Provider must be an IQProvider " +
271                     "or a Class instance.");
272         }
273         String JavaDoc key = getProviderKey(elementName, namespace);
274         iqProviders.put(key, provider);
275     }
276
277     /**
278      * Returns the packet extension provider registered to the specified XML element name
279      * and namespace. For example, if a provider was registered to the element name "x" and the
280      * namespace "jabber:x:event", then the following packet would trigger the provider:
281      *
282      * <pre>
283      * &lt;message to='romeo@montague.net' id='message_1'&gt;
284      * &lt;body&gt;Art thou not Romeo, and a Montague?&lt;/body&gt;
285      * &lt;x xmlns='jabber:x:event'&gt;
286      * &lt;composing/&gt;
287      * &lt;/x&gt;
288      * &lt;/message&gt;</pre>
289      *
290      * <p>Note: this method is generally only called by the internal Smack classes.
291      *
292      * @param elementName
293      * @param namespace
294      * @return the extenion provider.
295      */

296     public static Object JavaDoc getExtensionProvider(String JavaDoc elementName, String JavaDoc namespace) {
297         String JavaDoc key = getProviderKey(elementName, namespace);
298         return extensionProviders.get(key);
299     }
300
301     /**
302      * Adds an extension provider with the specified element name and name space. The provider
303      * will override any providers loaded through the classpath. The provider must be either
304      * a PacketExtensionProvider instance, or a Class object of a Javabean.
305      *
306      * @param elementName the XML element name.
307      * @param namespace the XML namespace.
308      * @param provider the extension provider.
309      */

310     public static void addExtensionProvider(String JavaDoc elementName, String JavaDoc namespace,
311             Object JavaDoc provider)
312     {
313         if (!(provider instanceof PacketExtensionProvider || provider instanceof Class JavaDoc)) {
314             throw new IllegalArgumentException JavaDoc("Provider must be a PacketExtensionProvider " +
315                     "or a Class instance.");
316         }
317         String JavaDoc key = getProviderKey(elementName, namespace);
318         extensionProviders.put(key, provider);
319     }
320
321     /**
322      * Returns an Iterator for all PacketExtensionProvider instances.
323      *
324      * @return an Iterator for all PacketExtensionProvider instances.
325      */

326     public static Iterator getExtensionProviders() {
327         return Collections.unmodifiableCollection(
328                 new HashMap(extensionProviders).values()).iterator();
329     }
330
331     /**
332      * Returns a String key for a given element name and namespace.
333      *
334      * @param elementName the element name.
335      * @param namespace the namespace.
336      * @return a unique key for the element name and namespace pair.
337      */

338     private static String JavaDoc getProviderKey(String JavaDoc elementName, String JavaDoc namespace) {
339         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
340         buf.append("<").append(elementName).append("/><").append(namespace).append("/>");
341         return buf.toString();
342     }
343
344     /**
345      * Returns an array of class loaders to load resources from.
346      *
347      * @return an array of ClassLoader instances.
348      */

349     private static ClassLoader JavaDoc[] getClassLoaders() {
350         ClassLoader JavaDoc[] classLoaders = new ClassLoader JavaDoc[2];
351         classLoaders[0] = new ProviderManager().getClass().getClassLoader();
352         classLoaders[1] = Thread.currentThread().getContextClassLoader();
353         return classLoaders;
354     }
355
356     private ProviderManager() {
357
358     }
359 }
Popular Tags