Commit 7283255b authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Optimized way shared groups are detected. JM-608

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3623 b35dd754-fafc-0310-a699-88a17e54d16e
parent 38b65e99
......@@ -23,6 +23,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Database implementation of the GroupManager interface.
......@@ -260,6 +261,10 @@ public class DefaultGroupProvider implements GroupProvider {
return new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
}
public Collection<Group> getGroups(Set<String> groupNames) {
return new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
}
public Collection<Group> getGroups(int startIndex, int numResults) {
List<String> groupNames = new ArrayList<String>();
Connection con = null;
......
......@@ -12,20 +12,20 @@
package org.jivesoftware.wildfire.group;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.event.GroupEventDispatcher;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.Cacheable;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.event.GroupEventDispatcher;
import org.xmpp.packet.JID;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.io.*;
/**
* Groups organize users into a single entity for easier management.<p>
......@@ -48,6 +48,9 @@ public class Group implements Cacheable {
"UPDATE jiveGroupProp SET propValue=? WHERE name=? AND groupName=?";
private static final String INSERT_PROPERTY =
"INSERT INTO jiveGroupProp (groupName, name, propValue) VALUES (?, ?, ?)";
private static final String LOAD_SHARED_GROUPS =
"SELECT groupName FROM jiveGroupProp WHERE name='sharedRoster.showInRoster' " +
"AND propValue IS NOT NULL AND propValue <> 'nobody'";
private transient GroupProvider provider;
private transient GroupManager groupManager;
......@@ -58,6 +61,36 @@ public class Group implements Cacheable {
private Set<JID> members;
private Set<JID> administrators;
/**
* Returns the name of the groups that are shared groups.
*
* @return the name of the groups that are shared groups.
*/
static Set<String> getSharedGroupsNames() {
Set<String> groupNames = new HashSet<String>();
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_SHARED_GROUPS);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
groupNames.add(rs.getString(1));
}
rs.close();
}
catch (SQLException sqle) {
Log.error(sqle);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
}
return groupNames;
}
/**
* Constructs a new group. Note: this constructor is intended for implementors of the
* {@link GroupProvider} interface. To create a new group, use the
......@@ -384,10 +417,6 @@ public class Group implements Cacheable {
}
}
}
// Remove the cache item for the groups that the user is in.
groupManager.groupCache.remove(user);
return true;
}
return false;
......
......@@ -18,10 +18,7 @@ import org.jivesoftware.wildfire.event.GroupEventListener;
import org.jivesoftware.wildfire.user.User;
import org.xmpp.packet.JID;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.*;
/**
* Manages groups.
......@@ -33,8 +30,10 @@ public class GroupManager {
Cache<String, Group> groupCache;
Cache<String, Collection<Group>> userGroupCache;
// Holds the place for the global group in the global gruop cache
// Holds the cache key for the global group in the users groups cache
private final String globalGroupKey = "ALL GROUPS";
// Holds the cache key for the shared groups in the users groups cache
private final String sharedGroupsKey = "SHARED GROUPS";
private GroupProvider provider;
private static GroupManager instance = new GroupManager();
......@@ -222,6 +221,25 @@ public class GroupManager {
}
}
/**
* 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;
}
}
/**
* Returns an iterator for all groups according to a filter.
* <p/>
......
......@@ -14,6 +14,7 @@ package org.jivesoftware.wildfire.group;
import org.xmpp.packet.JID;
import java.util.Collection;
import java.util.Set;
/**
* Provider interface for groups. Users that wish to integrate with
......@@ -103,6 +104,13 @@ public interface GroupProvider {
*/
Collection<Group> getGroups();
/**
* Returns the Collection of groups for the specified groups names.
*
* @return the Collection with the requested groups.
*/
Collection<Group> getGroups(Set<String> groupNames);
/**
* Returns the Collection of all groups in the system.
*
......
......@@ -168,6 +168,55 @@ public class LdapGroupProvider implements GroupProvider {
}
}
public Collection<Group> getGroups(Set<String> groupNames) {
if (groupNames.isEmpty()) {
return Collections.emptyList();
}
Collection<Group> groups = new ArrayList<Group>(groupNames.size());
String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
// Instead of loading all groups at once which may not work for super big collections
// of group names, we are going to make many queries and load by 10 groups at onces
Collection<String> searchFilters = new ArrayList<String>(groupNames.size());
List<String> names = new ArrayList<String>(groupNames);
int i = 0;
int range = 10;
do {
List<String> subset = names.subList(i, Math.min(i + range, groupNames.size()));
if (subset.size() == 1) {
String searchFilter = "(&" + filter + "(" +
manager.getGroupNameField() + "=" + subset.get(0) + "))";
searchFilters.add(searchFilter);
}
else {
StringBuilder sb = new StringBuilder(300);
sb.append("(&").append(filter).append("(|");
for (String groupName : subset) {
sb.append("(").append(manager.getGroupNameField()).append("=");
sb.append(groupName).append(")");
}
sb.append("))");
searchFilters.add(sb.toString());
}
// Increment counter to get next range
i = i + range;
}
while (groupNames.size() > i);
// Perform all required queries to load all requested groups
for (String searchFilter : searchFilters) {
try {
groups.addAll(populateGroups(searchForGroups(searchFilter, standardAttributes)));
}
catch (NamingException e) {
Log.error("Error populating groups from LDAP", e);
return Collections.emptyList();
}
}
return new ArrayList<Group>(groups);
}
public Collection<Group> getGroups(int start, int num) {
// Get an enumeration of all groups in the system
String searchFilter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
......
......@@ -259,7 +259,7 @@ public class Roster implements Cacheable {
throws UserAlreadyExistsException, SharedGroupException {
if (groups != null && !groups.isEmpty()) {
// Raise an error if the groups the item belongs to include a shared group
Collection<Group> sharedGroups = GroupManager.getInstance().getGroups();
Collection<Group> sharedGroups = GroupManager.getInstance().getSharedGroups();
for (String group : groups) {
for (Group sharedGroup : sharedGroups) {
if (group.equals(sharedGroup.getProperties().get("sharedRoster.displayName"))) {
......
......@@ -322,7 +322,7 @@ public class RosterItem implements Cacheable {
}
// Remove shared groups from the param
Collection<Group> existingGroups = GroupManager.getInstance().getGroups();
Collection<Group> existingGroups = GroupManager.getInstance().getSharedGroups();
for (Iterator<String> it=groups.iterator(); it.hasNext();) {
String groupName = it.next();
try {
......
......@@ -46,7 +46,7 @@ import java.util.*;
*/
public class RosterManager extends BasicModule implements GroupEventListener {
private Cache rosterCache = null;
private Cache<String, Roster> rosterCache = null;
private XMPPServer server;
private RoutingTable routingTable;
......@@ -81,10 +81,10 @@ public class RosterManager extends BasicModule implements GroupEventListener {
if (rosterCache == null) {
throw new UserNotFoundException("Could not load caches");
}
Roster roster = (Roster)rosterCache.get(username);
Roster roster = rosterCache.get(username);
if (roster == null) {
synchronized(username.intern()) {
roster = (Roster)rosterCache.get(username);
roster = rosterCache.get(username);
if (roster == null) {
// Not in cache so load a new one:
roster = new Roster(username);
......@@ -165,7 +165,7 @@ public class RosterManager extends BasicModule implements GroupEventListener {
*/
public Collection<Group> getSharedGroups(User user) {
Collection<Group> answer = new HashSet<Group>();
Collection<Group> groups = GroupManager.getInstance().getGroups();
Collection<Group> groups = GroupManager.getInstance().getSharedGroups();
for (Group group : groups) {
String showInRoster = group.getProperties().get("sharedRoster.showInRoster");
if ("onlyGroup".equals(showInRoster)) {
......@@ -559,7 +559,7 @@ public class RosterManager extends BasicModule implements GroupEventListener {
private Collection<Group> getVisibleGroups(Group groupToCheck) {
Collection<Group> answer = new HashSet<Group>();
Collection<Group> groups = GroupManager.getInstance().getGroups();
Collection<Group> groups = GroupManager.getInstance().getSharedGroups();
for (Group group : groups) {
if (group.equals(groupToCheck)) {
continue;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment