| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.jndi.ldap;  | 
 | 
 | 
 | 
import java.io.PrintStream;  | 
 | 
import java.io.OutputStream;  | 
 | 
import java.util.Hashtable;  | 
 | 
import java.util.Locale;  | 
 | 
import java.util.StringTokenizer;  | 
 | 
 | 
 | 
import javax.naming.ldap.Control;  | 
 | 
import javax.naming.NamingException;  | 
 | 
import javax.naming.CommunicationException;  | 
 | 
import java.security.AccessController;  | 
 | 
import java.security.PrivilegedAction;  | 
 | 
 | 
 | 
import com.sun.jndi.ldap.pool.PoolCleaner;  | 
 | 
import com.sun.jndi.ldap.pool.Pool;  | 
 | 
import sun.misc.InnocuousThread;  | 
 | 
 | 
 | 
/**  | 
 | 
 * Contains utilities for managing connection pools of LdapClient.  | 
 | 
 * Contains method for  | 
 | 
 * - checking whether attempted connection creation may be pooled  | 
 | 
 * - creating a pooled connection  | 
 | 
 * - closing idle connections.  | 
 | 
 *  | 
 | 
 * If a timeout period has been configured, then it will automatically  | 
 | 
 * close and remove idle connections (those that have not been  | 
 | 
 * used for the duration of the timeout period).  | 
 | 
 *  | 
 | 
 * @author Rosanna Lee  | 
 | 
 */  | 
 | 
 | 
 | 
public final class LdapPoolManager { | 
 | 
    private static final String DEBUG =  | 
 | 
        "com.sun.jndi.ldap.connect.pool.debug";  | 
 | 
 | 
 | 
    public static final boolean debug =  | 
 | 
        "all".equalsIgnoreCase(getProperty(DEBUG, null));  | 
 | 
 | 
 | 
    public static final boolean trace = debug ||  | 
 | 
        "fine".equalsIgnoreCase(getProperty(DEBUG, null));  | 
 | 
 | 
 | 
    // ---------- System properties for connection pooling  | 
 | 
 | 
 | 
      | 
 | 
    private static final String POOL_AUTH =  | 
 | 
        "com.sun.jndi.ldap.connect.pool.authentication";  | 
 | 
 | 
 | 
      | 
 | 
    private static final String POOL_PROTOCOL =  | 
 | 
        "com.sun.jndi.ldap.connect.pool.protocol";  | 
 | 
 | 
 | 
      | 
 | 
    private static final String MAX_POOL_SIZE =  | 
 | 
        "com.sun.jndi.ldap.connect.pool.maxsize";  | 
 | 
 | 
 | 
      | 
 | 
    private static final String PREF_POOL_SIZE =  | 
 | 
        "com.sun.jndi.ldap.connect.pool.prefsize";  | 
 | 
 | 
 | 
      | 
 | 
    private static final String INIT_POOL_SIZE =  | 
 | 
        "com.sun.jndi.ldap.connect.pool.initsize";  | 
 | 
 | 
 | 
      | 
 | 
    private static final String POOL_TIMEOUT =  | 
 | 
        "com.sun.jndi.ldap.connect.pool.timeout";  | 
 | 
 | 
 | 
      | 
 | 
    private static final String SASL_CALLBACK =  | 
 | 
        "java.naming.security.sasl.callback";  | 
 | 
 | 
 | 
      | 
 | 
    private static final int DEFAULT_MAX_POOL_SIZE = 0;  | 
 | 
    private static final int DEFAULT_PREF_POOL_SIZE = 0;  | 
 | 
    private static final int DEFAULT_INIT_POOL_SIZE = 1;  | 
 | 
    private static final int DEFAULT_TIMEOUT = 0;      | 
 | 
    private static final String DEFAULT_AUTH_MECHS = "none simple";  | 
 | 
    private static final String DEFAULT_PROTOCOLS = "plain";  | 
 | 
 | 
 | 
    private static final int NONE = 0;      | 
 | 
    private static final int SIMPLE = 1;  | 
 | 
    private static final int DIGEST = 2;  | 
 | 
 | 
 | 
    // --------- static fields  | 
 | 
    private static final long idleTimeout;  | 
 | 
    private static final int maxSize;       | 
 | 
    private static final int prefSize;      | 
 | 
    private static final int initSize;      | 
 | 
 | 
 | 
    private static boolean supportPlainProtocol = false;  | 
 | 
    private static boolean supportSslProtocol = false;  | 
 | 
 | 
 | 
      | 
 | 
    private static final Pool[] pools = new Pool[3];  | 
 | 
 | 
 | 
    static { | 
 | 
        maxSize = getInteger(MAX_POOL_SIZE, DEFAULT_MAX_POOL_SIZE);  | 
 | 
 | 
 | 
        prefSize = getInteger(PREF_POOL_SIZE, DEFAULT_PREF_POOL_SIZE);  | 
 | 
 | 
 | 
        initSize = getInteger(INIT_POOL_SIZE, DEFAULT_INIT_POOL_SIZE);  | 
 | 
 | 
 | 
        idleTimeout = getLong(POOL_TIMEOUT, DEFAULT_TIMEOUT);  | 
 | 
 | 
 | 
          | 
 | 
        String str = getProperty(POOL_AUTH, DEFAULT_AUTH_MECHS);  | 
 | 
        StringTokenizer parser = new StringTokenizer(str);  | 
 | 
        int count = parser.countTokens();  | 
 | 
        String mech;  | 
 | 
        int p;  | 
 | 
        for (int i = 0; i < count; i++) { | 
 | 
            mech = parser.nextToken().toLowerCase(Locale.ENGLISH);  | 
 | 
            if (mech.equals("anonymous")) { | 
 | 
                mech = "none";  | 
 | 
            }  | 
 | 
 | 
 | 
            p = findPool(mech);  | 
 | 
            if (p >= 0 && pools[p] == null) { | 
 | 
                pools[p] = new Pool(initSize, prefSize, maxSize);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        str= getProperty(POOL_PROTOCOL, DEFAULT_PROTOCOLS);  | 
 | 
        parser = new StringTokenizer(str);  | 
 | 
        count = parser.countTokens();  | 
 | 
        String proto;  | 
 | 
        for (int i = 0; i < count; i++) { | 
 | 
            proto = parser.nextToken();  | 
 | 
            if ("plain".equalsIgnoreCase(proto)) { | 
 | 
                supportPlainProtocol = true;  | 
 | 
            } else if ("ssl".equalsIgnoreCase(proto)) { | 
 | 
                supportSslProtocol = true;  | 
 | 
            } else { | 
 | 
                // ignore  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (idleTimeout > 0) { | 
 | 
              | 
 | 
            PrivilegedAction<Void> pa = new PrivilegedAction<Void>() { | 
 | 
                public Void run() { | 
 | 
                    Thread t = InnocuousThread.newSystemThread(  | 
 | 
                            "LDAP PoolCleaner",  | 
 | 
                            new PoolCleaner(idleTimeout, pools));  | 
 | 
                    assert t.getContextClassLoader() == null;  | 
 | 
                    t.setDaemon(true);  | 
 | 
                    t.start();  | 
 | 
                    return null;  | 
 | 
                }};  | 
 | 
            AccessController.doPrivileged(pa);  | 
 | 
        }  | 
 | 
 | 
 | 
        if (debug) { | 
 | 
            showStats(System.err);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private LdapPoolManager() { | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static int findPool(String mech) { | 
 | 
        if ("none".equalsIgnoreCase(mech)) { | 
 | 
            return NONE;  | 
 | 
        } else if ("simple".equalsIgnoreCase(mech)) { | 
 | 
            return SIMPLE;  | 
 | 
        } else if ("digest-md5".equalsIgnoreCase(mech)) { | 
 | 
            return DIGEST;  | 
 | 
        }  | 
 | 
        return -1;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    static boolean isPoolingAllowed(String socketFactory, OutputStream trace,  | 
 | 
        String authMech, String protocol, Hashtable<?,?> env)  | 
 | 
                throws NamingException { | 
 | 
 | 
 | 
        if (trace != null && !debug  | 
 | 
 | 
 | 
                  | 
 | 
                || (protocol == null && !supportPlainProtocol)  | 
 | 
 | 
 | 
                  | 
 | 
                || ("ssl".equalsIgnoreCase(protocol) && !supportSslProtocol)) { | 
 | 
 | 
 | 
            d("Pooling disallowed due to tracing or unsupported pooling of protocol"); | 
 | 
            return false;  | 
 | 
        }  | 
 | 
        // pooling of custom socket factory is possible only if the  | 
 | 
          | 
 | 
        String COMPARATOR = "java.util.Comparator";  | 
 | 
        boolean foundSockCmp = false;  | 
 | 
        if ((socketFactory != null) &&  | 
 | 
             !socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) { | 
 | 
            try { | 
 | 
                Class<?> socketFactoryClass = Obj.helper.loadClass(socketFactory);  | 
 | 
                Class<?>[] interfaces = socketFactoryClass.getInterfaces();  | 
 | 
                for (int i = 0; i < interfaces.length; i++) { | 
 | 
                    if (interfaces[i].getCanonicalName().equals(COMPARATOR)) { | 
 | 
                        foundSockCmp = true;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } catch (Exception e) { | 
 | 
                CommunicationException ce =  | 
 | 
                    new CommunicationException("Loading the socket factory"); | 
 | 
                ce.setRootCause(e);  | 
 | 
                throw ce;  | 
 | 
            }  | 
 | 
            if (!foundSockCmp) { | 
 | 
                return false;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        // Cannot use pooling if authMech is not a supported mechs  | 
 | 
          | 
 | 
        int p = findPool(authMech);  | 
 | 
        if (p < 0 || pools[p] == null) { | 
 | 
            d("authmech not found: ", authMech); | 
 | 
 | 
 | 
            return false;  | 
 | 
        }  | 
 | 
 | 
 | 
        d("using authmech: ", authMech); | 
 | 
 | 
 | 
        switch (p) { | 
 | 
        case NONE:  | 
 | 
        case SIMPLE:  | 
 | 
            return true;  | 
 | 
 | 
 | 
        case DIGEST:  | 
 | 
            // Provider won't be able to determine connection identity  | 
 | 
              | 
 | 
            return (env == null || env.get(SASL_CALLBACK) == null);  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    static LdapClient getLdapClient(String host, int port, String socketFactory,  | 
 | 
        int connTimeout, int readTimeout, OutputStream trace, int version,  | 
 | 
        String authMech, Control[] ctls, String protocol, String user,  | 
 | 
        Object passwd, Hashtable<?,?> env) throws NamingException { | 
 | 
 | 
 | 
          | 
 | 
        ClientId id = null;  | 
 | 
        Pool pool;  | 
 | 
 | 
 | 
        int p = findPool(authMech);  | 
 | 
        if (p < 0 || (pool=pools[p]) == null) { | 
 | 
            throw new IllegalArgumentException(  | 
 | 
                "Attempting to use pooling for an unsupported mechanism: " +  | 
 | 
                authMech);  | 
 | 
        }  | 
 | 
        switch (p) { | 
 | 
        case NONE:  | 
 | 
            id = new ClientId(version, host, port, protocol,  | 
 | 
                        ctls, trace, socketFactory);  | 
 | 
            break;  | 
 | 
 | 
 | 
        case SIMPLE:  | 
 | 
              | 
 | 
            id = new SimpleClientId(version, host, port, protocol,  | 
 | 
                ctls, trace, socketFactory, user, passwd);  | 
 | 
            break;  | 
 | 
 | 
 | 
        case DIGEST:  | 
 | 
              | 
 | 
            id = new DigestClientId(version, host, port, protocol,  | 
 | 
                ctls, trace, socketFactory, user, passwd, env);  | 
 | 
            break;  | 
 | 
        }  | 
 | 
 | 
 | 
        return (LdapClient) pool.getPooledConnection(id, connTimeout,  | 
 | 
            new LdapClientFactory(host, port, socketFactory, connTimeout,  | 
 | 
                                readTimeout, trace));  | 
 | 
    }  | 
 | 
 | 
 | 
    public static void showStats(PrintStream out) { | 
 | 
        out.println("***** start *****"); | 
 | 
        out.println("idle timeout: " + idleTimeout); | 
 | 
        out.println("maximum pool size: " + maxSize); | 
 | 
        out.println("preferred pool size: " + prefSize); | 
 | 
        out.println("initial pool size: " + initSize); | 
 | 
        out.println("protocol types: " + (supportPlainProtocol ? "plain " : "") + | 
 | 
            (supportSslProtocol ? "ssl" : ""));  | 
 | 
        out.println("authentication types: " + | 
 | 
            (pools[NONE] != null ? "none " : "") +  | 
 | 
            (pools[SIMPLE] != null ? "simple " : "") +  | 
 | 
            (pools[DIGEST] != null ? "DIGEST-MD5 " : ""));  | 
 | 
 | 
 | 
        for (int i = 0; i < pools.length; i++) { | 
 | 
            if (pools[i] != null) { | 
 | 
                out.println(  | 
 | 
                    (i == NONE ? "anonymous pools" :  | 
 | 
                        i == SIMPLE ? "simple auth pools" :  | 
 | 
                        i == DIGEST ? "digest pools" : "")  | 
 | 
                            + ":");  | 
 | 
                pools[i].showStats(out);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        out.println("***** end *****"); | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static void expire(long threshold) { | 
 | 
        for (int i = 0; i < pools.length; i++) { | 
 | 
            if (pools[i] != null) { | 
 | 
                pools[i].expire(threshold);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static void d(String msg) { | 
 | 
        if (debug) { | 
 | 
            System.err.println("LdapPoolManager: " + msg); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static void d(String msg, String o) { | 
 | 
        if (debug) { | 
 | 
            System.err.println("LdapPoolManager: " + msg + o); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final String getProperty(final String propName,  | 
 | 
        final String defVal) { | 
 | 
        return AccessController.doPrivileged(  | 
 | 
            new PrivilegedAction<String>() { | 
 | 
            public String run() { | 
 | 
                try { | 
 | 
                    return System.getProperty(propName, defVal);  | 
 | 
                } catch (SecurityException e) { | 
 | 
                    return defVal;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        });  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final int getInteger(final String propName,  | 
 | 
        final int defVal) { | 
 | 
        Integer val = AccessController.doPrivileged(  | 
 | 
            new PrivilegedAction<Integer>() { | 
 | 
            public Integer run() { | 
 | 
                try { | 
 | 
                    return Integer.getInteger(propName, defVal);  | 
 | 
                } catch (SecurityException e) { | 
 | 
                    return new Integer(defVal);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        });  | 
 | 
        return val.intValue();  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final long getLong(final String propName,  | 
 | 
        final long defVal) { | 
 | 
        Long val = AccessController.doPrivileged(  | 
 | 
            new PrivilegedAction<Long>() { | 
 | 
            public Long run() { | 
 | 
                try { | 
 | 
                    return Long.getLong(propName, defVal);  | 
 | 
                } catch (SecurityException e) { | 
 | 
                    return new Long(defVal);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        });  | 
 | 
        return val.longValue();  | 
 | 
    }  | 
 | 
}  |