KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > roster > Roster


1 /**
2  * $RCSfile: Roster.java,v $
3  * $Revision: 1.21 $
4  * $Date: 2005/06/28 16:44:08 $
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.roster;
13
14 import org.jivesoftware.messenger.auth.UnauthorizedException;
15 import org.jivesoftware.messenger.user.UserNotFoundException;
16 import org.jivesoftware.messenger.user.UserAlreadyExistsException;
17 import org.jivesoftware.messenger.user.User;
18 import org.jivesoftware.messenger.user.UserManager;
19 import org.jivesoftware.messenger.*;
20 import org.jivesoftware.messenger.group.GroupManager;
21 import org.jivesoftware.messenger.group.Group;
22 import org.jivesoftware.util.Cacheable;
23 import org.jivesoftware.util.CacheSizes;
24 import org.jivesoftware.util.Log;
25 import org.jivesoftware.util.JiveConstants;
26 import org.jivesoftware.database.JiveID;
27 import org.xmpp.packet.*;
28
29 import java.util.*;
30 import java.util.concurrent.ConcurrentHashMap JavaDoc;
31
32 /**
33  * <p>A roster is a list of users that the user wishes to know if they are online.</p>
34  * <p>Rosters are similar to buddy groups in popular IM clients. The Roster class is
35  * a representation of the roster data.<p/>
36  *
37  * <p>Updates to this roster is effectively a change to the user's roster. To reflect this,
38  * the changes to this class will automatically update the persistently stored roster, as well as
39  * send out update announcements to all logged in user sessions.</p>
40  *
41  * @author Gaston Dombiak
42  */

43 @JiveID(JiveConstants.ROSTER)
44 public class Roster implements Cacheable {
45
46     /**
47      * <p>Roster item cache - table: key jabberid string; value roster item.</p>
48      */

49     protected ConcurrentHashMap JavaDoc<String JavaDoc, RosterItem> rosterItems = new ConcurrentHashMap JavaDoc<String JavaDoc, RosterItem>();
50
51     private RosterItemProvider rosterItemProvider;
52     private String JavaDoc username;
53     private SessionManager sessionManager;
54     private XMPPServer server = XMPPServer.getInstance();
55     private RoutingTable routingTable;
56     private PresenceManager presenceManager;
57     /**
58      * Note: Used only for shared groups logic.
59      */

60     private RosterManager rosterManager;
61
62
63     /**
64      * <p>Create a roster for the given user, pulling the existing roster items
65      * out of the backend storage provider. The roster will also include items that
66      * belong to the user's shared groups.</p>
67      *
68      * <p>RosterItems that ONLY belong to shared groups won't be persistent unless the user
69      * explicitly subscribes to the contact's presence, renames the contact in his roster or adds
70      * the item to a personal group.</p>
71      *
72      * @param username The username of the user that owns this roster
73      */

74     public Roster(String JavaDoc username) {
75         presenceManager = XMPPServer.getInstance().getPresenceManager();
76         rosterManager = XMPPServer.getInstance().getRosterManager();
77         sessionManager = SessionManager.getInstance();
78         this.username = username;
79
80         // Get the shared groups of this user
81
Collection<Group> sharedGroups = null;
82         Collection<Group> userGroups = null;
83         try {
84             User rosterUser = UserManager.getInstance().getUser(getUsername());
85             sharedGroups = rosterManager.getSharedGroups(rosterUser);
86             userGroups = GroupManager.getInstance().getGroups(rosterUser);
87         }
88         catch (UserNotFoundException e) {
89             sharedGroups = new ArrayList<Group>();
90         }
91
92         // Add RosterItems that belong to the personal roster
93
rosterItemProvider = RosterItemProvider.getInstance();
94         Iterator items = rosterItemProvider.getItems(username);
95         while (items.hasNext()) {
96             RosterItem item = (RosterItem)items.next();
97             // Check if the item (i.e. contact) belongs to a shared group of the user. Add the
98
// shared group (if any) to this item
99
for (Group group : sharedGroups) {
100                 if (group.isUser(item.getJid().getNode())) {
101                     // TODO Group name conflicts are not being considered (do we need this?)
102
item.addSharedGroup(group);
103                 }
104             }
105             rosterItems.put(item.getJid().toBareJID(), item);
106         }
107         // Add RosterItems that belong only to shared groups
108
Map JavaDoc<JID,List<Group>> sharedUsers = getSharedUsers(sharedGroups);
109         for (JID jid : sharedUsers.keySet()) {
110             try {
111                 Collection<Group> itemGroups = new ArrayList<Group>();
112                 User user = UserManager.getInstance().getUser(jid.getNode());
113                 String JavaDoc nickname = "".equals(user.getName()) ? jid.getNode() : user.getName();
114                 RosterItem item = new RosterItem(jid, RosterItem.SUB_TO, RosterItem.ASK_NONE,
115                         RosterItem.RECV_NONE, nickname , null);
116                 // Add the shared groups to the new roster item
117
for (Group group : sharedUsers.get(jid)) {
118                     if (group.isUser(jid.getNode())) {
119                         item.addSharedGroup(group);
120                         itemGroups.add(group);
121                     }
122                     else {
123                         item.addInvisibleSharedGroup(group);
124                     }
125                 }
126                 // Set subscription type to BOTH if the roster user belongs to a shared group
127
// that is mutually visible with a shared group of the new roster item
128
if (rosterManager.hasMutualVisibility(username, userGroups, jid.getNode(),
129                         itemGroups)) {
130                     item.setSubStatus(RosterItem.SUB_BOTH);
131                 }
132                 else {
133                     // Set subscription type to FROM if the contact does not belong to any of
134
// the associated shared groups
135
boolean belongsToGroup = false;
136                     for (Group group : sharedUsers.get(jid)) {
137                         if (group.isUser(jid.getNode())) {
138                             belongsToGroup = true;
139                         }
140                     }
141                     if (!belongsToGroup) {
142                         item.setSubStatus(RosterItem.SUB_FROM);
143                     }
144                 }
145                 rosterItems.put(item.getJid().toBareJID(), item);
146             }
147             catch (UserNotFoundException e) {
148                 Log.error("Groups (" + sharedUsers.get(jid) + ") include non-existent username (" +
149                         jid.getNode() +
150                         ")");
151             }
152         }
153     }
154
155     /**
156      * Returns true if the specified user is a member of the roster, false otherwise.
157      *
158      * @param user the user object to check.
159      * @return true if the specified user is a member of the roster, false otherwise.
160      */

161     public boolean isRosterItem(JID user) {
162         return rosterItems.containsKey(user.toBareJID());
163     }
164
165     /**
166      * Returns a collection of users in this roster.
167      *
168      * @return a collection of users in this roster.
169      */

170     public Collection<RosterItem> getRosterItems() {
171         return Collections.unmodifiableCollection(rosterItems.values());
172     }
173
174     /**
175      * Returns the total number of users in the roster.
176      *
177      * @return the number of online users in the roster.
178      */

179     public int getTotalRosterItemCount() {
180         return rosterItems.size();
181     }
182
183     /**
184      * Gets a user from the roster. If the roster item does not exist, an empty one is created.
185      * The new roster item is not stored in the roster until it is added using
186      * addRosterItem().
187      *
188      * @param user the XMPPAddress for the roster item to retrieve
189      * @return The roster item associated with the user XMPPAddress
190      */

191     public RosterItem getRosterItem(JID user) throws UserNotFoundException {
192         RosterItem item = rosterItems.get(user.toBareJID());
193         if (item == null) {
194             throw new UserNotFoundException(user.toBareJID());
195         }
196         return item;
197     }
198
199     /**
200      * Create a new item to the roster. Roster items may not be created that contain the same user
201      * address as an existing item.
202      *
203      * @param user the item to add to the roster.
204      */

205     public RosterItem createRosterItem(JID user) throws UserAlreadyExistsException,
206             SharedGroupException {
207         return createRosterItem(user, null, null);
208     }
209
210     /**
211      * Create a new item to the roster. Roster items may not be created that contain the same user
212      * address as an existing item.
213      *
214      * @param user the item to add to the roster.
215      * @param nickname The nickname for the roster entry (can be null)
216      * @param groups The list of groups to assign this roster item to (can be null)
217      */

218     public RosterItem createRosterItem(JID user, String JavaDoc nickname, List<String JavaDoc> groups)
219             throws UserAlreadyExistsException, SharedGroupException {
220         RosterItem item = provideRosterItem(user, nickname, groups);
221         rosterItems.put(item.getJid().toBareJID(), item);
222         return item;
223     }
224
225     /**
226      * Create a new item to the roster based as a copy of the given item.
227      * Roster items may not be created that contain the same user address
228      * as an existing item in the roster.
229      *
230      * @param item the item to copy and add to the roster.
231      */

232     public void createRosterItem(org.xmpp.packet.Roster.Item item)
233             throws UserAlreadyExistsException, SharedGroupException {
234         RosterItem rosterItem = provideRosterItem(item);
235         rosterItems.put(item.getJID().toBareJID(), rosterItem);
236     }
237
238     /**
239      * <p>Generate a new RosterItem for use with createRosterItem.<p>
240      *
241      * @param item The item to copy settings for the new item in this roster
242      * @return The newly created roster items ready to be stored by the Roster item's hash table
243      */

244     protected RosterItem provideRosterItem(org.xmpp.packet.Roster.Item item)
245             throws UserAlreadyExistsException, SharedGroupException {
246         return provideRosterItem(item.getJID(), item.getName(),
247                 new ArrayList<String JavaDoc>(item.getGroups()));
248     }
249
250     /**
251      * <p>Generate a new RosterItem for use with createRosterItem.<p>
252      *
253      * @param user The roster jid address to create the roster item for
254      * @param nickname The nickname to assign the item (or null for none)
255      * @param groups The groups the item belongs to (or null for none)
256      * @return The newly created roster items ready to be stored by the Roster item's hash table
257      */

258     protected RosterItem provideRosterItem(JID user, String JavaDoc nickname, List<String JavaDoc> groups)
259             throws UserAlreadyExistsException, SharedGroupException {
260         if (groups != null && !groups.isEmpty()) {
261             // Raise an error if the groups the item belongs to include a shared group
262
Collection<Group> sharedGroups = GroupManager.getInstance().getGroups();
263             for (String JavaDoc group : groups) {
264                 for (Group sharedGroup : sharedGroups) {
265                     if (group.equals(sharedGroup.getProperties().get("sharedRoster.displayName"))) {
266                         throw new SharedGroupException("Cannot add an item to a shared group");
267                     }
268                 }
269             }
270         }
271         org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
272         roster.setType(IQ.Type.set);
273         org.xmpp.packet.Roster.Item item = roster.addItem(user, nickname, null,
274                 org.xmpp.packet.Roster.Subscription.none, groups);
275
276         RosterItem rosterItem = rosterItemProvider.createItem(username, new RosterItem(item));
277
278         // Broadcast the roster push to the user
279
broadcast(roster);
280
281         return rosterItem;
282     }
283
284     /**
285      * Update an item that is already in the roster.
286      *
287      * @param item the item to update in the roster.
288      * @throws UserNotFoundException If the roster item for the given user doesn't already exist
289      */

290     public void updateRosterItem(RosterItem item) throws UserNotFoundException {
291         if (rosterItems.putIfAbsent(item.getJid().toBareJID(), item) == null) {
292             rosterItems.remove(item.getJid().toBareJID());
293             throw new UserNotFoundException(item.getJid().toBareJID());
294         }
295         // If the item only had shared groups before this update then make it persistent
296
if (item.isShared() && item.getID() == 0) {
297             try {
298                 rosterItemProvider.createItem(username, item);
299             }
300             catch (UserAlreadyExistsException e) {
301                 // Do nothing. We shouldn't be here.
302
}
303         }
304         else {
305             // Update the backend data store
306
rosterItemProvider.updateItem(username, item);
307         }
308         // broadcast roster update
309
//if (item.getAskStatus() != RosterItem.ASK_NONE) {
310
broadcast(item, true);
311         //}
312
/*if (item.getSubStatus() == RosterItem.SUB_BOTH || item.getSubStatus() == RosterItem.SUB_TO) {
313             probePresence(item.getJid());
314         }*/

315     }
316
317     /**
318      * Remove a user from the roster.
319      *
320      * @param user the user to remove from the roster.
321      * @param doChecking flag that indicates if checkings should be done before deleting the user.
322      * @return The roster item being removed or null if none existed
323      * @throws SharedGroupException if the user to remove belongs to a shared group
324      */

325     public RosterItem deleteRosterItem(JID user, boolean doChecking) throws SharedGroupException {
326         // Answer an error if user (i.e. contact) to delete belongs to a shared group
327
RosterItem itemToRemove = rosterItems.get(user.toBareJID());
328         if (doChecking && itemToRemove != null && itemToRemove.isShared()) {
329             throw new SharedGroupException("Cannot remove contact that belongs to a shared group");
330         }
331
332         RosterItem.SubType subType = itemToRemove.getSubStatus();
333         // Cancel any existing presence subscription between the user and the contact
334
if (subType == RosterItem.SUB_TO || subType == RosterItem.SUB_BOTH) {
335             Presence presence = new Presence();
336             presence.setFrom(server.createJID(username, null));
337             presence.setTo(itemToRemove.getJid());
338             presence.setType(Presence.Type.unsubscribe);
339             server.getPacketRouter().route(presence);
340         }
341         // cancel any existing presence subscription between the contact and the user
342
if (subType == RosterItem.SUB_FROM || subType == RosterItem.SUB_BOTH) {
343             Presence presence = new Presence();
344             presence.setFrom(server.createJID(username, null));
345             presence.setTo(itemToRemove.getJid());
346             presence.setType(Presence.Type.unsubscribed);
347             server.getPacketRouter().route(presence);
348         }
349         // If removing the user was successful, remove the user from the subscriber list:
350
RosterItem item = rosterItems.remove(user.toBareJID());
351         if (item != null) {
352             // Delete the item from the provider if the item is persistent. RosteItems that only
353
// belong to shared groups won't be persistent
354
if (item.getID() > 0) {
355                 // If removing the user was successful, remove the user from the backend store
356
rosterItemProvider.deleteItem(username, item.getID());
357             }
358
359             // Broadcast the update to the user
360
org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
361             roster.setType(IQ.Type.set);
362             roster.addItem(user, org.xmpp.packet.Roster.Subscription.remove);
363             broadcast(roster);
364         }
365         return item;
366
367     }
368
369     /**
370      * <p>Return the username of the user or chatbot that owns this roster.</p>
371      *
372      * @return the username of the user or chatbot that owns this roster
373      */

374     public String JavaDoc getUsername() {
375         return username;
376     }
377
378     /**
379      * <p>Obtain a 'roster reset', a snapshot of the full cached roster as an Roster.</p>
380      *
381      * @return The roster reset (snapshot) as an Roster
382      */

383     public org.xmpp.packet.Roster getReset() {
384         org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
385
386         // Add the roster items (includes the personal roster and shared groups) to the answer
387
for (RosterItem item : rosterItems.values()) {
388             // Do not include items with status FROM that exist only because of shared groups
389
if (item.isOnlyShared() && item.getSubStatus() == RosterItem.SUB_FROM) {
390                 continue;
391             }
392             org.xmpp.packet.Roster.Ask ask = getAskStatus(item.getAskStatus());
393             org.xmpp.packet.Roster.Subscription sub = org.xmpp.packet.Roster.Subscription.valueOf(item.getSubStatus()
394                     .getName());
395             // Set the groups to broadcast (include personal and shared groups)
396
List<String JavaDoc> groups = new ArrayList<String JavaDoc>(item.getGroups());
397             if (groups.contains(null)) {
398                 Log.warn("A group is null in roster item: " + item.getJid() + " of user: " +
399                         getUsername());
400             }
401             for (Group sharedGroup : item.getSharedGroups()) {
402                 String JavaDoc displayName = sharedGroup.getProperties().get("sharedRoster.displayName");
403                 if (displayName != null) {
404                     groups.add(displayName);
405                 }
406                 else {
407                     // Do not add the shared group if it does not have a displayName.
408
Log.warn("Found shared group: " + sharedGroup.getName() +
409                             " with no displayName");
410                 }
411             }
412             //if (item.getAskStatus() != RosterItem.ASK_NONE) {
413
roster.addItem(item.getJid(), item.getNickname(), ask, sub, groups);
414             //}
415
}
416         return roster;
417     }
418
419     private org.xmpp.packet.Roster.Ask getAskStatus(RosterItem.AskType askType) {
420         if (askType == null || "".equals(askType.getName())) {
421             return null;
422         }
423         return org.xmpp.packet.Roster.Ask.valueOf(askType.getName());
424     }
425
426     /**
427      * <p>Broadcast the presence update to all subscribers of the roter.</p>
428      * <p/>
429      * <p>Any presence change typically results in a broadcast to the roster members.</p>
430      *
431      * @param packet The presence packet to broadcast
432      */

433     public void broadcastPresence(Presence packet) {
434         if (routingTable == null) {
435             routingTable = XMPPServer.getInstance().getRoutingTable();
436         }
437         if (routingTable == null) {
438             return;
439         }
440         for (RosterItem item : rosterItems.values()) {
441             if (item.getSubStatus() == RosterItem.SUB_BOTH
442                     || item.getSubStatus() == RosterItem.SUB_FROM) {
443                 JID searchNode = new JID(item.getJid().getNode(), item.getJid().getDomain(), null);
444                 Iterator sessions = routingTable.getRoutes(searchNode);
445                 packet.setTo(item.getJid());
446                 while (sessions.hasNext()) {
447                     ChannelHandler session = (ChannelHandler)sessions.next();
448                     try {
449                         session.process(packet);
450                     }
451                     catch (Exception JavaDoc e) {
452                         // Ignore any problems with sending - theoretically
453
// only happens if session has been closed
454
}
455                 }
456             }
457         }
458     }
459
460     /**
461      * Returns the list of users that belong ONLY to a shared group of this user. If the contact
462      * belongs to the personal roster and a shared group then it wont' be included in the answer.
463      *
464      * @param sharedGroups the shared groups of this user.
465      * @return the list of users that belong ONLY to a shared group of this user.
466      */

467     private Map JavaDoc<JID,List<Group>> getSharedUsers(Collection<Group> sharedGroups) {
468         // Get the users to process from the shared groups. Users that belong to different groups
469
// will have one entry in the map associated with all the groups
470
Map JavaDoc<JID,List<Group>> sharedGroupUsers = new HashMap JavaDoc<JID,List<Group>>();
471         for (Group group : sharedGroups) {
472             // Get all the users that should be in this roster
473
Collection<String JavaDoc> users = rosterManager.getSharedUsersForRoster(group, this);
474             // Add the users of the group to the general list of users to process
475
for (String JavaDoc user : users) {
476                 // Add the user to the answer if the user doesn't belong to the personal roster
477
// (since we have already added the user to the answer)
478
JID jid = XMPPServer.getInstance().createJID(user, null);
479                 if (!isRosterItem(jid) && !getUsername().equals(user)) {
480                     List<Group> groups = sharedGroupUsers.get(jid);
481                     if (groups == null) {
482                         groups = new ArrayList<Group>();
483                         sharedGroupUsers.put(jid, groups);
484                     }
485                     groups.add(group);
486                 }
487             }
488         }
489         return sharedGroupUsers;
490     }
491
492     private void broadcast(org.xmpp.packet.Roster roster) {
493         JID recipient = server.createJID(username, null);
494         roster.setTo(recipient);
495         if (sessionManager == null) {
496             sessionManager = SessionManager.getInstance();
497         }
498         try {
499             sessionManager.userBroadcast(username, roster);
500         }
501         catch (UnauthorizedException e) {
502             // Do nothing. We should never end here.
503
}
504     }
505
506     /**
507      * Broadcasts the RosterItem to all the connected resources of this user. Due to performance
508      * optimizations and due to some clients errors that are showing items with subscription status
509      * FROM we added a flag that indicates if a roster items that exists only because of a shared
510      * group with subscription status FROM will not be sent.
511      *
512      * @param item the item to broadcast.
513      * @param optimize true indicates that items that exists only because of a shared
514      * group with subscription status FROM will not be sent
515      */

516     void broadcast(RosterItem item, boolean optimize) {
517         // Do not broadcast items with status FROM that exist only because of shared groups
518
if (optimize && item.isOnlyShared() && item.getSubStatus() == RosterItem.SUB_FROM) {
519             return;
520         }
521         // Set the groups to broadcast (include personal and shared groups)
522
List<String JavaDoc> groups = new ArrayList<String JavaDoc>(item.getGroups());
523         for (Group sharedGroup : item.getSharedGroups()) {
524             groups.add(sharedGroup.getProperties().get("sharedRoster.displayName"));
525         }
526
527         org.xmpp.packet.Roster roster = new org.xmpp.packet.Roster();
528         roster.setType(IQ.Type.set);
529         roster.addItem(item.getJid(), item.getNickname(),
530                 getAskStatus(item.getAskStatus()),
531                 org.xmpp.packet.Roster.Subscription.valueOf(item.getSubStatus().getName()),
532                 groups);
533         broadcast(roster);
534     }
535
536     /**
537      * Sends a presence probe to the probee for each connected resource of this user.
538      */

539     private void probePresence(JID probee) {
540         for (ClientSession session : sessionManager.getSessions(username)) {
541             presenceManager.probePresence(session.getAddress(), probee);
542         }
543     }
544
545     public int getCachedSize() {
546         // Approximate the size of the object in bytes by calculating the size
547
// of each field.
548
int size = 0;
549         size += CacheSizes.sizeOfObject(); // overhead of object
550
size += CacheSizes.sizeOfCollection(rosterItems.values()); // roster item cache
551
size += CacheSizes.sizeOfString(username); // username
552
return size;
553     }
554
555     /**
556      * Update the roster since a group user has been added to a shared group. Create a new
557      * RosterItem if the there doesn't exist an item for the added user. The new RosterItem won't be
558      * saved to the backend store unless the user explicitly subscribes to the contact's presence,
559      * renames the contact in his roster or adds the item to a personal group. Otherwise the shared
560      * group will be added to the shared groups lists. In any case an update broadcast will be sent
561      * to all the users logged resources.
562      *
563      * @param group the shared group where the user was added.
564      * @param addedUser the contact to update in the roster.
565      */

566     void addSharedUser(Group group, String JavaDoc addedUser) {
567         boolean newItem = false;
568         RosterItem item = null;
569         JID jid = XMPPServer.getInstance().createJID(addedUser, "");
570         try {
571             // Get the RosterItem for the *local* user to add
572
item = getRosterItem(jid);
573             // Do nothing if the item already includes the shared group
574
if (item.getSharedGroups().contains(group)) {
575                 return;
576             }
577             newItem = false;
578         }
579         catch (UserNotFoundException e) {
580             try {
581                 // Create a new RosterItem for this new user
582
User user = UserManager.getInstance().getUser(addedUser);
583                 String JavaDoc nickname = "".equals(user.getName()) ? jid.getNode() : user.getName();
584                 item =
585                         new RosterItem(jid, RosterItem.SUB_BOTH, RosterItem.ASK_NONE,
586                                 RosterItem.RECV_NONE, nickname, null);
587                 // Add the new item to the list of items
588
rosterItems.put(item.getJid().toBareJID(), item);
589                 newItem = true;
590             }
591             catch (UserNotFoundException ex) {
592                 Log.error("Group (" + group.getName() + ") includes non-existent username (" +
593                         addedUser +
594                         ")");
595             }
596         }
597         // Update the subscription of the item **based on the item groups**
598
if (newItem || item.isOnlyShared()) {
599             Collection<Group> userGroups = null;
600             Collection<Group> sharedGroups = new ArrayList<Group>();
601             try {
602                 User rosterUser = UserManager.getInstance().getUser(getUsername());
603                 GroupManager groupManager = GroupManager.getInstance();
604                 userGroups = groupManager.getGroups(rosterUser);
605                 sharedGroups.addAll(item.getSharedGroups());
606                 // Add the new group to the list of groups to check
607
sharedGroups.add(group);
608                 // Set subscription type to BOTH if the roster user belongs to a shared group
609
// that is mutually visible with a shared group of the new roster item
610
if (rosterManager.hasMutualVisibility(getUsername(), userGroups, jid.getNode(),
611                         sharedGroups)) {
612                     item.setSubStatus(RosterItem.SUB_BOTH);
613                 }
614                 // Update the subscription status depending on the group membership of the new
615
// user and this user
616
else if (group.isUser(addedUser) && !group.isUser(getUsername())) {
617                     item.setSubStatus(RosterItem.SUB_TO);
618                 }
619                 else if (!group.isUser(addedUser) && group.isUser(getUsername())) {
620                     item.setSubStatus(RosterItem.SUB_FROM);
621                 }
622             }
623             catch (UserNotFoundException e) {
624             }
625         }
626
627         // Add the shared group to the list of shared groups
628
if (item.getSubStatus() != RosterItem.SUB_FROM) {
629             item.addSharedGroup(group);
630         }
631         else {
632             item.addInvisibleSharedGroup(group);
633         }
634         // Brodcast to all the user resources of the updated roster item
635
broadcast(item, true);
636         // Probe the presence of the new group user
637
if (item.getSubStatus() == RosterItem.SUB_BOTH || item.getSubStatus() == RosterItem.SUB_TO) {
638             probePresence(item.getJid());
639         }
640     }
641
642     /**
643      * Adds a new contact that belongs to a certain list of groups to the roster. Depending on
644      * the contact's groups and this user's groups, the presence subscription of the roster item may
645      * vary.
646      *
647      * @param addedUser the new contact to add to the roster
648      * @param groups the groups where the contact is a member
649      */

650     void addSharedUser(String JavaDoc addedUser, Collection<Group> groups, Group addedGroup) {
651         boolean newItem = false;
652         RosterItem item = null;
653         JID jid = XMPPServer.getInstance().createJID(addedUser, "");
654         try {
655             // Get the RosterItem for the *local* user to add
656
item = getRosterItem(jid);
657             newItem = false;
658         }
659         catch (UserNotFoundException e) {
660             try {
661                 // Create a new RosterItem for this new user
662
User user = UserManager.getInstance().getUser(addedUser);
663                 String JavaDoc nickname = "".equals(user.getName()) ? jid.getNode() : user.getName();
664                 item =
665                         new RosterItem(jid, RosterItem.SUB_BOTH, RosterItem.ASK_NONE,
666                                 RosterItem.RECV_NONE, nickname, null);
667                 // Add the new item to the list of items
668
rosterItems.put(item.getJid().toBareJID(), item);
669                 newItem = true;
670             }
671             catch (UserNotFoundException ex) {
672                 Log.error("Couldn't find a user with username (" + addedUser + ")");
673             }
674         }
675         // Update the subscription of the item **based on the item groups**
676
if (newItem || item.isOnlyShared()) {
677             Collection<Group> userGroups = null;
678             try {
679                 User rosterUser = UserManager.getInstance().getUser(getUsername());
680                 GroupManager groupManager = GroupManager.getInstance();
681                 userGroups = groupManager.getGroups(rosterUser);
682                 // Set subscription type to BOTH if the roster user belongs to a shared group
683
// that is mutually visible with a shared group of the new roster item
684
if (rosterManager.hasMutualVisibility(getUsername(), userGroups, jid.getNode(),
685                         groups)) {
686                     item.setSubStatus(RosterItem.SUB_BOTH);
687                     for (Group group : groups) {
688                         if (rosterManager.isGroupVisible(group, getUsername())) {
689                             // Add the shared group to the list of shared groups
690
item.addSharedGroup(group);
691                         }
692                     }
693                     // Add to the item the groups of this user that generated a FROM subscription
694
// Note: This FROM subscription is overridden by the BOTH subscription but in
695
// fact there is a TO-FROM relation between these two users that ends up in a
696
// BOTH subscription
697
for (Group group : userGroups) {
698                         if (!group.isUser(addedUser) &&
699                                 rosterManager.isGroupVisible(group, addedUser)) {
700                             // Add the shared group to the list of invisible shared groups
701
item.addInvisibleSharedGroup(group);
702                         }
703                     }
704                 }
705                 else {
706                     // Assume by default that the contact has subscribed from the presence of
707
// this user
708
item.setSubStatus(RosterItem.SUB_FROM);
709                     // Check if the user may see the new contact in a shared group
710
for (Group group : groups) {
711                         if (rosterManager.isGroupVisible(group, getUsername())) {
712                             // Add the shared group to the list of shared groups
713
item.addSharedGroup(group);
714                             item.setSubStatus(RosterItem.SUB_TO);
715                         }
716                     }
717                     if (item.getSubStatus() == RosterItem.SUB_FROM) {
718                         item.addInvisibleSharedGroup(addedGroup);
719                     }
720                 }
721             }
722             catch (UserNotFoundException e) {
723             }
724         }
725         // Brodcast to all the user resources of the updated roster item
726
broadcast(item, true);
727         // Probe the presence of the new group user
728
if (item.getSubStatus() == RosterItem.SUB_BOTH || item.getSubStatus() == RosterItem.SUB_TO) {
729             probePresence(item.getJid());
730         }
731     }
732
733     /**
734      * Update the roster since a group user has been deleted from a shared group. If the RosterItem
735      * (of the deleted contact) exists only because of of the sahred group then the RosterItem will
736      * be deleted physically from the backend store. Otherwise the shared group will be removed from
737      * the shared groups lists. In any case an update broadcast will be sent to all the users
738      * logged resources.
739      *
740      * @param sharedGroup the shared group from where the user was deleted.
741      * @param deletedUser the contact to update in the roster.
742      */

743     void deleteSharedUser(Group sharedGroup, String JavaDoc deletedUser) {
744         JID jid = XMPPServer.getInstance().createJID(deletedUser, "");
745         try {
746             // Get the RosterItem for the *local* user to remove
747
RosterItem item = getRosterItem(jid);
748             int groupSize = item.getSharedGroups().size() + item.getInvisibleSharedGroups().size();
749             if (item.isOnlyShared() && groupSize == 1) {
750                 // Do nothing if the existing shared group is not the sharedGroup to remove
751
if (!item.getSharedGroups().contains(sharedGroup)) {
752                     return;
753                 }
754                 // Delete the roster item from the roster since it exists only because of this
755
// group which is being removed
756
deleteRosterItem(jid, false);
757             }
758             else {
759                 // Remove the removed shared group from the list of shared groups
760
item.removeSharedGroup(sharedGroup);
761                 // Update the subscription of the item based on the remaining groups
762
if (item.isOnlyShared()) {
763                     Collection<Group> userGroups = null;
764                     Collection<Group> sharedGroups = new ArrayList<Group>();
765                     try {
766                         User rosterUser = UserManager.getInstance().getUser(getUsername());
767                         GroupManager groupManager = GroupManager.getInstance();
768                         userGroups = groupManager.getGroups(rosterUser);
769                         sharedGroups.addAll(item.getSharedGroups());
770                         // Set subscription type to BOTH if the roster user belongs to a shared group
771
// that is mutually visible with a shared group of the new roster item
772
if (rosterManager.hasMutualVisibility(getUsername(), userGroups,
773                                 jid.getNode(), sharedGroups)) {
774                             item.setSubStatus(RosterItem.SUB_BOTH);
775                         }
776                         else if (item.getSharedGroups().isEmpty() &&
777                                 !item.getInvisibleSharedGroups().isEmpty()) {
778                             item.setSubStatus(RosterItem.SUB_FROM);
779                         }
780                         else {
781                             item.setSubStatus(RosterItem.SUB_TO);
782                         }
783
784                     }
785                     catch (UserNotFoundException e) {
786                     }
787                 }
788                 // Brodcast to all the user resources of the updated roster item
789
broadcast(item, false);
790             }
791         }
792         catch (SharedGroupException e) {
793             // Do nothing. Checkings are disabled so this exception should never happen.
794
}
795         catch (UserNotFoundException e) {
796             // Do nothing since the contact does not exist in the user's roster. (strange case!)
797
}
798     }
799
800     void deleteSharedUser(String JavaDoc deletedUser, Collection<Group> groups, Group deletedGroup) {
801         JID jid = XMPPServer.getInstance().createJID(deletedUser, "");
802         try {
803             // Get the RosterItem for the *local* user to remove
804
RosterItem item = getRosterItem(jid);
805             int groupSize = item.getSharedGroups().size() + item.getInvisibleSharedGroups().size();
806             if (item.isOnlyShared() && groupSize == 1 &&
807                     // Do not delete the item if deletedUser belongs to a public group since the
808
// subcription status will change
809
!(deletedGroup.isUser(deletedUser) &&
810                     rosterManager.isGroupPublic(deletedGroup))) {
811                 // Delete the roster item from the roster since it exists only because of this
812
// group which is being removed
813
deleteRosterItem(jid, false);
814             }
815             else {
816                 // Remove the shared group from the item if deletedUser does not belong to a
817
// public group
818
if (!(deletedGroup.isUser(deletedUser) &&
819                         rosterManager.isGroupPublic(deletedGroup))) {
820                     item.removeSharedGroup(deletedGroup);
821                 }
822                 // Remove all invalid shared groups from the roster item
823
for (Group group : groups) {
824                     if (!rosterManager.isGroupVisible(group, getUsername())) {
825                         // Remove the shared group from the list of shared groups
826
item.removeSharedGroup(group);
827                     }
828                 }
829
830                 // Update the subscription of the item **based on the item groups**
831
if (item.isOnlyShared()) {
832                     Collection<Group> userGroups = null;
833                     try {
834                         User rosterUser = UserManager.getInstance().getUser(getUsername());
835                         GroupManager groupManager = GroupManager.getInstance();
836                         userGroups = groupManager.getGroups(rosterUser);
837                         // Set subscription type to BOTH if the roster user belongs to a shared group
838
// that is mutually visible with a shared group of the new roster item
839
if (rosterManager.hasMutualVisibility(getUsername(), userGroups,
840                                 jid.getNode(), groups)) {
841                             item.setSubStatus(RosterItem.SUB_BOTH);
842                         }
843                         else {
844                             // Assume by default that the contact has subscribed from the presence of
845
// this user
846
item.setSubStatus(RosterItem.SUB_FROM);
847                             // Check if the user may see the new contact in a shared group
848
for (Group group : groups) {
849                                 if (rosterManager.isGroupVisible(group, getUsername())) {
850                                     item.setSubStatus(RosterItem.SUB_TO);
851                                 }
852                             }
853                         }
854                     }
855                     catch (UserNotFoundException e) {
856                     }
857                 }
858                 // Brodcast to all the user resources of the updated roster item
859
broadcast(item, false);
860             }
861         }
862         catch (SharedGroupException e) {
863             // Do nothing. Checkings are disabled so this exception should never happen.
864
}
865         catch (UserNotFoundException e) {
866             // Do nothing since the contact does not exist in the user's roster. (strange case!)
867
}
868     }
869
870     /**
871      * A shared group of the user has been renamed. Update the existing roster items with the new
872      * name of the shared group and make a roster push for all the available resources.
873      *
874      * @param users group users of the renamed group.
875      */

876     void shareGroupRenamed(Collection<String JavaDoc> users) {
877         for (String JavaDoc user : users) {
878             if (username.equals(user)) {
879                 continue;
880             }
881             RosterItem item = null;
882             JID jid = XMPPServer.getInstance().createJID(user, "");
883             try {
884                 // Get the RosterItem for the *local* user to add
885
item = getRosterItem(jid);
886                 // Brodcast to all the user resources of the updated roster item
887
broadcast(item, true);
888             }
889             catch (UserNotFoundException e) {
890                 // Do nothing since the contact does not exist in the user's roster. (strange case!)
891
}
892         }
893     }
894 }
Popular Tags