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; ...@@ -23,6 +23,7 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* Database implementation of the GroupManager interface. * Database implementation of the GroupManager interface.
...@@ -260,6 +261,10 @@ public class DefaultGroupProvider implements GroupProvider { ...@@ -260,6 +261,10 @@ public class DefaultGroupProvider implements GroupProvider {
return new GroupCollection(groupNames.toArray(new String[groupNames.size()])); 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) { public Collection<Group> getGroups(int startIndex, int numResults) {
List<String> groupNames = new ArrayList<String>(); List<String> groupNames = new ArrayList<String>();
Connection con = null; Connection con = null;
......
...@@ -12,20 +12,20 @@ ...@@ -12,20 +12,20 @@
package org.jivesoftware.wildfire.group; package org.jivesoftware.wildfire.group;
import org.jivesoftware.database.DbConnectionManager; 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.CacheSizes;
import org.jivesoftware.util.Cacheable; import org.jivesoftware.util.Cacheable;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.event.GroupEventDispatcher;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.io.*;
/** /**
* Groups organize users into a single entity for easier management.<p> * Groups organize users into a single entity for easier management.<p>
...@@ -48,6 +48,9 @@ public class Group implements Cacheable { ...@@ -48,6 +48,9 @@ public class Group implements Cacheable {
"UPDATE jiveGroupProp SET propValue=? WHERE name=? AND groupName=?"; "UPDATE jiveGroupProp SET propValue=? WHERE name=? AND groupName=?";
private static final String INSERT_PROPERTY = private static final String INSERT_PROPERTY =
"INSERT INTO jiveGroupProp (groupName, name, propValue) VALUES (?, ?, ?)"; "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 GroupProvider provider;
private transient GroupManager groupManager; private transient GroupManager groupManager;
...@@ -58,6 +61,36 @@ public class Group implements Cacheable { ...@@ -58,6 +61,36 @@ public class Group implements Cacheable {
private Set<JID> members; private Set<JID> members;
private Set<JID> administrators; 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 * Constructs a new group. Note: this constructor is intended for implementors of the
* {@link GroupProvider} interface. To create a new group, use the * {@link GroupProvider} interface. To create a new group, use the
...@@ -384,10 +417,6 @@ public class Group implements Cacheable { ...@@ -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 true;
} }
return false; return false;
......
...@@ -18,10 +18,7 @@ import org.jivesoftware.wildfire.event.GroupEventListener; ...@@ -18,10 +18,7 @@ import org.jivesoftware.wildfire.event.GroupEventListener;
import org.jivesoftware.wildfire.user.User; import org.jivesoftware.wildfire.user.User;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/** /**
* Manages groups. * Manages groups.
...@@ -33,8 +30,10 @@ public class GroupManager { ...@@ -33,8 +30,10 @@ public class GroupManager {
Cache<String, Group> groupCache; Cache<String, Group> groupCache;
Cache<String, Collection<Group>> userGroupCache; 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"; 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 GroupProvider provider;
private static GroupManager instance = new GroupManager(); private static GroupManager instance = new GroupManager();
...@@ -222,6 +221,25 @@ public class 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. * Returns an iterator for all groups according to a filter.
* <p/> * <p/>
......
...@@ -14,6 +14,7 @@ package org.jivesoftware.wildfire.group; ...@@ -14,6 +14,7 @@ package org.jivesoftware.wildfire.group;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import java.util.Collection; import java.util.Collection;
import java.util.Set;
/** /**
* Provider interface for groups. Users that wish to integrate with * Provider interface for groups. Users that wish to integrate with
...@@ -103,6 +104,13 @@ public interface GroupProvider { ...@@ -103,6 +104,13 @@ public interface GroupProvider {
*/ */
Collection<Group> getGroups(); 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. * Returns the Collection of all groups in the system.
* *
......
...@@ -168,6 +168,55 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -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) { public Collection<Group> getGroups(int start, int num) {
// Get an enumeration of all groups in the system // Get an enumeration of all groups in the system
String searchFilter = MessageFormat.format(manager.getGroupSearchFilter(), "*"); String searchFilter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
......
...@@ -259,7 +259,7 @@ public class Roster implements Cacheable { ...@@ -259,7 +259,7 @@ public class Roster implements Cacheable {
throws UserAlreadyExistsException, SharedGroupException { throws UserAlreadyExistsException, SharedGroupException {
if (groups != null && !groups.isEmpty()) { if (groups != null && !groups.isEmpty()) {
// Raise an error if the groups the item belongs to include a shared group // 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 (String group : groups) {
for (Group sharedGroup : sharedGroups) { for (Group sharedGroup : sharedGroups) {
if (group.equals(sharedGroup.getProperties().get("sharedRoster.displayName"))) { if (group.equals(sharedGroup.getProperties().get("sharedRoster.displayName"))) {
......
...@@ -322,7 +322,7 @@ public class RosterItem implements Cacheable { ...@@ -322,7 +322,7 @@ public class RosterItem implements Cacheable {
} }
// Remove shared groups from the param // 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();) { for (Iterator<String> it=groups.iterator(); it.hasNext();) {
String groupName = it.next(); String groupName = it.next();
try { try {
......
...@@ -46,7 +46,7 @@ import java.util.*; ...@@ -46,7 +46,7 @@ import java.util.*;
*/ */
public class RosterManager extends BasicModule implements GroupEventListener { public class RosterManager extends BasicModule implements GroupEventListener {
private Cache rosterCache = null; private Cache<String, Roster> rosterCache = null;
private XMPPServer server; private XMPPServer server;
private RoutingTable routingTable; private RoutingTable routingTable;
...@@ -81,10 +81,10 @@ public class RosterManager extends BasicModule implements GroupEventListener { ...@@ -81,10 +81,10 @@ public class RosterManager extends BasicModule implements GroupEventListener {
if (rosterCache == null) { if (rosterCache == null) {
throw new UserNotFoundException("Could not load caches"); throw new UserNotFoundException("Could not load caches");
} }
Roster roster = (Roster)rosterCache.get(username); Roster roster = rosterCache.get(username);
if (roster == null) { if (roster == null) {
synchronized(username.intern()) { synchronized(username.intern()) {
roster = (Roster)rosterCache.get(username); roster = rosterCache.get(username);
if (roster == null) { if (roster == null) {
// Not in cache so load a new one: // Not in cache so load a new one:
roster = new Roster(username); roster = new Roster(username);
...@@ -165,7 +165,7 @@ public class RosterManager extends BasicModule implements GroupEventListener { ...@@ -165,7 +165,7 @@ public class RosterManager extends BasicModule implements GroupEventListener {
*/ */
public Collection<Group> getSharedGroups(User user) { public Collection<Group> getSharedGroups(User user) {
Collection<Group> answer = new HashSet<Group>(); Collection<Group> answer = new HashSet<Group>();
Collection<Group> groups = GroupManager.getInstance().getGroups(); Collection<Group> groups = GroupManager.getInstance().getSharedGroups();
for (Group group : groups) { for (Group group : groups) {
String showInRoster = group.getProperties().get("sharedRoster.showInRoster"); String showInRoster = group.getProperties().get("sharedRoster.showInRoster");
if ("onlyGroup".equals(showInRoster)) { if ("onlyGroup".equals(showInRoster)) {
...@@ -559,7 +559,7 @@ public class RosterManager extends BasicModule implements GroupEventListener { ...@@ -559,7 +559,7 @@ public class RosterManager extends BasicModule implements GroupEventListener {
private Collection<Group> getVisibleGroups(Group groupToCheck) { private Collection<Group> getVisibleGroups(Group groupToCheck) {
Collection<Group> answer = new HashSet<Group>(); Collection<Group> answer = new HashSet<Group>();
Collection<Group> groups = GroupManager.getInstance().getGroups(); Collection<Group> groups = GroupManager.getInstance().getSharedGroups();
for (Group group : groups) { for (Group group : groups) {
if (group.equals(groupToCheck)) { if (group.equals(groupToCheck)) {
continue; 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