GroupManager.java 11.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/**
 * $RCSfile$
 * $Revision: 3117 $
 * $Date: 2005-11-25 22:57:29 -0300 (Fri, 25 Nov 2005) $
 *
 * Copyright (C) 2004 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */

package org.jivesoftware.wildfire.group;

import org.jivesoftware.util.*;
import org.jivesoftware.wildfire.XMPPServer;
16
import org.jivesoftware.wildfire.event.GroupEventDispatcher;
17
import org.jivesoftware.wildfire.event.GroupEventListener;
18
import org.jivesoftware.wildfire.user.User;
19 20
import org.xmpp.packet.JID;

21
import java.util.*;
22 23 24 25 26 27 28 29 30

/**
 * Manages groups.
 *
 * @see Group
 * @author Matt Tucker
 */
public class GroupManager {

31 32
    Cache<String, Group> groupCache;
    Cache<String, Collection<Group>> userGroupCache;
33
    // Holds the cache key for the global group in the users groups cache
34
    private final String globalGroupKey = "ALL GROUPS";
35 36
    // Holds the cache key for the shared groups in the users groups cache
    private final String sharedGroupsKey = "SHARED GROUPS";
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
    private GroupProvider provider;

    private static GroupManager instance = new GroupManager();

    /**
     * Returns a singleton instance of GroupManager.
     *
     * @return a GroupManager instance.
     */
    public static GroupManager getInstance() {
        return instance;
    }

    private GroupManager() {
        // Initialize caches.
Gaston Dombiak's avatar
Gaston Dombiak committed
52
        groupCache = CacheManager.initializeCache("Group", "group", 512 * 1024);
53 54

        // A cache for all groups and groups related to a particular user
Gaston Dombiak's avatar
Gaston Dombiak committed
55 56
        String cacheName = "User Group Cache";
        CacheManager.initializeCache(cacheName, "userGroup", 512 * 1024, 1000 * 60 * 60 * 3);
57 58
        userGroupCache = CacheManager.getCache(cacheName);

59 60 61 62 63
        // Load a group provider.
        String className = JiveGlobals.getXMLProperty("provider.group.className",
                "org.jivesoftware.wildfire.group.DefaultGroupProvider");
        try {
            Class c = ClassUtils.forName(className);
64
            provider = (GroupProvider) c.newInstance();
65 66 67 68 69
        }
        catch (Exception e) {
            Log.error("Error loading group provider: " + className, e);
            provider = new DefaultGroupProvider();
        }
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

        GroupEventDispatcher.addListener(new GroupEventListener() {
            public void groupCreated(Group group, Map params) {
                userGroupCache.clear();
            }

            public void groupDeleting(Group group, Map params) {
                userGroupCache.clear();
            }

            public void groupModified(Group group, Map params) {
                /* Ignore */
            }

            public void memberAdded(Group group, Map params) {
                userGroupCache.clear();
            }

            public void memberRemoved(Group group, Map params) {
                userGroupCache.clear();
            }

            public void adminAdded(Group group, Map params) {
                userGroupCache.clear();
            }

            public void adminRemoved(Group group, Map params) {
                userGroupCache.clear();
            }
        });
100 101 102 103 104 105 106 107 108 109 110
    }

    /**
     * Factory method for creating a new Group. A unique name is the only required field.
     *
     * @param name the new and unique name for the group.
     * @return a new Group.
     * @throws GroupAlreadyExistsException if the group name already exists in the system.
     */
    public Group createGroup(String name) throws GroupAlreadyExistsException {
        synchronized (name.intern()) {
111
            Group newGroup;
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
            try {
                getGroup(name);
                // The group already exists since now exception, so:
                throw new GroupAlreadyExistsException();
            }
            catch (GroupNotFoundException unfe) {
                // The group doesn't already exist so we can create a new group
                newGroup = provider.createGroup(name);
                groupCache.put(name, newGroup);

                // Fire event.
                GroupEventDispatcher.dispatchEvent(newGroup,
                        GroupEventDispatcher.EventType.group_created, Collections.emptyMap());
            }
            return newGroup;
        }
    }

    /**
     * Returns a Group by name.
     *
     * @param name The name of the group to retrieve
     * @return The group corresponding to that name
     * @throws GroupNotFoundException if the group does not exist.
     */
    public Group getGroup(String name) throws GroupNotFoundException {
138
        Group group = groupCache.get(name);
139 140 141
        // If ID wan't found in cache, load it up and put it there.
        if (group == null) {
            synchronized (name.intern()) {
142
                group = groupCache.get(name);
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
                // If ID wan't found in cache, load it up and put it there.
                if (group == null) {
                    group = provider.getGroup(name);
                    groupCache.put(name, group);
                }
            }
        }
        return group;
    }

    /**
     * Deletes a group from the system.
     *
     * @param group the group to delete.
     */
    public void deleteGroup(Group group) {
        // Fire event.
        GroupEventDispatcher.dispatchEvent(group, GroupEventDispatcher.EventType.group_deleting,
                Collections.emptyMap());

        // Delete the group.
        provider.deleteGroup(group.getName());

        // Expire all relevant caches.
        groupCache.remove(group.getName());
    }

    /**
     * Deletes a user from all the groups where he/she belongs. The most probable cause
     * for this request is that the user has been deleted from the system.
     *
     * TODO: remove this method and use events isntead.
     *
     * @param user the deleted user from the system.
     */
    public void deleteUser(User user) {
        JID userJID = XMPPServer.getInstance().createJID(user.getUsername(), null);
        for (Group group : getGroups(userJID)) {
            if (group.getAdmins().contains(userJID)) {
                group.getAdmins().remove(userJID);
            }
            else {
                group.getMembers().remove(userJID);
            }
        }
188
        userGroupCache.clear();
189 190 191 192 193 194 195 196
    }

    /**
     * Returns the total number of groups in the system.
     *
     * @return the total number of groups.
     */
    public int getGroupCount() {
197 198 199 200 201
        Collection<Group> groups = userGroupCache.get(globalGroupKey);
        if(groups == null) {
            return provider.getGroupCount();
        }
        return groups.size();
202 203 204 205 206 207 208 209
    }

    /**
     * Returns an unmodifiable Collection of all groups in the system.
     *
     * @return an unmodifiable Collection of all groups.
     */
    public Collection<Group> getGroups() {
Alex Wenckus's avatar
Alex Wenckus committed
210 211 212 213 214 215 216 217 218
        synchronized (globalGroupKey) {
            Collection<Group> groups = userGroupCache.get(globalGroupKey);
            if (groups == null) {
                groups = provider.getGroups();
                // Add to cache and ensure correct identity
                groups = cacheAndEnsureIdentity(groups);
                userGroupCache.put(globalGroupKey, groups);
            }
            return groups;
219
        }
220 221
    }

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
    /**
     * Returns an unmodifiable Collection of all shared groups in the system.
     *
     * @return an unmodifiable Collection of all shared groups.
     */
    public Collection<Group> getSharedGroups() {
        synchronized (sharedGroupsKey) {
            Collection<Group> groups = userGroupCache.get(sharedGroupsKey);
            if (groups == null) {
                Set<String> groupsNames = Group.getSharedGroupsNames();
                groups = provider.getGroups(groupsNames);
                // Add to cache and ensure correct identity
                groups = cacheAndEnsureIdentity(groups);
                userGroupCache.put(sharedGroupsKey, groups);
            }
            return groups;
        }
    }

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
    /**
     * Returns an iterator for all groups according to a filter.
     * <p/>
     * This is useful to support
     * pagination in a GUI where you may only want to display a certain
     * number of results per page. It is possible that the
     * number of results returned will be less than that specified by
     * numResults if numResults is greater than the number of records left in
     * the system to display.
     *
     * @param startIndex start index in results.
     * @param numResults number of results to return.
     * @return an Iterator for all groups in the specified range.
     */
    public Collection<Group> getGroups(int startIndex, int numResults) {
256 257 258 259
        Collection<Group> groups = provider.getGroups(startIndex, numResults);
        // Add to cache and ensure correct identity
        groups = cacheAndEnsureIdentity(groups);
        return groups;
260 261
    }

262 263 264 265 266 267 268 269 270 271
    /**
     * Returns an iterator for all groups that the User is a member of.
     *
     * @param user the user.
     * @return all groups the user belongs to.
     */
    public Collection<Group> getGroups(User user) {
        return getGroups(XMPPServer.getInstance().createJID(user.getUsername(), null));
    }

272 273 274 275 276 277 278
    /**
     * Returns an iterator for all groups that the entity with the specified JID is a member of.
     *
     * @param user the JID of the entity to get a list of groups for.
     * @return all groups that an entity belongs to.
     */
    public Collection<Group> getGroups(JID user) {
Alex Wenckus's avatar
Alex Wenckus committed
279 280 281 282 283 284 285 286 287 288
        String userID = user.toString();
        synchronized (userID.intern()) {
            Collection<Group> groups = userGroupCache.get(userID);
            if (groups == null) {
                groups = provider.getGroups(user);
                // Add to cache and ensure correct identity
                groups = cacheAndEnsureIdentity(groups);
                userGroupCache.put(userID, groups);
            }
            return groups;
289
        }
290 291
    }

292 293 294 295 296 297 298 299 300 301
    /**
     * Returns the configured group provider. Note that this method has special access
     * privileges since only a few certain classes need to access the provider directly.
     *
     * @return the group provider.
     */
    GroupProvider getProvider() {
        return provider;
    }

302 303 304 305 306 307 308 309 310 311 312
    /**
     * Caches groups present in the specified collection that are not already cached and
     * ensures correct identity of already cached groups.
     *
     * @param groups loaded groups from the backend store.
     * @return a list containing the correct and valid groups (i.e. ensuring identity).
     */
    private Collection<Group> cacheAndEnsureIdentity(Collection<Group> groups) {
        Collection<Group> answer = new ArrayList<Group>(groups.size());
        for (Group group : groups) {
            synchronized (group.getName().intern()) {
313
                Group existingGroup = groupCache.get(group.getName());
314 315 316 317 318 319 320 321 322 323 324 325
                if (existingGroup == null) {
                    // Add loaded group to the cache
                    groupCache.put(group.getName(), group);
                    answer.add(group);
                }
                else {
                    // Replace loaded group with the cached one to ensure correct identity
                    answer.add(existingGroup);
                }
            }
        }
        return answer;
326 327
    }
}