Add config options of LDAP 'connection pooling'

With these config options user can enable LDAP 'connection pooling'
for Helper.open() to improve the performance of LDAP server access.

Change-Id: If1c7bb5a9f5824aaa0bd71e8419b91a8f588493d
This commit is contained in:
Bruce Zu
2014-07-25 15:48:09 +08:00
parent d1feb48787
commit cd04bbc125
3 changed files with 153 additions and 8 deletions

View File

@@ -2369,6 +2369,91 @@ On Windows servers the registry key `HKEY_LOCAL_MACHINE\System\CurrentControlSet
must have the DWORD value `allowtgtsessionkey` set to 1 and the account must not
have local administrator privileges.
[[ldap.useConnectionPooling]]ldap.useConnectionPooling::
+
_(Optional)_ Enable the LDAP connection pooling or not.
+
If it is true, the LDAP service provider maintains a pool of (possibly)
previously used connections and assigns them to a Context instance as
needed. When a Context instance is done with a connection (closed or
garbage collected), the connection is returned to the pool for future use.
+
For details, see link:http://docs.oracle.com/javase/tutorial/jndi/ldap/pool.html[
LDAP connection management (Pool)] and link:http://docs.oracle.com/javase/tutorial/jndi/ldap/config.html[
LDAP connection management (Configuration)]
+
By default, false.
[[ldap.connectTimeout]]ldap.connectTimeout::
+
_(Optional)_ Specify how long to wait for a pooled connection.
This is also used to specify a timeout period for establishment
of the LDAP connection.
+
The value is in the usual time-unit format like "1 s", "100 ms",
etc...
+
By default there is no timeout and Gerrit will wait indefinitely.
[[ldap.poolAuthentication]]ldap.poolAuthentication::
+
_(Optional)_ A list of space-separated authentication types of
connections that may be pooled. Valid types are "none", "simple",
and "DIGEST-MD5".
+
Default is "none simple".
[[ldap.poolDebug]]ldap.poolDebug::
+
_(Optional)_ A string that indicates the level of debug output
to produce. Valid values are "fine" (trace connection creation
and removal) and "all" (all debugging information).
[[ldap.poolInitsize]]ldap.poolInitsize::
+
_(Optional)_ The string representation of an integer that
represents the number of connections per connection identity
to create when initially creating a connection for the identity.
+
Default is 1.
[[ldap.poolMaxsize]]ldap.poolMaxsize::
+
_(Optional)_ The string representation of an integer that
represents the maximum number of connections per connection
identity that can be maintained concurrently.
+
Default is 0, means that there is no maximum size: A request for
a pooled connection will use an existing pooled idle connection
or a newly created pooled connection.
[[ldap.poolPrefsize]]ldap.poolPrefsize::
+
_(Optional)_ The string representation of an integer that
represents the preferred number of connections per connection
identity that should be maintained concurrently.
+
Default is 0, means that there is no preferred size: A request
for a pooled connection will result in a newly created connection
only if no idle ones are available.
[[ldap.poolProtocol]]ldap.poolProtocol::
+
_(Optional)_ A list of space-separated protocol types of
connections that may be pooled. Valid types are "plain" and "ssl".
+
Default is "plain".
[[ldap.poolTimeout]]ldap.poolTimeout::
+
_(Optional)_ Specify how long an idle connection may remain
in the pool without being closed and removed from the pool.
+
The value is in the usual time-unit format like "1 s", "100 ms",
etc...
+
By default there is no timeout.
[[mimetype]]
=== Section mimetype

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.auth.ldap;
import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.gerrit.common.data.ParameterizedString;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountException;
@@ -37,6 +38,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -58,6 +60,42 @@ import javax.security.auth.login.LoginException;
@Singleton class Helper {
static final String LDAP_UUID = "ldap:";
static private Map<String, String> getPoolProperties(Config config) {
if (LdapRealm.optional(config, "useConnectionPooling", false)) {
Map<String, String> r = Maps.newHashMap();
r.put("com.sun.jndi.ldap.connect.pool", "true");
String connectTimeout = LdapRealm.optional(config, "connectTimeout");
String poolDebug = LdapRealm.optional(config, "poolDebug");
String poolTimeout = LdapRealm.optional(config, "poolTimeout");
if (connectTimeout != null) {
r.put("com.sun.jndi.ldap.connect.timeout", Long.toString(ConfigUtil
.getTimeUnit(connectTimeout, 0, TimeUnit.MILLISECONDS)));
}
r.put("com.sun.jndi.ldap.connect.pool.authentication",
LdapRealm.optional(config, "poolAuthentication", "none simple"));
if (poolDebug != null) {
r.put("com.sun.jndi.ldap.connect.pool.debug", poolDebug);
}
r.put("com.sun.jndi.ldap.connect.pool.initsize",
String.valueOf(LdapRealm.optional(config, "poolInitsize", 1)));
r.put("com.sun.jndi.ldap.connect.pool.maxsize",
String.valueOf(LdapRealm.optional(config, "poolMaxsize", 0)));
r.put("com.sun.jndi.ldap.connect.pool.prefsize",
String.valueOf(LdapRealm.optional(config, "poolPrefsize", 0)));
r.put("com.sun.jndi.ldap.connect.pool.protocol",
LdapRealm.optional(config, "poolProtocol", "plain"));
if (poolTimeout != null) {
r.put("com.sun.jndi.ldap.connect.pool.timeout", Long
.toString(ConfigUtil.getTimeUnit(poolTimeout, 0,
TimeUnit.MILLISECONDS)));
}
return r;
}
return null;
}
private final Cache<String, ImmutableSet<String>> groupsByInclude;
private final Config config;
private final String server;
@@ -68,6 +106,7 @@ import javax.security.auth.login.LoginException;
private final String authentication;
private volatile LdapSchema ldapSchema;
private final String readTimeOutMillis;
private final Map<String, String> connectionPoolConfig;
@Inject
Helper(@GerritServerConfig final Config config,
@@ -76,10 +115,11 @@ import javax.security.auth.login.LoginException;
this.config = config;
this.server = LdapRealm.optional(config, "server");
this.username = LdapRealm.optional(config, "username");
this.password = LdapRealm.optional(config, "password");
this.referral = LdapRealm.optional(config, "referral");
this.password = LdapRealm.optional(config, "password", "");
this.referral = LdapRealm.optional(config, "referral", "ignore");
this.sslVerify = config.getBoolean("ldap", "sslverify", true);
this.authentication = LdapRealm.optional(config, "authentication");
this.authentication =
LdapRealm.optional(config, "authentication", "simple");
String timeout = LdapRealm.optional(config, "readTimeout");
if (timeout != null) {
readTimeOutMillis =
@@ -89,6 +129,7 @@ import javax.security.auth.login.LoginException;
readTimeOutMillis = null;
}
this.groupsByInclude = groupsByInclude;
this.connectionPoolConfig = getPoolProperties(config);
}
private Properties createContextProperties() {
@@ -107,14 +148,17 @@ import javax.security.auth.login.LoginException;
DirContext open() throws NamingException, LoginException {
final Properties env = createContextProperties();
env.put(Context.SECURITY_AUTHENTICATION, authentication != null ? authentication : "simple");
env.put(Context.REFERRAL, referral != null ? referral : "ignore");
if (connectionPoolConfig != null) {
env.putAll(connectionPoolConfig);
}
env.put(Context.SECURITY_AUTHENTICATION, authentication);
env.put(Context.REFERRAL, referral);
if ("GSSAPI".equals(authentication)) {
return kerberosOpen(env);
} else {
if (username != null) {
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password != null ? password : "");
env.put(Context.SECURITY_CREDENTIALS, password);
}
return new InitialDirContext(env);
}
@@ -146,8 +190,8 @@ import javax.security.auth.login.LoginException;
final Properties env = createContextProperties();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, dn);
env.put(Context.SECURITY_CREDENTIALS, password != null ? password : "");
env.put(Context.REFERRAL, referral != null ? referral : "ignore");
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.REFERRAL, referral);
try {
return new InitialDirContext(env);
} catch (NamingException e) {

View File

@@ -106,6 +106,22 @@ public class LdapRealm implements Realm {
return config.getString("ldap", null, name);
}
static int optional(Config config, String name, int defaultValue) {
return config.getInt("ldap", name, defaultValue);
}
static String optional(Config config, String name, String defaultValue) {
final String v = optional(config, name);
if (Strings.isNullOrEmpty(v)) {
return defaultValue;
}
return v;
}
static boolean optional(Config config, String name, boolean defaultValue) {
return config.getBoolean("ldap", name, defaultValue);
}
static String required(final Config config, final String name) {
final String v = optional(config, name);
if (v == null || "".equals(v)) {