GroupManager.java 11.5 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.
52 53 54
        String cacheName = "Group";
        CacheManager.initializeCache(cacheName, "group", 128 * 1024);
        groupCache = CacheManager.getCache(cacheName);
55 56 57 58 59 60

        // A cache for all groups and groups related to a particular user
        cacheName = "User Group Cache";
        CacheManager.initializeCache(cacheName, "userGroup", 128 * 1024, 1000 * 60 * 60 * 3);
        userGroupCache = CacheManager.getCache(cacheName);

61 62 63 64 65
        // Load a group provider.
        String className = JiveGlobals.getXMLProperty("provider.group.className",
                "org.jivesoftware.wildfire.group.DefaultGroupProvider");
        try {
            Class c = ClassUtils.forName(className);
66
            provider = (GroupProvider) c.newInstance();
67 68 69 70 71
        }
        catch (Exception e) {
            Log.error("Error loading group provider: " + className, e);
            provider = new DefaultGroupProvider();
        }
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 100 101

        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();
            }
        });
102 103 104 105 106 107 108 109 110 111 112
    }

    /**
     * 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()) {
113
            Group newGroup;
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
            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 {
140
        Group group = groupCache.get(name);
141 142 143
        // If ID wan't found in cache, load it up and put it there.
        if (group == null) {
            synchronized (name.intern()) {
144
                group = groupCache.get(name);
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 188 189
                // 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);
            }
        }
190
        userGroupCache.clear();
191 192 193 194 195 196 197 198
    }

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

    /**
     * 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
212 213 214 215 216 217 218 219 220
        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;
221
        }
222 223
    }

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
    /**
     * 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;
        }
    }

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
    /**
     * 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) {
258 259 260 261
        Collection<Group> groups = provider.getGroups(startIndex, numResults);
        // Add to cache and ensure correct identity
        groups = cacheAndEnsureIdentity(groups);
        return groups;
262 263
    }

264 265 266 267 268 269 270 271 272 273
    /**
     * 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));
    }

274 275 276 277 278 279 280
    /**
     * 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
281 282 283 284 285 286 287 288 289 290
        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;
291
        }
292 293
    }

294 295 296 297 298 299 300 301 302 303
    /**
     * 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;
    }

304 305 306 307 308 309 310 311 312 313 314
    /**
     * 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()) {
315
                Group existingGroup = groupCache.get(group.getName());
316 317 318 319 320 321 322 323 324 325 326 327
                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;
328 329
    }
}