From cd04bbc125c290891dc90ba0da6515f45ea88347 Mon Sep 17 00:00:00 2001 From: Bruce Zu Date: Fri, 25 Jul 2014 15:48:09 +0800 Subject: [PATCH] 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 --- Documentation/config-gerrit.txt | 85 +++++++++++++++++++ .../gerrit/server/auth/ldap/Helper.java | 60 +++++++++++-- .../gerrit/server/auth/ldap/LdapRealm.java | 16 ++++ 3 files changed, 153 insertions(+), 8 deletions(-) diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index c95ff73369..02bed57a59 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -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 diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java index 5a19814a86..cc61695faa 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java @@ -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 getPoolProperties(Config config) { + if (LdapRealm.optional(config, "useConnectionPooling", false)) { + Map 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> 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 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) { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java index 84b5277988..22c60b4ada 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java @@ -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)) {