有人可以解释一下为什么这段代码会随机“泄漏”LDAP连接吗?我可以看到已建立的TCP / IP连接数量随着时间的推移而增加,并且在某个阶段这会引发问题。我试着摆弄com.sun.jndi.ldap.connect环境属性(启用池,禁用它等)但它似乎没有帮助。
这意味着我的蹩脚代码已经有bug了。这应该如何做得更好,并确保我从不“泄漏”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);
}
}
答案 0 :(得分:4)
} 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
}
}
当atrEnum.close()
因异常而失败时,resultsEnum
和ctx
永远不会关闭。
要解决此问题,只需将所有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) {}
}
答案 1 :(得分:3)
一个明显的问题是您的finally
阻止了错误。 Ff close
抛出异常,将不会调用以下close
方法。始终写下如下代码:
final Res res = acquire();
try {
use(res);
} finally {
res.release();
}
即使这意味着嵌套try-finally。
(如果您使用旧版本的Sun JRE,则在例外情况下LDAP(和Kerberos)资源泄漏存在问题。"SunSolve" page)
答案 2 :(得分:0)
感谢您的回答。我写了这个:
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) {}
}
并决定从之前的finally块中调用它。跟踪正在发生的事情稍微不那么混乱。