Path: blob/master/src/java.naming/share/classes/com/sun/jndi/ldap/LdapPoolManager.java
41161 views
/*1* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.ldap;2627import java.io.PrintStream;28import java.io.OutputStream;29import java.util.Hashtable;30import java.util.Locale;31import java.util.StringTokenizer;3233import javax.naming.ldap.Control;34import javax.naming.NamingException;35import javax.naming.CommunicationException;36import java.security.AccessController;37import java.security.PrivilegedAction;3839import com.sun.jndi.ldap.pool.PoolCleaner;40import com.sun.jndi.ldap.pool.Pool;41import jdk.internal.misc.InnocuousThread;4243/**44* Contains utilities for managing connection pools of LdapClient.45* Contains method for46* - checking whether attempted connection creation may be pooled47* - creating a pooled connection48* - closing idle connections.49*50* If a timeout period has been configured, then it will automatically51* close and remove idle connections (those that have not been52* used for the duration of the timeout period).53*54* @author Rosanna Lee55*/5657@SuppressWarnings("removal")58public final class LdapPoolManager {59private static final String DEBUG =60"com.sun.jndi.ldap.connect.pool.debug";6162public static final boolean debug =63"all".equalsIgnoreCase(getProperty(DEBUG, null));6465public static final boolean trace = debug ||66"fine".equalsIgnoreCase(getProperty(DEBUG, null));6768// ---------- System properties for connection pooling6970// Authentication mechanisms of connections that may be pooled71private static final String POOL_AUTH =72"com.sun.jndi.ldap.connect.pool.authentication";7374// Protocol types of connections that may be pooled75private static final String POOL_PROTOCOL =76"com.sun.jndi.ldap.connect.pool.protocol";7778// Maximum number of identical connections per pool79private static final String MAX_POOL_SIZE =80"com.sun.jndi.ldap.connect.pool.maxsize";8182// Preferred number of identical connections per pool83private static final String PREF_POOL_SIZE =84"com.sun.jndi.ldap.connect.pool.prefsize";8586// Initial number of identical connections per pool87private static final String INIT_POOL_SIZE =88"com.sun.jndi.ldap.connect.pool.initsize";8990// Milliseconds to wait before closing idle connections91private static final String POOL_TIMEOUT =92"com.sun.jndi.ldap.connect.pool.timeout";9394// Properties for DIGEST95private static final String SASL_CALLBACK =96"java.naming.security.sasl.callback";9798// --------- Constants99private static final int DEFAULT_MAX_POOL_SIZE = 0;100private static final int DEFAULT_PREF_POOL_SIZE = 0;101private static final int DEFAULT_INIT_POOL_SIZE = 1;102private static final int DEFAULT_TIMEOUT = 0; // no timeout103private static final String DEFAULT_AUTH_MECHS = "none simple";104private static final String DEFAULT_PROTOCOLS = "plain";105106private static final int NONE = 0; // indices into pools107private static final int SIMPLE = 1;108private static final int DIGEST = 2;109110// --------- static fields111private static final long idleTimeout;// ms to wait before closing idle conn112private static final int maxSize; // max num of identical conns/pool113private static final int prefSize; // preferred num of identical conns/pool114private static final int initSize; // initial num of identical conns/pool115116private static boolean supportPlainProtocol = false;117private static boolean supportSslProtocol = false;118119// List of pools used for different auth types120private static final Pool[] pools = new Pool[3];121122static {123maxSize = getInteger(MAX_POOL_SIZE, DEFAULT_MAX_POOL_SIZE);124125prefSize = getInteger(PREF_POOL_SIZE, DEFAULT_PREF_POOL_SIZE);126127initSize = getInteger(INIT_POOL_SIZE, DEFAULT_INIT_POOL_SIZE);128129idleTimeout = getLong(POOL_TIMEOUT, DEFAULT_TIMEOUT);130131// Determine supported authentication mechanisms132String str = getProperty(POOL_AUTH, DEFAULT_AUTH_MECHS);133StringTokenizer parser = new StringTokenizer(str);134int count = parser.countTokens();135String mech;136int p;137for (int i = 0; i < count; i++) {138mech = parser.nextToken().toLowerCase(Locale.ENGLISH);139if (mech.equals("anonymous")) {140mech = "none";141}142143p = findPool(mech);144if (p >= 0 && pools[p] == null) {145pools[p] = new Pool(initSize, prefSize, maxSize);146}147}148149// Determine supported protocols150str= getProperty(POOL_PROTOCOL, DEFAULT_PROTOCOLS);151parser = new StringTokenizer(str);152count = parser.countTokens();153String proto;154for (int i = 0; i < count; i++) {155proto = parser.nextToken();156if ("plain".equalsIgnoreCase(proto)) {157supportPlainProtocol = true;158} else if ("ssl".equalsIgnoreCase(proto)) {159supportSslProtocol = true;160} else {161// ignore162}163}164165if (idleTimeout > 0) {166// Create cleaner to expire idle connections167PrivilegedAction<Void> pa = new PrivilegedAction<Void>() {168public Void run() {169Thread t = InnocuousThread.newSystemThread(170"LDAP PoolCleaner",171new PoolCleaner(idleTimeout, pools));172assert t.getContextClassLoader() == null;173t.setDaemon(true);174t.start();175return null;176}};177AccessController.doPrivileged(pa);178}179180if (debug) {181showStats(System.err);182}183}184185// Cannot instantiate one of these186private LdapPoolManager() {187}188189/**190* Find the index of the pool for the specified mechanism. If not191* one of "none", "simple", "DIGEST-MD5", or "GSSAPI",192* return -1.193* @param mech mechanism type194*/195private static int findPool(String mech) {196if ("none".equalsIgnoreCase(mech)) {197return NONE;198} else if ("simple".equalsIgnoreCase(mech)) {199return SIMPLE;200} else if ("digest-md5".equalsIgnoreCase(mech)) {201return DIGEST;202}203return -1;204}205206/**207* Determines whether pooling is allowed given information on how208* the connection will be used.209*210* Non-configurable rejections:211* - nonstandard socketFactory has been specified: the pool manager212* cannot track input or parameters used by the socket factory and213* thus has no way of determining whether two connection requests214* are equivalent. Maybe in the future it might add a list of allowed215* socket factories to be configured216* - trace enabled (except when debugging)217* - for Digest authentication, if a callback handler has been specified:218* the pool manager cannot track input collected by the handler219* and thus has no way of determining whether two connection requests are220* equivalent. Maybe in the future it might add a list of allowed221* callback handlers.222*223* Configurable tests:224* - Pooling for the requested protocol (plain or ssl) is supported225* - Pooling for the requested authentication mechanism is supported226*227*/228static boolean isPoolingAllowed(String socketFactory, OutputStream trace,229String authMech, String protocol, Hashtable<?,?> env)230throws NamingException {231232if (trace != null && !debug233234// Requesting plain protocol but it is not supported235|| (protocol == null && !supportPlainProtocol)236237// Requesting ssl protocol but it is not supported238|| ("ssl".equalsIgnoreCase(protocol) && !supportSslProtocol)) {239240d("Pooling disallowed due to tracing or unsupported pooling of protocol");241return false;242}243// pooling of custom socket factory is possible only if the244// socket factory interface implements java.util.comparator245String COMPARATOR = "java.util.Comparator";246boolean foundSockCmp = false;247if ((socketFactory != null) &&248!socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) {249try {250Class<?> socketFactoryClass = Obj.helper.loadClass(socketFactory);251Class<?>[] interfaces = socketFactoryClass.getInterfaces();252for (int i = 0; i < interfaces.length; i++) {253if (interfaces[i].getCanonicalName().equals(COMPARATOR)) {254foundSockCmp = true;255}256}257} catch (Exception e) {258CommunicationException ce =259new CommunicationException("Loading the socket factory");260ce.setRootCause(e);261throw ce;262}263if (!foundSockCmp) {264return false;265}266}267// Cannot use pooling if authMech is not a supported mechs268// Cannot use pooling if authMech contains multiple mechs269int p = findPool(authMech);270if (p < 0 || pools[p] == null) {271d("authmech not found: ", authMech);272273return false;274}275276d("using authmech: ", authMech);277278switch (p) {279case NONE:280case SIMPLE:281return true;282283case DIGEST:284// Provider won't be able to determine connection identity285// if an alternate callback handler is used286return (env == null || env.get(SASL_CALLBACK) == null);287}288return false;289}290291/**292* Obtains a pooled connection that either already exists or is293* newly created using the parameters supplied. If it is newly294* created, it needs to go through the authentication checks to295* determine whether an LDAP bind is necessary.296*297* Caller needs to invoke ldapClient.authenticateCalled() to298* determine whether ldapClient.authenticate() needs to be invoked.299* Caller has that responsibility because caller needs to deal300* with the LDAP bind response, which might involve referrals,301* response controls, errors, etc. This method is responsible only302* for establishing the connection.303*304* @return an LdapClient that is pooled.305*/306static LdapClient getLdapClient(String host, int port, String socketFactory,307int connTimeout, int readTimeout, OutputStream trace, int version,308String authMech, Control[] ctls, String protocol, String user,309Object passwd, Hashtable<?,?> env) throws NamingException {310311// Create base identity for LdapClient312ClientId id = null;313Pool pool;314315int p = findPool(authMech);316if (p < 0 || (pool=pools[p]) == null) {317throw new IllegalArgumentException(318"Attempting to use pooling for an unsupported mechanism: " +319authMech);320}321switch (p) {322case NONE:323id = new ClientId(version, host, port, protocol,324ctls, trace, socketFactory);325break;326327case SIMPLE:328// Add identity information used in simple authentication329id = new SimpleClientId(version, host, port, protocol,330ctls, trace, socketFactory, user, passwd);331break;332333case DIGEST:334// Add user/passwd/realm/authzid/qop/strength/maxbuf/mutual/policy*335id = new DigestClientId(version, host, port, protocol,336ctls, trace, socketFactory, user, passwd, env);337break;338}339340return (LdapClient) pool.getPooledConnection(id, connTimeout,341new LdapClientFactory(host, port, socketFactory, connTimeout,342readTimeout, trace));343}344345public static void showStats(PrintStream out) {346out.println("***** start *****");347out.println("idle timeout: " + idleTimeout);348out.println("maximum pool size: " + maxSize);349out.println("preferred pool size: " + prefSize);350out.println("initial pool size: " + initSize);351out.println("protocol types: " + (supportPlainProtocol ? "plain " : "") +352(supportSslProtocol ? "ssl" : ""));353out.println("authentication types: " +354(pools[NONE] != null ? "none " : "") +355(pools[SIMPLE] != null ? "simple " : "") +356(pools[DIGEST] != null ? "DIGEST-MD5 " : ""));357358for (int i = 0; i < pools.length; i++) {359if (pools[i] != null) {360out.println(361(i == NONE ? "anonymous pools" :362i == SIMPLE ? "simple auth pools" :363i == DIGEST ? "digest pools" : "")364+ ":");365pools[i].showStats(out);366}367}368out.println("***** end *****");369}370371/**372* Closes idle connections idle since specified time.373*374* @param threshold Close connections idle since this time, as375* specified in milliseconds since "the epoch".376* @see java.util.Date377*/378public static void expire(long threshold) {379for (int i = 0; i < pools.length; i++) {380if (pools[i] != null) {381pools[i].expire(threshold);382}383}384}385386private static void d(String msg) {387if (debug) {388System.err.println("LdapPoolManager: " + msg);389}390}391392private static void d(String msg, String o) {393if (debug) {394System.err.println("LdapPoolManager: " + msg + o);395}396}397398private static final String getProperty(final String propName, final String defVal) {399PrivilegedAction<String> pa = () -> System.getProperty(propName, defVal);400return AccessController.doPrivileged(pa);401}402403private static final int getInteger(final String propName, final int defVal) {404PrivilegedAction<Integer> pa = () -> Integer.getInteger(propName, defVal);405return AccessController.doPrivileged(pa);406}407408private static final long getLong(final String propName, final long defVal) {409PrivilegedAction<Long> pa = () -> Long.getLong(propName, defVal);410return AccessController.doPrivileged(pa);411}412}413414415