Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/jndi/ldap/LdapPoolManager.java
38924 views
/*1* Copyright (c) 2002, 2013, 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 sun.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*/5657public final class LdapPoolManager {58private static final String DEBUG =59"com.sun.jndi.ldap.connect.pool.debug";6061public static final boolean debug =62"all".equalsIgnoreCase(getProperty(DEBUG, null));6364public static final boolean trace = debug ||65"fine".equalsIgnoreCase(getProperty(DEBUG, null));6667// ---------- System properties for connection pooling6869// Authentication mechanisms of connections that may be pooled70private static final String POOL_AUTH =71"com.sun.jndi.ldap.connect.pool.authentication";7273// Protocol types of connections that may be pooled74private static final String POOL_PROTOCOL =75"com.sun.jndi.ldap.connect.pool.protocol";7677// Maximum number of identical connections per pool78private static final String MAX_POOL_SIZE =79"com.sun.jndi.ldap.connect.pool.maxsize";8081// Preferred number of identical connections per pool82private static final String PREF_POOL_SIZE =83"com.sun.jndi.ldap.connect.pool.prefsize";8485// Initial number of identical connections per pool86private static final String INIT_POOL_SIZE =87"com.sun.jndi.ldap.connect.pool.initsize";8889// Milliseconds to wait before closing idle connections90private static final String POOL_TIMEOUT =91"com.sun.jndi.ldap.connect.pool.timeout";9293// Properties for DIGEST94private static final String SASL_CALLBACK =95"java.naming.security.sasl.callback";9697// --------- Constants98private static final int DEFAULT_MAX_POOL_SIZE = 0;99private static final int DEFAULT_PREF_POOL_SIZE = 0;100private static final int DEFAULT_INIT_POOL_SIZE = 1;101private static final int DEFAULT_TIMEOUT = 0; // no timeout102private static final String DEFAULT_AUTH_MECHS = "none simple";103private static final String DEFAULT_PROTOCOLS = "plain";104105private static final int NONE = 0; // indices into pools106private static final int SIMPLE = 1;107private static final int DIGEST = 2;108109// --------- static fields110private static final long idleTimeout;// ms to wait before closing idle conn111private static final int maxSize; // max num of identical conns/pool112private static final int prefSize; // preferred num of identical conns/pool113private static final int initSize; // initial num of identical conns/pool114115private static boolean supportPlainProtocol = false;116private static boolean supportSslProtocol = false;117118// List of pools used for different auth types119private static final Pool[] pools = new Pool[3];120121static {122maxSize = getInteger(MAX_POOL_SIZE, DEFAULT_MAX_POOL_SIZE);123124prefSize = getInteger(PREF_POOL_SIZE, DEFAULT_PREF_POOL_SIZE);125126initSize = getInteger(INIT_POOL_SIZE, DEFAULT_INIT_POOL_SIZE);127128idleTimeout = getLong(POOL_TIMEOUT, DEFAULT_TIMEOUT);129130// Determine supported authentication mechanisms131String str = getProperty(POOL_AUTH, DEFAULT_AUTH_MECHS);132StringTokenizer parser = new StringTokenizer(str);133int count = parser.countTokens();134String mech;135int p;136for (int i = 0; i < count; i++) {137mech = parser.nextToken().toLowerCase(Locale.ENGLISH);138if (mech.equals("anonymous")) {139mech = "none";140}141142p = findPool(mech);143if (p >= 0 && pools[p] == null) {144pools[p] = new Pool(initSize, prefSize, maxSize);145}146}147148// Determine supported protocols149str= getProperty(POOL_PROTOCOL, DEFAULT_PROTOCOLS);150parser = new StringTokenizer(str);151count = parser.countTokens();152String proto;153for (int i = 0; i < count; i++) {154proto = parser.nextToken();155if ("plain".equalsIgnoreCase(proto)) {156supportPlainProtocol = true;157} else if ("ssl".equalsIgnoreCase(proto)) {158supportSslProtocol = true;159} else {160// ignore161}162}163164if (idleTimeout > 0) {165// Create cleaner to expire idle connections166PrivilegedAction<Void> pa = new PrivilegedAction<Void>() {167public Void run() {168Thread t = InnocuousThread.newSystemThread(169"LDAP PoolCleaner",170new PoolCleaner(idleTimeout, pools));171assert t.getContextClassLoader() == null;172t.setDaemon(true);173t.start();174return null;175}};176AccessController.doPrivileged(pa);177}178179if (debug) {180showStats(System.err);181}182}183184// Cannot instantiate one of these185private LdapPoolManager() {186}187188/**189* Find the index of the pool for the specified mechanism. If not190* one of "none", "simple", "DIGEST-MD5", or "GSSAPI",191* return -1.192* @param mech mechanism type193*/194private static int findPool(String mech) {195if ("none".equalsIgnoreCase(mech)) {196return NONE;197} else if ("simple".equalsIgnoreCase(mech)) {198return SIMPLE;199} else if ("digest-md5".equalsIgnoreCase(mech)) {200return DIGEST;201}202return -1;203}204205/**206* Determines whether pooling is allowed given information on how207* the connection will be used.208*209* Non-configurable rejections:210* - nonstandard socketFactory has been specified: the pool manager211* cannot track input or parameters used by the socket factory and212* thus has no way of determining whether two connection requests213* are equivalent. Maybe in the future it might add a list of allowed214* socket factories to be configured215* - trace enabled (except when debugging)216* - for Digest authentication, if a callback handler has been specified:217* the pool manager cannot track input collected by the handler218* and thus has no way of determining whether two connection requests are219* equivalent. Maybe in the future it might add a list of allowed220* callback handlers.221*222* Configurable tests:223* - Pooling for the requested protocol (plain or ssl) is supported224* - Pooling for the requested authentication mechanism is supported225*226*/227static boolean isPoolingAllowed(String socketFactory, OutputStream trace,228String authMech, String protocol, Hashtable<?,?> env)229throws NamingException {230231if (trace != null && !debug232233// Requesting plain protocol but it is not supported234|| (protocol == null && !supportPlainProtocol)235236// Requesting ssl protocol but it is not supported237|| ("ssl".equalsIgnoreCase(protocol) && !supportSslProtocol)) {238239d("Pooling disallowed due to tracing or unsupported pooling of protocol");240return false;241}242// pooling of custom socket factory is possible only if the243// socket factory interface implements java.util.comparator244String COMPARATOR = "java.util.Comparator";245boolean foundSockCmp = false;246if ((socketFactory != null) &&247!socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) {248try {249Class<?> socketFactoryClass = Obj.helper.loadClass(socketFactory);250Class<?>[] interfaces = socketFactoryClass.getInterfaces();251for (int i = 0; i < interfaces.length; i++) {252if (interfaces[i].getCanonicalName().equals(COMPARATOR)) {253foundSockCmp = true;254}255}256} catch (Exception e) {257CommunicationException ce =258new CommunicationException("Loading the socket factory");259ce.setRootCause(e);260throw ce;261}262if (!foundSockCmp) {263return false;264}265}266// Cannot use pooling if authMech is not a supported mechs267// Cannot use pooling if authMech contains multiple mechs268int p = findPool(authMech);269if (p < 0 || pools[p] == null) {270d("authmech not found: ", authMech);271272return false;273}274275d("using authmech: ", authMech);276277switch (p) {278case NONE:279case SIMPLE:280return true;281282case DIGEST:283// Provider won't be able to determine connection identity284// if an alternate callback handler is used285return (env == null || env.get(SASL_CALLBACK) == null);286}287return false;288}289290/**291* Obtains a pooled connection that either already exists or is292* newly created using the parameters supplied. If it is newly293* created, it needs to go through the authentication checks to294* determine whether an LDAP bind is necessary.295*296* Caller needs to invoke ldapClient.authenticateCalled() to297* determine whether ldapClient.authenticate() needs to be invoked.298* Caller has that responsibility because caller needs to deal299* with the LDAP bind response, which might involve referrals,300* response controls, errors, etc. This method is responsible only301* for establishing the connection.302*303* @return an LdapClient that is pooled.304*/305static LdapClient getLdapClient(String host, int port, String socketFactory,306int connTimeout, int readTimeout, OutputStream trace, int version,307String authMech, Control[] ctls, String protocol, String user,308Object passwd, Hashtable<?,?> env) throws NamingException {309310// Create base identity for LdapClient311ClientId id = null;312Pool pool;313314int p = findPool(authMech);315if (p < 0 || (pool=pools[p]) == null) {316throw new IllegalArgumentException(317"Attempting to use pooling for an unsupported mechanism: " +318authMech);319}320switch (p) {321case NONE:322id = new ClientId(version, host, port, protocol,323ctls, trace, socketFactory);324break;325326case SIMPLE:327// Add identity information used in simple authentication328id = new SimpleClientId(version, host, port, protocol,329ctls, trace, socketFactory, user, passwd);330break;331332case DIGEST:333// Add user/passwd/realm/authzid/qop/strength/maxbuf/mutual/policy*334id = new DigestClientId(version, host, port, protocol,335ctls, trace, socketFactory, user, passwd, env);336break;337}338339return (LdapClient) pool.getPooledConnection(id, connTimeout,340new LdapClientFactory(host, port, socketFactory, connTimeout,341readTimeout, trace));342}343344public static void showStats(PrintStream out) {345out.println("***** start *****");346out.println("idle timeout: " + idleTimeout);347out.println("maximum pool size: " + maxSize);348out.println("preferred pool size: " + prefSize);349out.println("initial pool size: " + initSize);350out.println("protocol types: " + (supportPlainProtocol ? "plain " : "") +351(supportSslProtocol ? "ssl" : ""));352out.println("authentication types: " +353(pools[NONE] != null ? "none " : "") +354(pools[SIMPLE] != null ? "simple " : "") +355(pools[DIGEST] != null ? "DIGEST-MD5 " : ""));356357for (int i = 0; i < pools.length; i++) {358if (pools[i] != null) {359out.println(360(i == NONE ? "anonymous pools" :361i == SIMPLE ? "simple auth pools" :362i == DIGEST ? "digest pools" : "")363+ ":");364pools[i].showStats(out);365}366}367out.println("***** end *****");368}369370/**371* Closes idle connections idle since specified time.372*373* @param threshold Close connections idle since this time, as374* specified in milliseconds since "the epoch".375* @see java.util.Date376*/377public static void expire(long threshold) {378for (int i = 0; i < pools.length; i++) {379if (pools[i] != null) {380pools[i].expire(threshold);381}382}383}384385private static void d(String msg) {386if (debug) {387System.err.println("LdapPoolManager: " + msg);388}389}390391private static void d(String msg, String o) {392if (debug) {393System.err.println("LdapPoolManager: " + msg + o);394}395}396397private static final String getProperty(final String propName,398final String defVal) {399return AccessController.doPrivileged(400new PrivilegedAction<String>() {401public String run() {402try {403return System.getProperty(propName, defVal);404} catch (SecurityException e) {405return defVal;406}407}408});409}410411private static final int getInteger(final String propName,412final int defVal) {413Integer val = AccessController.doPrivileged(414new PrivilegedAction<Integer>() {415public Integer run() {416try {417return Integer.getInteger(propName, defVal);418} catch (SecurityException e) {419return new Integer(defVal);420}421}422});423return val.intValue();424}425426private static final long getLong(final String propName,427final long defVal) {428Long val = AccessController.doPrivileged(429new PrivilegedAction<Long>() {430public Long run() {431try {432return Long.getLong(propName, defVal);433} catch (SecurityException e) {434return new Long(defVal);435}436}437});438return val.longValue();439}440}441442443