Commit f497ddf7 authored by Guus der Kinderen's avatar Guus der Kinderen Committed by daryl herzmann

OF-1306: Add a cache for UserDN search results. (#769)

Openfire repeatedly searches for the same UserDN values. This cache should alleviate the backend storage and improve performance.
parent 8c554c16
...@@ -16,19 +16,16 @@ ...@@ -16,19 +16,16 @@
package org.jivesoftware.openfire.ldap; package org.jivesoftware.openfire.ldap;
import java.net.URLEncoder; import org.jivesoftware.openfire.group.GroupNotFoundException;
import java.nio.charset.StandardCharsets; import org.jivesoftware.openfire.user.UserNotFoundException;
import java.text.MessageFormat; import org.jivesoftware.util.JiveGlobals;
import java.util.ArrayList; import org.jivesoftware.util.JiveInitialLdapContext;
import java.util.Collection; import org.jivesoftware.util.cache.Cache;
import java.util.Collections; import org.jivesoftware.util.cache.CacheFactory;
import java.util.Hashtable; import org.slf4j.Logger;
import java.util.List; import org.slf4j.LoggerFactory;
import java.util.Map; import org.xmpp.packet.JID;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.naming.Context; import javax.naming.Context;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException; import javax.naming.NamingException;
...@@ -36,22 +33,14 @@ import javax.naming.directory.DirContext; ...@@ -36,22 +33,14 @@ import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext; import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls; import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult; import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control; import javax.naming.ldap.*;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.PagedResultsControl;
import javax.naming.ldap.PagedResultsResponseControl;
import javax.naming.ldap.SortControl;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
import java.net.URLEncoder;
import org.jivesoftware.openfire.group.GroupNotFoundException; import java.nio.charset.StandardCharsets;
import org.jivesoftware.openfire.user.UserNotFoundException; import java.text.MessageFormat;
import org.jivesoftware.util.JiveGlobals; import java.util.*;
import org.jivesoftware.util.JiveInitialLdapContext; import java.util.regex.Matcher;
import org.slf4j.Logger; import java.util.regex.Pattern;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
/** /**
* Centralized administration of LDAP connections. The {@link #getInstance()} method * Centralized administration of LDAP connections. The {@link #getInstance()} method
...@@ -201,6 +190,8 @@ public class LdapManager { ...@@ -201,6 +190,8 @@ public class LdapManager {
private final Map<String, String> properties; private final Map<String, String> properties;
private Cache<String, DNCacheEntry> userDNCache = null;
/** /**
* Provides singleton access to an instance of the LdapManager class. * Provides singleton access to an instance of the LdapManager class.
* *
...@@ -255,6 +246,11 @@ public class LdapManager { ...@@ -255,6 +246,11 @@ public class LdapManager {
JiveGlobals.migrateProperty("ldap.ldapDebugEnabled"); JiveGlobals.migrateProperty("ldap.ldapDebugEnabled");
JiveGlobals.migrateProperty("ldap.encodeMultibyteCharacters"); JiveGlobals.migrateProperty("ldap.encodeMultibyteCharacters");
if (JiveGlobals.getBooleanProperty("ldap.userDNCache.enabled", true)) {
String cacheName = "LDAP UserDN";
userDNCache = CacheFactory.createCache( cacheName );
}
String host = properties.get("ldap.host"); String host = properties.get("ldap.host");
if (host != null) { if (host != null) {
// Parse the property and check if many hosts were defined. Hosts can be separated // Parse the property and check if many hosts were defined. Hosts can be separated
...@@ -920,15 +916,41 @@ public class LdapManager { ...@@ -920,15 +916,41 @@ public class LdapManager {
* @return the dn associated with <tt>username</tt>. * @return the dn associated with <tt>username</tt>.
* @throws Exception if the search for the dn fails. * @throws Exception if the search for the dn fails.
*/ */
public String findUserDN(String username) throws Exception { public String findUserDN( String username ) throws Exception
try { {
return findUserDN(username, baseDN); if ( userDNCache != null )
{
// Return a cache entry if one exists.
final DNCacheEntry dnCacheEntry = userDNCache.get( username );
if ( dnCacheEntry != null )
{
return dnCacheEntry.getUserDN();
}
} }
catch (Exception e) {
if (alternateBaseDN != null) { // No cache entry. Query for the value, and add that to the cache.
return findUserDN(username, alternateBaseDN); try
{
final String userDN = findUserDN( username, baseDN );
if ( userDNCache != null )
{
userDNCache.put( username, new DNCacheEntry( userDN, baseDN ) );
} }
else { return userDN;
}
catch ( Exception e )
{
if ( alternateBaseDN != null )
{
final String userDN = findUserDN( username, alternateBaseDN );
if ( userDNCache != null )
{
userDNCache.put( username, new DNCacheEntry( userDN, alternateBaseDN ) );
}
return userDN;
}
else
{
throw e; throw e;
} }
} }
...@@ -1502,22 +1524,48 @@ public class LdapManager { ...@@ -1502,22 +1524,48 @@ public class LdapManager {
* @return the BaseDN for the given username. If no baseDN is found, * @return the BaseDN for the given username. If no baseDN is found,
* this method will return <tt>null</tt>. * this method will return <tt>null</tt>.
*/ */
public String getUsersBaseDN(String username) { public String getUsersBaseDN( String username )
try { {
findUserDN(username, baseDN); if ( userDNCache != null )
{
// Return a cache entry if one exists.
final DNCacheEntry dnCacheEntry = userDNCache.get( username );
if ( dnCacheEntry != null )
{
return dnCacheEntry.getBaseDN();
}
}
// No cache entry. Query for the value, and add that to the cache.
try
{
final String userDN = findUserDN( username, baseDN );
if ( userDNCache != null )
{
userDNCache.put( username, new DNCacheEntry( userDN, baseDN ) );
}
return baseDN; return baseDN;
} }
catch (Exception e) { catch ( Exception e )
try { {
if (alternateBaseDN != null) { try
findUserDN(username, alternateBaseDN); {
if ( alternateBaseDN != null )
{
final String userDN = findUserDN( username, alternateBaseDN );
if ( userDNCache != null )
{
userDNCache.put( username, new DNCacheEntry( userDN, alternateBaseDN ) );
}
return alternateBaseDN; return alternateBaseDN;
} }
} }
catch (Exception ex) { catch ( Exception ex )
Log.debug(ex.getMessage(), ex); {
Log.debug( ex.getMessage(), ex );
} }
} }
return null; return null;
} }
...@@ -2315,4 +2363,54 @@ public class LdapManager { ...@@ -2315,4 +2363,54 @@ public class LdapManager {
// Set the pattern to use to wrap DN values with " // Set the pattern to use to wrap DN values with "
private static Pattern dnPattern; private static Pattern dnPattern;
private static class DNCacheEntry
{
private final String userDN;
private final String baseDN;
public DNCacheEntry( String userDN, String baseDN )
{
this.userDN = userDN;
this.baseDN = baseDN;
}
public String getUserDN()
{
return userDN;
}
public String getBaseDN()
{
return baseDN;
}
@Override
public boolean equals( Object o )
{
if ( this == o )
{
return true;
}
if ( o == null || getClass() != o.getClass() )
{
return false;
}
DNCacheEntry that = (DNCacheEntry) o;
if ( userDN != null ? !userDN.equals( that.userDN ) : that.userDN != null )
{
return false;
}
return baseDN != null ? baseDN.equals( that.baseDN ) : that.baseDN == null;
}
@Override
public int hashCode()
{
int result = userDN != null ? userDN.hashCode() : 0;
result = 31 * result + ( baseDN != null ? baseDN.hashCode() : 0 );
return result;
}
}
} }
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