KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > disco > IQDiscoInfoHandler


1 /**
2  * $RCSfile: IQDiscoInfoHandler.java,v $
3  * $Revision: 1.13 $
4  * $Date: 2005/07/26 05:23:45 $
5  *
6  * Copyright (C) 2004 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.messenger.disco;
13
14 import org.dom4j.DocumentHelper;
15 import org.dom4j.Element;
16 import org.dom4j.QName;
17 import org.jivesoftware.messenger.IQHandlerInfo;
18 import org.jivesoftware.messenger.XMPPServer;
19 import org.jivesoftware.messenger.auth.UnauthorizedException;
20 import org.jivesoftware.messenger.forms.spi.XDataFormImpl;
21 import org.jivesoftware.messenger.handler.IQHandler;
22 import org.jivesoftware.messenger.user.UserManager;
23 import org.jivesoftware.messenger.user.UserNotFoundException;
24 import org.xmpp.packet.IQ;
25 import org.xmpp.packet.JID;
26 import org.xmpp.packet.PacketError;
27
28 import java.util.*;
29 import java.util.concurrent.ConcurrentHashMap JavaDoc;
30
31 /**
32  * IQDiscoInfoHandler is responsible for handling disco#info requests. This class holds a map with
33  * the main entities and the associated DiscoInfoProvider. We are considering the host of the
34  * recipient JIDs as main entities. It's the DiscoInfoProvider responsibility to provide information
35  * about the JID's name together with any possible requested node.<p>
36  * <p/>
37  * For example, let's have in the entities map the following entries: "localhost" and
38  * "conference.localhost". Associated with each entry we have different DiscoInfoProviders. Now we
39  * receive a disco#info request for the following JID: "room@conference.localhost" which is a disco
40  * request for a MUC room. So IQDiscoInfoHandler will look for the DiscoInfoProvider associated
41  * with the JID's host which in this case is "conference.localhost". Once we have located the
42  * provider we will delegate to the provider the responsibility to provide the info specific to
43  * the JID's name which in this case is "room". Among the information that a room could provide we
44  * could find its identity and the features it supports (e.g. 'muc_passwordprotected',
45  * 'muc_unmoderated', etc.). Finally, after we have collected all the information provided by the
46  * provider we will add it to the reply. On the other hand, if no provider was found or the provider
47  * has no information for the requested name/node then a not-found error will be returned.
48  *
49  * @author Gaston Dombiak
50  */

51 public class IQDiscoInfoHandler extends IQHandler {
52
53     private HashMap JavaDoc entities = new HashMap JavaDoc();
54     private List<String JavaDoc> serverFeatures = new ArrayList<String JavaDoc>();
55     private Map JavaDoc<String JavaDoc, DiscoInfoProvider> serverNodeProviders =
56             new ConcurrentHashMap JavaDoc<String JavaDoc, DiscoInfoProvider>();
57     private IQHandlerInfo info;
58
59     private List<Element> userIdentities = new ArrayList<Element>();
60     private List<String JavaDoc> userFeatures = new ArrayList<String JavaDoc>();
61
62     public IQDiscoInfoHandler() {
63         super("XMPP Disco Info Handler");
64         info = new IQHandlerInfo("query", "http://jabber.org/protocol/disco#info");
65         serverFeatures.add("http://jabber.org/protocol/disco#info");
66         // Initialize the user identity and features collections (optimization to avoid creating
67
// the same objects for each response)
68
Element userIdentity = DocumentHelper.createElement("identity");
69         userIdentity.addAttribute("category", "account");
70         userIdentity.addAttribute("type", "registered");
71         userIdentities.add(userIdentity);
72         userFeatures.add("http://jabber.org/protocol/disco#info");
73     }
74
75     public IQHandlerInfo getInfo() {
76         return info;
77     }
78
79     public IQ handleIQ(IQ packet) throws UnauthorizedException {
80         // TODO Let configure an authorization policy (ACL?). Currently anyone can discover info.
81

82         // Create a copy of the sent pack that will be used as the reply
83
// we only need to add the requested info to the reply if any otherwise add
84
// a not found error
85
IQ reply = IQ.createResultIQ(packet);
86
87         // Look for a DiscoInfoProvider associated with the requested entity.
88
// We consider the host of the recipient JID of the packet as the entity. It's the
89
// DiscoInfoProvider responsibility to provide information about the JID's name together
90
// with any possible requested node.
91
DiscoInfoProvider infoProvider = getProvider(packet.getTo() == null ?
92                 XMPPServer.getInstance().getServerInfo().getName() : packet.getTo().getDomain());
93         if (infoProvider != null) {
94             // Get the JID's name
95
String JavaDoc name = packet.getTo() == null ? null : packet.getTo().getNode();
96             if (name == null || name.trim().length() == 0) {
97                 name = null;
98             }
99             // Get the requested node
100
Element iq = packet.getChildElement();
101             String JavaDoc node = iq.attributeValue("node");
102             //String node = metaData.getProperty("query:node");
103

104             // Check if we have information about the requested name and node
105
if (infoProvider.hasInfo(name, node, packet.getFrom())) {
106                 reply.setChildElement(iq.createCopy());
107                 Element queryElement = reply.getChildElement();
108
109                 // Add to the reply all the identities provided by the DiscoInfoProvider
110
Element identity;
111                 Iterator identities = infoProvider.getIdentities(name, node, packet.getFrom());
112                 while (identities.hasNext()) {
113                     identity = (Element)identities.next();
114                     identity.setQName(new QName(identity.getName(), queryElement.getNamespace()));
115                     queryElement.add((Element)identity.clone());
116                 }
117                 
118                 // Add to the reply all the features provided by the DiscoInfoProvider
119
Iterator features = infoProvider.getFeatures(name, node, packet.getFrom());
120                 while (features.hasNext()) {
121                     queryElement.addElement("feature").addAttribute("var", (String JavaDoc)features.next());
122                 }
123
124                 // Add to the reply the extended info (XDataForm) provided by the DiscoInfoProvider
125
XDataFormImpl dataForm = infoProvider.getExtendedInfo(name, node, packet.getFrom());
126                 if (dataForm != null) {
127                     queryElement.add(dataForm.asXMLElement());
128                 }
129             }
130             else {
131                 // If the DiscoInfoProvider has no information for the requested name and node
132
// then answer a not found error
133
reply.setChildElement(packet.getChildElement().createCopy());
134                 reply.setError(PacketError.Condition.item_not_found);
135             }
136         }
137         else {
138             // If we didn't find a DiscoInfoProvider then answer a not found error
139
reply.setChildElement(packet.getChildElement().createCopy());
140             reply.setError(PacketError.Condition.item_not_found);
141         }
142
143         return reply;
144     }
145
146     /**
147      * Sets the DiscoInfoProvider to use when a disco#info packet is sent to the server itself
148      * and the specified node. For instance, if node matches "http://jabber.org/protocol/offline"
149      * then a special DiscoInfoProvider should be use to return information about offline messages.
150      *
151      * @param node the node that the provider will handle.
152      * @param provider the DiscoInfoProvider that will handle disco#info packets sent with the
153      * specified node.
154      */

155     public void setServerNodeInfoProvider(String JavaDoc node, DiscoInfoProvider provider) {
156         serverNodeProviders.put(node, provider);
157     }
158
159     /**
160      * Removes the DiscoInfoProvider to use when a disco#info packet is sent to the server itself
161      * and the specified node.
162      *
163      * @param node the node that the provider was handling.
164      */

165     public void removeServerNodeInfoProvider(String JavaDoc node) {
166         serverNodeProviders.remove(node);
167     }
168
169     /**
170      * Returns the DiscoInfoProvider responsible for providing information about a given entity or
171      * null if none was found.
172      *
173      * @param name the name of the identity.
174      * @return the DiscoInfoProvider responsible for providing information about a given entity or
175      * null if none was found.
176      */

177     private DiscoInfoProvider getProvider(String JavaDoc name) {
178         return (DiscoInfoProvider)entities.get(name);
179     }
180
181     /**
182      * Sets that a given DiscoInfoProvider will provide information about a given entity. This
183      * message must be used when new modules (e.g. MUC) are implemented and need to provide
184      * information about them.
185      *
186      * @param name the name of the entity.
187      * @param provider the DiscoInfoProvider that will provide the entity's information.
188      */

189     protected void setProvider(String JavaDoc name, DiscoInfoProvider provider) {
190         entities.put(name, provider);
191     }
192
193     /**
194      * Removes the DiscoInfoProvider related to a given entity.
195      *
196      * @param name the name of the entity.
197      */

198     protected void removeProvider(String JavaDoc name) {
199         entities.remove(name);
200     }
201
202     /**
203      * Adds the features provided by the new service that implements the ServerFeaturesProvider
204      * interface. This information will be used whenever a disco for information is made against
205      * the server (i.e. the packet's target is the server).
206      * Example of features are: jabber:iq:agents, jabber:iq:time, etc.
207      *
208      * @param provider the ServerFeaturesProvider that provides new server features.
209      */

210     private void addServerFeaturesProvider(ServerFeaturesProvider provider) {
211         for (Iterator<String JavaDoc> it = provider.getFeatures(); it.hasNext();) {
212             serverFeatures.add(it.next());
213         }
214     }
215
216     public void initialize(XMPPServer server) {
217         super.initialize(server);
218         // Track the implementors of ServerFeaturesProvider so that we can collect the features
219
// provided by the server
220
for (ServerFeaturesProvider provider : server.getServerFeaturesProviders()) {
221             addServerFeaturesProvider(provider);
222         }
223         setProvider(server.getServerInfo().getName(), getServerInfoProvider());
224     }
225
226     /**
227      * Returns the DiscoInfoProvider responsible for providing information at the server level. This
228      * means that this DiscoInfoProvider will provide information whenever a disco request whose
229      * recipient JID is the server (e.g. localhost) is made.
230      *
231      * @return the DiscoInfoProvider responsible for providing information at the server level.
232      */

233     private DiscoInfoProvider getServerInfoProvider() {
234         DiscoInfoProvider discoInfoProvider = new DiscoInfoProvider() {
235             ArrayList<Element> identities = new ArrayList<Element>();
236
237             public Iterator<Element> getIdentities(String JavaDoc name, String JavaDoc node, JID senderJID) {
238                 if (node != null && serverNodeProviders.get(node) != null) {
239                     // Redirect the request to the disco info provider of the specified node
240
return serverNodeProviders.get(node).getIdentities(name, node, senderJID);
241                 }
242                 if (name == null) {
243                     // Answer identity of the server
244
synchronized (identities) {
245                         if (identities.isEmpty()) {
246                             Element identity = DocumentHelper.createElement("identity");
247                             identity.addAttribute("category", "services");
248                             identity.addAttribute("name", "Messenger Server");
249                             identity.addAttribute("type", "jabber");
250
251                             identities.add(identity);
252                         }
253                     }
254                     return identities.iterator();
255                 }
256                 else {
257                     // Answer identity of a registered user.
258
// Note: We know that this user exists because #hasInfo returned true
259
return userIdentities.iterator();
260                 }
261             }
262
263             public Iterator<String JavaDoc> getFeatures(String JavaDoc name, String JavaDoc node, JID senderJID) {
264                 if (node != null && serverNodeProviders.get(node) != null) {
265                     // Redirect the request to the disco info provider of the specified node
266
return serverNodeProviders.get(node).getFeatures(name, node, senderJID);
267                 }
268                 if (name == null) {
269                     // Answer features of the server
270
return serverFeatures.iterator();
271                 }
272                 else {
273                     // Answer features of the user
274
return userFeatures.iterator();
275                 }
276             }
277
278             public boolean hasInfo(String JavaDoc name, String JavaDoc node, JID senderJID) {
279                 if (node != null) {
280                     if (serverNodeProviders.get(node) != null) {
281                         // Redirect the request to the disco info provider of the specified node
282
return serverNodeProviders.get(node).hasInfo(name, node, senderJID);
283                     }
284                     // Unknown node
285
return false;
286                 }
287                 try {
288                     // True if it is an info request of the server or of a registered user. We
289
// now support disco of user's bare JIDs
290
return node == null &&
291                             (name == null || UserManager.getInstance().getUser(name) != null);
292                 }
293                 catch (UserNotFoundException e) {
294                     return false;
295                 }
296             }
297
298             public XDataFormImpl getExtendedInfo(String JavaDoc name, String JavaDoc node, JID senderJID) {
299                 if (node != null && serverNodeProviders.get(node) != null) {
300                     // Redirect the request to the disco info provider of the specified node
301
return serverNodeProviders.get(node).getExtendedInfo(name, node, senderJID);
302                 }
303                 return null;
304             }
305         };
306         return discoInfoProvider;
307     }
308 }
Popular Tags