简体   繁体   English

此代码中的LDAP连接泄漏

[英]LDAP connection leak in this code

Could someone explain me why this piece of code randomly "leaks" LDAP connections? 有人可以解释一下为什么这段代码随机“泄漏” LDAP连接吗? I can see the amount of established TCP/IP connections rising by time and at some stage this starts causing problems. 我可以看到建立的TCP / IP连接的数量随着时间的推移而增加,并且在某个阶段这开始引起问题。 I tried fiddling with the com.sun.jndi.ldap.connect environment properties (enabling pooling, disabling it and such) but it did not seem to help. 我尝试摆弄com.sun.jndi.ldap.connect环境属性(启用缓冲池,禁用缓冲池等),但似乎没有帮助。

That means my lame piece of code has got bug(s). 这意味着我la脚的代码出现了错误。 How should this done generally better and make sure I never "leak" LDAP connections? 通常应该怎样做才能更好,并确保我永不“泄漏” LDAP连接?

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

public class LdapUtil {
    private String ldap_context = "ou=myunit,dc=com";
    protected static String ldap_server = "ldap://ldapserver:389/";
    private String ldap_prefix = "(&(uid=";
    private String ldap_postfix = ")(objectclass=inetOrgPerson))";
    private String[] ldap_attributes = {"uid","departmentNumber","cn","postOfficeBox","mail"};
    private Properties ldap_properties;
    private SearchControls ldap_searchcontrols;
        private static String ldap_principal = "uid=bind_account,cn=users,ou=myunit,dc=com";
        private static String ldap_credentials = "qwerty";

    private List<String> getUserAttributes(final String userId) {
        List<String> UserAttributes = new ArrayList<String>();
        InitialDirContext ctx = null;
        NamingEnumeration<SearchResult> resultsEnum = null;
        NamingEnumeration<String> atrEnum = null;

        // Connect the LDAP
        try {
        ctx = new InitialDirContext(this.ldap_properties);
        // Prepare the query string
        String query = this.ldap_prefix+ userId+ this.ldap_postfix;
        // Query!
        resultsEnum = ctx.search(this.ldap_context, query, this.ldap_searchcontrols);

        // Enumerate the results
        while (resultsEnum.hasMore()) {
            SearchResult sr = (SearchResult) resultsEnum.nextElement();

            // Get all the attributes for a hit
            Attributes atr = sr.getAttributes();
            // Enumerate the attributes
            atrEnum = atr.getIDs();

            while (atrEnum.hasMore()) {
                    String nextid = atrEnum.nextElement();
                    String nextattribute = atr.get(nextid).toString();
                    UserAttributes.add(nextattribute);
            }

        }
        } catch ( Exception eom ) {
            System.out.println("LDAP exception");
        } finally {
            try {
                if (atrEnum!=null)
                atrEnum.close();
                if (resultsEnum!=null)
                resultsEnum.close();
                if (ctx!=null)
                ctx.close();
            } catch (NamingException eo) {
                // nothing
            } catch (NullPointerException eo) {
                // Nothing
            }
        } 
        return UserAttributes;
    }


    /*
     * Parses the LDAP search results and searches for selected attribute.
     */
    private String getAttribute (final List<String> attributes,final String attribuutti) {
        String result = null;
        // Let's go through all attributes
        for (int i = 0; i < attributes.size(); i++) {
            String attribute = attributes.get(i).toString();
            // Look for match
            if (attribute.startsWith(attribuutti)) {
                // Return the value after the space
                int k = attribute.indexOf(" ");
                result = attribute.substring(k+1,attribute.length());
            }
        }
        return result;
    }


    public LdapUtil(String remoteuser) {

        // Pre-initialize LDAP connection related properties
        this.ldap_properties = new Properties();
        this.ldap_properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        this.ldap_properties.put(Context.PROVIDER_URL, ldap_server) ;
        this.ldap_properties.put(Context.REFERRAL, "follow" );
        this.ldap_properties.put(Context.SECURITY_AUTHENTICATION, "simple");
        this.ldap_properties.put(Context.SECURITY_PRINCIPAL,ldap_principal);
        this.ldap_properties.put(Context.SECURITY_CREDENTIALS,ldap_credentials);
        this.ldap_properties.put("com.sun.jndi.ldap.read.timeout", "10000");
        this.ldap_properties.put("com.sun.jndi.ldap.connect.timeout", "10000");
        // Tried with both pooling and without - doesn't solve problems
        this.ldap_properties.put("com.sun.jndi.ldap.connect.pool", "false");

        // Pre-initialize LDAP search controls
        this.ldap_searchcontrols = new SearchControls();
        this.ldap_searchcontrols.setSearchScope(SearchControls.SUBTREE_SCOPE);
        this.ldap_searchcontrols.setReturningAttributes(this.ldap_attributes);

            // The List for attributes
        List<String> attributes = null;

        attributes = getUserAttributes(remoteuser);
    }

}
} finally {
    try {
        if (atrEnum!=null)
        atrEnum.close();
        if (resultsEnum!=null)
        resultsEnum.close();
        if (ctx!=null)
        ctx.close();
    } catch (NamingException eo) {
        // nothing
    } catch (NullPointerException eo) {
        // Nothing
    }
}

When atrEnum.close() fails with an exception, the resultsEnum and ctx will never be closed. atrEnum.close()失败,一个例外, resultsEnumctx 永远不会被关闭。

To fix this, just put all close() calls each in its own try/catch block. 要解决此问题,只需将所有close()调用分别放在其自己的 try / catch块中。

} finally {
    if (atrEnum != null) try { 
        atrEnum.close(); 
    } catch (NamingException logOrIgnore) {}
    if (resultsEnum != null) try {
        resultsEnum.close();
    } catch (NamingException logOrIgnore) {}
    if (ctx != null) try {
        ctx.close();
    } catch (NamingException logOrIgnore) {}
}

One obvious issue is that your finally block is wrong. 一个明显的问题是您的finally块是错误的。 Ff close throws an exception the following close methods will not be called. Ff close引发异常,因此不会调用以下close方法。 Always write such code as: 始终编​​写如下代码:

final Res res = acquire();
try {
    use(res);
} finally {
    res.release();
}

Even if that means nesting try-finally. 即使这意味着最终尝试嵌套。

(If you are using an old version of the Sun JRE there was an issue with LDAP (and Kerberos) resource leakage in an exception case. "SunSolve" page ) (如果使用的是Sun JRE的旧版本,则在例外情况下LDAP(和Kerberos)资源泄漏会出现问题。 “ SunSolve”页面

Thanks for the answers. 感谢您的回答。 I wrote this: 我这样写:

private void closeResources(final InitialDirContext ctx,final NamingEnumeration<SearchResult> resultsEnum,final NamingEnumeration<String> atrEnum) {
    try {
        atrEnum.close();
    } catch (Exception eom) {}

    try {
        resultsEnum.close();
    } catch (Exception eom) {}

    try {
        ctx.close();
    } catch (Exception eom) {}
}

and decided to call it from the finally block from previous. 并决定从上一个的finally块中调用它。 Slightly less messy to follow what is happening. 跟踪正在发生的事情的混乱程度略微降低。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM