KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > plugin > SearchPlugin


1 /**
2  * Copyright (C) 2005 Jive Software. All rights reserved.
3  *
4  * This software is published under the terms of the GNU Public License (GPL),
5  * a copy of which is included in this distribution.
6  */

7
8 package org.jivesoftware.messenger.plugin;
9
10 import java.io.File JavaDoc;
11 import java.util.ArrayList JavaDoc;
12 import java.util.Arrays JavaDoc;
13 import java.util.Collection JavaDoc;
14 import java.util.Enumeration JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Hashtable JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20
21 import org.dom4j.Attribute;
22 import org.dom4j.DocumentHelper;
23 import org.dom4j.Element;
24 import org.dom4j.QName;
25 import org.jivesoftware.messenger.XMPPServer;
26 import org.jivesoftware.messenger.container.Plugin;
27 import org.jivesoftware.messenger.container.PluginManager;
28 import org.jivesoftware.messenger.forms.DataForm;
29 import org.jivesoftware.messenger.forms.FormField;
30 import org.jivesoftware.messenger.forms.spi.XDataFormImpl;
31 import org.jivesoftware.messenger.forms.spi.XFormFieldImpl;
32 import org.jivesoftware.messenger.user.User;
33 import org.jivesoftware.messenger.user.UserManager;
34 import org.jivesoftware.messenger.user.UserNotFoundException;
35 import org.jivesoftware.util.JiveGlobals;
36 import org.jivesoftware.util.Log;
37 import org.jivesoftware.util.PropertyEventDispatcher;
38 import org.jivesoftware.util.PropertyEventListener;
39 import org.xmpp.component.Component;
40 import org.xmpp.component.ComponentException;
41 import org.xmpp.component.ComponentManager;
42 import org.xmpp.component.ComponentManagerFactory;
43 import org.xmpp.packet.IQ;
44 import org.xmpp.packet.JID;
45 import org.xmpp.packet.Packet;
46
47 /**
48  * Provides support for Jabber Search
49  * (<a HREF="http://www.jabber.org/jeps/jep-0055.html">JEP-0055</a>).<p>
50  *
51  * The basic functionality is to query an information repository
52  * regarding the possible search fields, to send a search query,
53  * and to receive search results. This implementation below primarily uses
54  * <a HREF="http://www.jabber.org/jeps/jep-0004.html">Data Forms</a>, but
55  * limited support for non-datforms searches has been added to support the
56  * Miranda client.
57  * <p/>
58  *
59  * @author Ryan Graham
60  */

61 public class SearchPlugin implements Component, Plugin, PropertyEventListener {
62     private XMPPServer server;
63     private UserManager userManager;
64     private ComponentManager componentManager;
65     private PluginManager pluginManager;
66
67     private String JavaDoc serviceName;
68     private boolean serviceEnabled;
69     
70     private static String JavaDoc serverName;
71
72     private static String JavaDoc instructions = "The following fields are available for search. "
73         + "Wildcard (*) characters are allowed as part the of query.";
74     
75     private static Element probeResult;
76
77     private Collection JavaDoc<String JavaDoc> searchFields;
78
79     public SearchPlugin() {
80         serviceName = JiveGlobals.getProperty("plugin.search.serviceName", "search");
81         serviceEnabled = JiveGlobals.getBooleanProperty("plugin.search.serviceEnabled", true);
82         
83         server = XMPPServer.getInstance();
84         serverName = server.getServerInfo().getName();
85         // See if the installed provider supports searching. If not, workaround
86
// by providing our own searching.
87
UserManager manager = UserManager.getInstance();
88         try {
89             searchFields = manager.getSearchFields();
90             userManager = UserManager.getInstance();
91             searchFields = userManager.getSearchFields();
92         }
93         catch (UnsupportedOperationException JavaDoc uoe) {
94             // Use a SearchPluginUserManager instead.
95
searchFields = getSearchFields();
96         }
97     }
98
99     public String JavaDoc getName() {
100         return pluginManager.getName(this);
101     }
102
103     public String JavaDoc getDescription() {
104         return pluginManager.getDescription(this);
105     }
106     
107     public void initializePlugin(PluginManager manager, File JavaDoc pluginDirectory) {
108         pluginManager = manager;
109         
110         componentManager = ComponentManagerFactory.getComponentManager();
111         try {
112             componentManager.addComponent(serviceName, this);
113         }
114         catch (Exception JavaDoc e) {
115             componentManager.getLog().error(e);
116         }
117         PropertyEventDispatcher.addListener(this);
118
119         if (probeResult == null) {
120             probeResult = DocumentHelper.createElement(QName.get("query", "jabber:iq:search"));
121
122             //non-data form
123
probeResult.addElement("instructions").addText(instructions);
124             
125             XDataFormImpl searchForm = new XDataFormImpl(DataForm.TYPE_FORM);
126             searchForm.setTitle("User Search");
127             searchForm.addInstruction(instructions);
128             
129             XFormFieldImpl field = new XFormFieldImpl("FORM_TYPE");
130             field.setType(FormField.TYPE_HIDDEN);
131             field.addValue("jabber:iq:search");
132             searchForm.addField(field);
133             
134             field = new XFormFieldImpl("search");
135             field.setType(FormField.TYPE_TEXT_SINGLE);
136             field.setLabel("Search");
137             field.setRequired(false);
138             searchForm.addField(field);
139             
140             for (String JavaDoc searchField : searchFields) {
141                 //non-data form
142
probeResult.addElement(searchField.toLowerCase());
143
144                 field = new XFormFieldImpl(searchField);
145                 field.setType(FormField.TYPE_BOOLEAN);
146                 field.addValue("1");
147                 field.setLabel(searchField);
148                 field.setRequired(false);
149                 searchForm.addField(field);
150             }
151
152             probeResult.add(searchForm.asXMLElement());
153         }
154     }
155     
156     public void initialize(JID jid, ComponentManager componentManager) {
157     }
158
159     public void start() {
160     }
161
162     public void destroyPlugin() {
163         PropertyEventDispatcher.removeListener(this);
164         pluginManager = null;
165         try {
166             componentManager.removeComponent(serviceName);
167             componentManager = null;
168         }
169         catch (Exception JavaDoc e) {
170             componentManager.getLog().error(e);
171         }
172         server = null;
173         userManager = null;
174     }
175
176     public void shutdown() {
177     }
178     
179     public void processPacket(Packet p) {
180         if (p instanceof IQ) {
181             IQ packet = (IQ) p;
182
183             Element childElement = (packet).getChildElement();
184             String JavaDoc namespace = null;
185             if (childElement != null) {
186                 namespace = childElement.getNamespaceURI();
187             }
188
189             if ("jabber:iq:search".equals(namespace)) {
190                 try {
191                     IQ replyPacket = handleIQ(packet);
192                     componentManager.sendPacket(this, replyPacket);
193                 }
194                 catch (ComponentException e) {
195                     componentManager.getLog().error(e);
196                 }
197
198             }
199             else if ("http://jabber.org/protocol/disco#info".equals(namespace)) {
200                 try {
201                     IQ replyPacket = IQ.createResultIQ(packet);
202
203                     Element responseElement = DocumentHelper.createElement(QName.get(
204                             "query", "http://jabber.org/protocol/disco#info"));
205                     responseElement.addElement("identity").addAttribute("category", "search")
206                                                           .addAttribute("type", "text")
207                                                           .addAttribute("name", "User Search");
208                     responseElement.addElement("feature").addAttribute("var", "jabber:iq:search");
209                     replyPacket.setChildElement(responseElement);
210
211                     componentManager.sendPacket(this, replyPacket);
212                 }
213                 catch (ComponentException e) {
214                     componentManager.getLog().error(e);
215                 }
216             }
217             else if ("http://jabber.org/protocol/disco#items".equals(namespace)) {
218                 try {
219                     IQ replyPacket = IQ.createResultIQ(packet);
220                     Element responseElement = DocumentHelper.createElement(QName.get(
221                             "query", "http://jabber.org/protocol/disco#info"));
222                     
223                     replyPacket.setChildElement(responseElement);
224                     componentManager.sendPacket(this, replyPacket);
225                 }
226                 catch (ComponentException e) {
227                     componentManager.getLog().error(e);
228                 }
229             }
230         }
231     }
232
233     private IQ handleIQ(IQ packet) {
234         if (!serviceEnabled) {
235             return replyDisabled(packet);
236         }
237         
238         if (IQ.Type.get.equals(packet.getType())) {
239             return processGetPacket(packet);
240         }
241         else if (IQ.Type.set.equals(packet.getType())) {
242             return processSetPacket(packet);
243         }
244
245         return null;
246     }
247     
248     private IQ replyDisabled(IQ packet) {
249         Element reply = DocumentHelper.createElement(QName.get("query", "jabber:iq:search"));
250         XDataFormImpl unavailableForm = new XDataFormImpl(DataForm.TYPE_CANCEL);
251         unavailableForm.setTitle("User Search");
252         unavailableForm.addInstruction("This service is unavailable.");
253         reply.add(unavailableForm.asXMLElement());
254         
255         IQ replyPacket = IQ.createResultIQ(packet);
256         replyPacket.setChildElement("query", "jabber:iq:search");
257         replyPacket.setChildElement(reply.createCopy());
258
259         return replyPacket;
260     }
261
262     private IQ processGetPacket(IQ packet) {
263         IQ replyPacket = IQ.createResultIQ(packet);
264         replyPacket.setChildElement("query", "jabber:iq:search");
265         replyPacket.setChildElement(probeResult.createCopy());
266
267         return replyPacket;
268     }
269
270     private IQ processSetPacket(IQ packet) {
271         List JavaDoc<User> users = new ArrayList JavaDoc<User>();
272         
273         Element incomingForm = packet.getChildElement();
274         boolean isDataFormQuery = (incomingForm.element(QName.get("x", "jabber:x:data")) != null);
275         
276         Hashtable JavaDoc<String JavaDoc, String JavaDoc> searchList = extractSearchQuery(incomingForm);
277         Enumeration JavaDoc<String JavaDoc> searchIter = searchList.keys();
278         while (searchIter.hasMoreElements()) {
279             String JavaDoc field = (String JavaDoc) searchIter.nextElement();
280             String JavaDoc query = (String JavaDoc) searchList.get(field);
281             
282             Collection JavaDoc<User> foundUsers = new ArrayList JavaDoc<User>();
283             if (userManager != null) {
284                 if (query.length() > 0 && !query.equals("jabber:iq:search")) {
285                     foundUsers.addAll(userManager.findUsers(new HashSet JavaDoc<String JavaDoc>(
286                             Arrays.asList((field))), query));
287                 }
288             }
289             else {
290                 foundUsers.addAll(findUsers(field, query));
291             }
292             
293             // Filter out all duplicate users.
294
for (User user : foundUsers) {
295                 if (!users.contains(user)) {
296                     users.add(user);
297                 }
298             }
299         }
300         
301         if (isDataFormQuery) {
302             return replyDataFormResult(users, packet);
303         }
304         else {
305             return replyNonDataFormResult(users, packet);
306         }
307     }
308     
309     /**
310      * nick, first, last, email fields have been hardcoded to support Miranda which does not
311      * make a query to discover which fields are available to be searched
312      */

313     private Hashtable JavaDoc<String JavaDoc, String JavaDoc> extractSearchQuery(Element incomingForm) {
314         Hashtable JavaDoc<String JavaDoc, String JavaDoc> searchList = new Hashtable JavaDoc<String JavaDoc, String JavaDoc>();
315         Element form = incomingForm.element(QName.get("x", "jabber:x:data"));
316         if (form == null) {
317             Element name = incomingForm.element("name");
318             if (name != null) {
319                 searchList.put("Name", name.getTextTrim());
320             }
321             
322             Element nick = incomingForm.element("nick");
323             if (nick != null) {
324                 searchList.put("Username", nick.getTextTrim());
325             }
326             
327             Element first = incomingForm.element("first");
328             if (first != null) {
329                 searchList.put("Name", first.getTextTrim());
330             }
331             
332             Element last = incomingForm.element("last");
333             if (last != null) {
334                 searchList.put("Name", last.getTextTrim());
335             }
336             
337             Element email = incomingForm.element("email");
338             if (email != null) {
339                 searchList.put("Email", email.getTextTrim());
340             }
341         }
342         else {
343             List JavaDoc<String JavaDoc> searchFields = new ArrayList JavaDoc<String JavaDoc>();
344             String JavaDoc search = "";
345             
346             Iterator JavaDoc fields = form.elementIterator("field");
347             while (fields.hasNext()) {
348                 Element searchField = (Element) fields.next();
349                 
350                 String JavaDoc field = searchField.attributeValue("var");
351                 String JavaDoc value = searchField.element("value").getTextTrim();
352                 if (field.equals("search")) {
353                     search = value;
354                 }
355                 else if (value.equals("1")) {
356                     searchFields.add(field);
357                 }
358             }
359             
360             for (String JavaDoc field : searchFields) {
361                 searchList.put(field, search);
362             }
363         }
364         
365         return searchList;
366     }
367     
368     private IQ replyDataFormResult(List JavaDoc<User> users, IQ packet) {
369         XDataFormImpl searchResults = new XDataFormImpl(DataForm.TYPE_RESULT);
370         
371         XFormFieldImpl field = new XFormFieldImpl("FORM_TYPE");
372         field.setType(FormField.TYPE_HIDDEN);
373         searchResults.addField(field);
374         
375         field = new XFormFieldImpl("jid");
376         field.setLabel("JID");
377         searchResults.addReportedField(field);
378
379         for (String JavaDoc fieldName : searchFields) {
380             field = new XFormFieldImpl(fieldName);
381             field.setLabel(fieldName);
382             searchResults.addReportedField(field);
383         }
384
385         for (User user : users) {
386             String JavaDoc username = user.getUsername();
387
388             ArrayList JavaDoc<XFormFieldImpl> items = new ArrayList JavaDoc<XFormFieldImpl>();
389             
390             XFormFieldImpl fieldJID = new XFormFieldImpl("jid");
391             fieldJID.addValue(username + "@" + serverName);
392             items.add(fieldJID);
393
394             XFormFieldImpl fieldUsername = new XFormFieldImpl("Username");
395             fieldUsername.addValue(username);
396             items.add(fieldUsername);
397
398             XFormFieldImpl fieldName = new XFormFieldImpl("Name");
399             fieldName.addValue(user.getName());
400             items.add(fieldName);
401
402             XFormFieldImpl fieldEmail = new XFormFieldImpl("Email");
403             fieldEmail.addValue(user.getEmail());
404             items.add(fieldEmail);
405
406             searchResults.addItemFields(items);
407         }
408
409         Element reply = DocumentHelper.createElement(QName.get("query", "jabber:iq:search"));
410         reply.add(searchResults.asXMLElement());
411
412         IQ replyPacket = IQ.createResultIQ(packet);
413         replyPacket.setChildElement(reply);
414         
415         return replyPacket;
416     }
417
418     private IQ replyNonDataFormResult(List JavaDoc<User> users, IQ packet) {
419         Element replyQuery = DocumentHelper.createElement(QName.get("query", "jabber:iq:search"));
420         String JavaDoc serverName = XMPPServer.getInstance().getServerInfo().getName();
421         
422         for (User user : users) {
423             Element item = DocumentHelper.createElement("item");
424             Attribute jib = DocumentHelper.createAttribute(item, "jid", user.getUsername() + "@" + serverName);
425             item.add(jib);
426             Element nick = DocumentHelper.createElement("nick");
427             nick.addText(user.getName());
428             item.add(nick);
429             Element email = DocumentHelper.createElement("email");
430             email.addText(user.getEmail());
431             item.add(email);
432             replyQuery.add(item);
433         }
434         IQ replyPacket = IQ.createResultIQ(packet);
435         replyPacket.setChildElement(replyQuery);
436         
437         return replyPacket;
438     }
439     
440     public String JavaDoc getServiceName() {
441         return serviceName;
442     }
443     
444     public void setServiceName(String JavaDoc name) {
445         changeServiceName(name);
446         JiveGlobals.setProperty("plugin.search.serviceName", name);
447     }
448     
449     public boolean getServiceEnabled() {
450         return serviceEnabled;
451     }
452     
453     public void setServiceEnabled(boolean enabled) {
454         serviceEnabled = enabled;
455         JiveGlobals.setProperty("plugin.search.serviceEnabled", enabled ? "true" : "false");
456     }
457     
458     public void propertySet(String JavaDoc property, Map JavaDoc params) {
459         if (property.equals("plugin.search.serviceEnabled")) {
460             this.serviceEnabled = Boolean.parseBoolean((String JavaDoc)params.get("value"));
461         }
462         else if (property.equals("plugin.search.serviceName")) {
463             changeServiceName((String JavaDoc)params.get("value"));
464         }
465     }
466
467     public void propertyDeleted(String JavaDoc property, Map JavaDoc params) {
468         if (property.equals("plugin.search.serviceEnabled")) {
469             this.serviceEnabled = true;
470         }
471         else if (property.equals("plugin.search.serviceName")) {
472             changeServiceName("search");
473         }
474     }
475
476     public void xmlPropertySet(String JavaDoc property, Map JavaDoc params) {
477         // not used
478
}
479
480     public void xmlPropertyDeleted(String JavaDoc property, Map JavaDoc params) {
481         // not used
482
}
483     
484     private void changeServiceName(String JavaDoc serviceName) {
485         if (serviceName == null) {
486             throw new NullPointerException JavaDoc("Service name cannot be null");
487         }
488         
489         if (this.serviceName.equals(serviceName)) {
490             return;
491         }
492
493         // Re-register the service.
494
try {
495             componentManager.removeComponent(this.serviceName);
496         }
497         catch (Exception JavaDoc e) {
498             componentManager.getLog().error(e);
499         }
500         
501         try {
502             componentManager.addComponent(serviceName, this);
503         }
504         catch (Exception JavaDoc e) {
505             componentManager.getLog().error(e);
506         }
507         
508         this.serviceName = serviceName;
509     }
510
511     /**
512      * Returns the collection of field names that can be used to search for a
513      * user. Typical fields are username, name, and email. These values can be
514      * used to contruct a data form.
515      */

516     public Collection JavaDoc<String JavaDoc> getSearchFields() {
517         return Arrays.asList("Username", "Name", "Email");
518     }
519
520     /**
521      * Finds a user using the specified field and query string. For example, a
522      * field name of "email" and query of "jsmith@example.com" would search for
523      * the user with that email address. Wildcard (*) characters are allowed as
524      * part of queries.
525      *
526      * A possible future improvement would be to have a third parameter that
527      * sets the maximum number of users returned and/or the number of users
528      * that are searched.
529      */

530     public Collection JavaDoc<User> findUsers(String JavaDoc field, String JavaDoc query) {
531         List JavaDoc<User> foundUsers = new ArrayList JavaDoc<User>();
532
533         if (!getSearchFields().contains(field)) {
534             return foundUsers;
535         }
536
537         int index = query.indexOf("*");
538         if (index == -1) {
539             Collection JavaDoc<User> users = userManager.getUsers();
540             for (User user : users) {
541                 if (field.equals("Username")) {
542                     try {
543                         foundUsers.add(userManager.getUser(query));
544                         return foundUsers;
545                     }
546                     catch (UserNotFoundException e) {
547                         Log.error("Error getting user", e);
548                     }
549                 }
550                 else if (field.equals("Name")) {
551                     if (query.equalsIgnoreCase(user.getName())) {
552                         foundUsers.add(user);
553                     }
554                 }
555                 else if (field.equals("Email")) {
556                     if (user.getEmail() != null) {
557                         if (query.equalsIgnoreCase(user.getEmail())) {
558                             foundUsers.add(user);
559                         }
560                     }
561                 }
562             }
563         }
564         else {
565             String JavaDoc prefix = query.substring(0, index);
566             Collection JavaDoc<User> users = userManager.getUsers();
567             for (User user : users) {
568                 String JavaDoc userInfo = "";
569                 if (field.equals("Username")) {
570                     userInfo = user.getUsername();
571                 }
572                 else if (field.equals("Name")) {
573                     userInfo = user.getName();
574                 }
575                 else if (field.equals("Email")) {
576                     userInfo = user.getEmail() == null ? "" : user.getEmail();
577                 }
578
579                 if (index < userInfo.length()) {
580                     if (userInfo.substring(0, index).equalsIgnoreCase(prefix)) {
581                         foundUsers.add(user);
582                     }
583                 }
584             }
585         }
586
587         return foundUsers;
588     }
589 }
590
Popular Tags